Files
UnrealEngine/Engine/Plugins/Media/D3D12VideoDecodersElectra/Source/Private/VideoDecoder_D3D12.cpp
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

1084 lines
39 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "VideoDecoder_D3D12.h"
#include "VideoDecoder_D3D12_Common.h"
#include "Features/IModularFeatures.h"
#include "Features/IModularFeature.h"
#include "RHI.h"
#include "HAL/IConsoleManager.h"
#include "IElectraCodecFactory.h"
#include "IElectraCodecFactoryModule.h"
#include "IElectraCodecRegistry.h"
#include "ElectraDecodersPlatformResources.h"
#include "D3D12VideoDecodersElectraModule.h"
// Codec specific implementations
#include "VideoDecoder_D3D12_H264.h"
#include "VideoDecoder_D3D12_H265.h"
namespace ElectraVideoDecodersD3D12Video
{
#ifndef ELECTRA_DECODERS_D3D12VIDEO_DISABLED_ON_PLATFORM
#define ELECTRA_DECODERS_D3D12VIDEO_DISABLED_ON_PLATFORM 1
#endif
#ifndef ELECTRA_DECODERS_D3D12VIDEO_IGNORED_ON_PLATFORM
#define ELECTRA_DECODERS_D3D12VIDEO_IGNORED_ON_PLATFORM 1
#endif
#if ELECTRA_DECODERS_D3D12VIDEO_DISABLED_ON_PLATFORM
static bool bDisableThisDecoder = true;
#else
static bool bDisableThisDecoder = false;
#endif
#if ELECTRA_DECODERS_D3D12VIDEO_IGNORED_ON_PLATFORM
static bool bDoNotUseThisDecoder = true;
#else
static bool bDoNotUseThisDecoder = false;
#endif
FAutoConsoleVariableRef CVarElectraDecoderD3D12VideoDisable(
TEXT("ElectraDecoders.bDisableD3D12Video"),
bDisableThisDecoder,
TEXT("Globally disable the use of the D3D12 native video decoder"));
FAutoConsoleVariableRef CVarElectraDecoderD3D12DoNotUse(
TEXT("ElectraDecoders.bDoNotUseD3D12Video"),
bDoNotUseThisDecoder,
TEXT("Do not use the D3D12 native video decoder on this platform"));
int32 FCodecFormatHelper::FindSupportedFormats(ID3D12Device* InD3D12Device)
{
HRESULT Result;
CodecInfos.Empty();
DxVideoDevice = nullptr;
ID3D12Device* DxDevice = InD3D12Device;
if (!DxDevice)
{
// Not D3D 12, nothing to do.
if (RHIGetInterfaceType() != ERHIInterfaceType::D3D12)
{
return 0;
}
DxDevice = static_cast<ID3D12Device*>(GDynamicRHI->RHIGetNativeDevice());
}
if (!DxDevice)
{
return 0;
}
// Is this device a video capable device?
TRefCountPtr<ID3D12VideoDevice> VideoDevice;
if ((Result = DxDevice->QueryInterface(__uuidof(ID3D12VideoDevice), (void**)VideoDevice.GetInitReference())) != S_OK)
{
UE_LOG(LogD3D12VideoDecodersElectra, Log, TEXT("The current RHI device is not a video decoding capable device."));
return 0;
}
UINT NumNodes = DxDevice->GetNodeCount();
if (NumNodes == 0)
{
UE_LOG(LogD3D12VideoDecodersElectra, Log, TEXT("The current RHI device reports zero nodes and cannot be used."));
return 0;
}
else if (NumNodes > 1)
{
UE_LOG(LogD3D12VideoDecodersElectra, Log, TEXT("The current RHI device reports %u nodes. Using node index 0"), NumNodes);
}
UINT NodeIndex = 0;
D3D12_FEATURE_DATA_VIDEO_DECODE_PROFILE_COUNT ProfileCount {};
ProfileCount.NodeIndex = NodeIndex;
if ((Result = VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_DECODE_PROFILE_COUNT, &ProfileCount, sizeof(ProfileCount))) != S_OK)
{
UE_LOG(LogD3D12VideoDecodersElectra, Error, TEXT("CheckFeatureSupport(D3D12_FEATURE_VIDEO_DECODE_PROFILE_COUNT) failed with 0x%08x"), Result);
return 0;
}
// Get all supported profiles
TArray<GUID> ProfileGUIDs;
ProfileGUIDs.SetNum(ProfileCount.ProfileCount);
D3D12_FEATURE_DATA_VIDEO_DECODE_PROFILES Profiles {};
Profiles.NodeIndex = NodeIndex;
Profiles.ProfileCount = ProfileCount.ProfileCount;
Profiles.pProfiles = ProfileGUIDs.GetData();
if ((Result = VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_DECODE_PROFILES, &Profiles, sizeof(Profiles))) != S_OK)
{
UE_LOG(LogD3D12VideoDecodersElectra, Error, TEXT("CheckFeatureSupport(D3D12_FEATURE_VIDEO_DECODE_PROFILES) failed with 0x%08x"), Result);
return 0;
}
// Iterate the profiles and handle those we are interested in.
for(int32 nProf=0; nProf<ProfileGUIDs.Num(); ++nProf)
{
if (ProfileGUIDs[nProf] != D3D12_VIDEO_DECODE_PROFILE_H264 &&
ProfileGUIDs[nProf] != D3D12_VIDEO_DECODE_PROFILE_HEVC_MAIN &&
ProfileGUIDs[nProf] != D3D12_VIDEO_DECODE_PROFILE_HEVC_MAIN10 &&
ProfileGUIDs[nProf] != D3D12_VIDEO_DECODE_PROFILE_VP9 &&
ProfileGUIDs[nProf] != D3D12_VIDEO_DECODE_PROFILE_VP9_10BIT_PROFILE2)
{
continue;
}
FCodecInfo Info;
Info.ProfileGUID = ProfileGUIDs[nProf];
if (ProfileGUIDs[nProf] == D3D12_VIDEO_DECODE_PROFILE_H264)
{
Info.CodecType = ECodecType::H264;
}
else if (ProfileGUIDs[nProf] == D3D12_VIDEO_DECODE_PROFILE_HEVC_MAIN)
{
Info.CodecType = ECodecType::H265;
}
else if (ProfileGUIDs[nProf] == D3D12_VIDEO_DECODE_PROFILE_HEVC_MAIN10)
{
Info.CodecType = ECodecType::H265;
Info.b10Bit = true;
}
else if (ProfileGUIDs[nProf] == D3D12_VIDEO_DECODE_PROFILE_VP9)
{
Info.CodecType = ECodecType::VP9;
}
else if (ProfileGUIDs[nProf] == D3D12_VIDEO_DECODE_PROFILE_VP9_10BIT_PROFILE2)
{
Info.CodecType = ECodecType::VP9;
Info.b10Bit = true;
}
else
{
check(!"This wasn't skipped above!");
continue;
}
D3D12_VIDEO_DECODE_CONFIGURATION DecodeConfiguration {};
DecodeConfiguration.BitstreamEncryption = D3D12_BITSTREAM_ENCRYPTION_TYPE_NONE;
DecodeConfiguration.InterlaceType = D3D12_VIDEO_FRAME_CODED_INTERLACE_TYPE_NONE;
DecodeConfiguration.DecodeProfile = ProfileGUIDs[nProf];
// Get number of supported pixel formats
D3D12_FEATURE_DATA_VIDEO_DECODE_FORMAT_COUNT FormatCount {};
FormatCount.NodeIndex = NodeIndex;
FormatCount.Configuration = DecodeConfiguration;
if ((Result = VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_DECODE_FORMAT_COUNT, &FormatCount, sizeof(FormatCount))) != S_OK)
{
UE_LOG(LogD3D12VideoDecodersElectra, Error, TEXT("CheckFeatureSupport(D3D12_FEATURE_VIDEO_DECODE_FORMAT_COUNT) failed with 0x%08x"), Result);
return 0;
}
// Get supported pixel formats
D3D12_FEATURE_DATA_VIDEO_DECODE_FORMATS Formats {};
Formats.NodeIndex = NodeIndex;
Formats.Configuration = DecodeConfiguration;
Formats.FormatCount = FormatCount.FormatCount;
TArray<DXGI_FORMAT> PixFmts;
PixFmts.SetNum(FormatCount.FormatCount);
Formats.pOutputFormats = PixFmts.GetData();
if ((Result = VideoDevice->CheckFeatureSupport(D3D12_FEATURE_VIDEO_DECODE_FORMATS, &Formats, sizeof(Formats))) != S_OK)
{
UE_LOG(LogD3D12VideoDecodersElectra, Error, TEXT("CheckFeatureSupport(D3D12_FEATURE_VIDEO_DECODE_FORMATS) failed with 0x%08x"), Result);
return 0;
}
// Only use common pixel formats, nothing obscure please.
for(int32 nPixFmt=0; nPixFmt<PixFmts.Num(); ++nPixFmt)
{
if (PixFmts[nPixFmt] == DXGI_FORMAT_NV12 ||
PixFmts[nPixFmt] == DXGI_FORMAT_P010 ||
PixFmts[nPixFmt] == DXGI_FORMAT_P016)
{
Info.PixelFormats.Emplace(PixFmts[nPixFmt]);
}
}
// Is this usable?
if (Info.PixelFormats.Num())
{
// Do an AddUnique here since we have seen the exact same profile being reported more than once.
// Note: the == comparison of FCodecInfo doesn't handle different ordering of the pixel formats,
// so if those were shuffled in the profiles we could end up with more than one, but that
// is not an actual problem.
CodecInfos.AddUnique(Info);
}
}
// If we have anything supported we remember the video device for later.
if (CodecInfos.Num())
{
DxVideoDevice = VideoDevice;
DxDeviceNodeIndex = NodeIndex;
}
return CodecInfos.Num();
}
const FCodecFormatHelper::FCodecInfo* FCodecFormatHelper::HaveFormat(ECodecType InType, int32 InNumBits)
{
for(int32 i=0, iMax=CodecInfos.Num(); i<iMax; ++i)
{
if (CodecInfos[i].CodecType == InType &&
((InNumBits == 8 && CodecInfos[i].b10Bit == false) || (InNumBits == 10 && CodecInfos[i].b10Bit == true)))
{
return &CodecInfos[i];
}
}
return nullptr;
}
FDecodedPictureBuffer::~FDecodedPictureBuffer()
{
check(Frames.IsEmpty());
ReleaseAllFrames(0);
}
void FDecodedPictureBuffer::ReleaseAllFrames(int32 InWaitForEachFrameMillis)
{
// Drop all frames in the available queue. These are shared with the
// frames list so that is safe.
AvailableQueue.Empty();
while(!Frames.IsEmpty())
{
TSharedPtr<FDecodedFrame, ESPMode::ThreadSafe> Frame = Frames.Pop();
check(Frame.IsValid());
if (Frame.IsValid())
{
if (InWaitForEachFrameMillis)
{
Frame->Sync.AwaitCompletion(InWaitForEachFrameMillis);
}
Frame->Texture.SafeRelease();
}
Frame.Reset();
}
}
TSharedPtr<FDecodedFrame, ESPMode::ThreadSafe> FDecodedPictureBuffer::GetFrameAtIndex(int32 InIndex)
{
check(InIndex >= 0 && InIndex < Frames.Num());
return InIndex >= 0 && InIndex < Frames.Num() ? Frames[InIndex] : nullptr;
}
TSharedPtr<FDecodedFrame, ESPMode::ThreadSafe> FDecodedPictureBuffer::GetFrameForResource(const ID3D12Resource* InResource)
{
for(auto& it : Frames)
{
if (it->Texture.GetReference() == InResource)
{
return it;
}
}
return nullptr;
}
TSharedPtr<FDecodedFrame, ESPMode::ThreadSafe> FDecodedPictureBuffer::GetNextUnusedFrame()
{
return AvailableQueue.Num() ? AvailableQueue.Pop() : nullptr;
}
void FDecodedPictureBuffer::ReturnUnusedFrameToAvailableQueue(TSharedPtr<FDecodedFrame, ESPMode::ThreadSafe>&& InFrame)
{
if (InFrame.IsValid())
{
AvailableQueue.Push(MoveTemp(InFrame));
}
}
void FDecodedPictureBuffer::ReturnFrameToAvailableQueue(TSharedPtr<FDecodedFrame, ESPMode::ThreadSafe>&& InFrame)
{
if (InFrame.IsValid())
{
AvailableQueue.Insert(MoveTemp(InFrame), 0);
}
}
/*********************************************************************************************************************/
/*********************************************************************************************************************/
/*********************************************************************************************************************/
class FD3D12VideoDecoderFactory : public IElectraCodecFactory, public IElectraCodecModularFeature, public TSharedFromThis<FD3D12VideoDecoderFactory, ESPMode::ThreadSafe>
{
private:
TUniquePtr<FCodecFormatHelper> CurrentFormats;
FCriticalSection AccessLock;
public:
FD3D12VideoDecoderFactory(TUniquePtr<FCodecFormatHelper> InCurrentFormats)
: CurrentFormats(MoveTemp(InCurrentFormats))
{ }
virtual ~FD3D12VideoDecoderFactory()
{}
void GetListOfFactories(TArray<TWeakPtr<IElectraCodecFactory, ESPMode::ThreadSafe>>& OutCodecFactories) override
{
OutCodecFactories.Add(AsShared());
}
const FCodecFormatHelper::FCodecInfo* GetFormatIfSupported(D3D12_FEATURE_DATA_VIDEO_DECODE_SUPPORT& OutSupport, const FString& InCodecFormat, const TMap<FString, FVariant>& InOptions) const
{
int32 Width=0, Height=0;
int64 bps=0, fps_n=0;
uint32 fps_d=0;
// Get properties that cannot be passed with the codec string alone.
int32 MaxWidth = (int32)ElectraDecodersUtil::GetVariantValueSafeI64(InOptions, TEXT("max_width"), 0);
if (MaxWidth > 0)
{
Width = MaxWidth;
Height = (int32)ElectraDecodersUtil::GetVariantValueSafeI64(InOptions, TEXT("max_height"), 0);
bps = ElectraDecodersUtil::GetVariantValueSafeI64(InOptions, TEXT("max_bitrate"), 0);
fps_n = ElectraDecodersUtil::GetVariantValueSafeI64(InOptions, TEXT("max_fps_n"), 0);
fps_d = (uint32)ElectraDecodersUtil::GetVariantValueSafeI64(InOptions, TEXT("max_fps_d"), 0);
}
else
{
Width = (int32)ElectraDecodersUtil::GetVariantValueSafeI64(InOptions, TEXT("width"), 0);
Height = (int32)ElectraDecodersUtil::GetVariantValueSafeI64(InOptions, TEXT("height"), 0);
bps = ElectraDecodersUtil::GetVariantValueSafeI64(InOptions, TEXT("bitrate"), 0);
fps_n = ElectraDecodersUtil::GetVariantValueSafeI64(InOptions, TEXT("fps_n"), 0);
fps_d = (uint32)ElectraDecodersUtil::GetVariantValueSafeI64(InOptions, TEXT("fps_d"), 0);
}
ElectraDecodersUtil::FMimeTypeVideoCodecInfo ci;
const FCodecFormatHelper::FCodecInfo* Codec = nullptr;
D3D12_VIDEO_DECODE_CONFIGURATION DecodeConfiguration {};
DecodeConfiguration.BitstreamEncryption = D3D12_BITSTREAM_ENCRYPTION_TYPE_NONE;
DecodeConfiguration.InterlaceType = D3D12_VIDEO_FRAME_CODED_INTERLACE_TYPE_NONE;
if (ElectraDecodersUtil::ParseCodecH264(ci, InCodecFormat))
{
// Only support Baseline, Main and High profile.
if (ci.Profile == 66 || ci.Profile == 77 || ci.Profile == 100)
{
Codec = CurrentFormats->HaveFormat(FCodecFormatHelper::ECodecType::H264, 8);
}
}
else if (ElectraDecodersUtil::ParseCodecH265(ci, InCodecFormat))
{
// The DXVA2 structure `DXVA_PicParams_HEVC` has fixed sizes for `column_width_minus1`
// and `row_height_minus1` that allow for at most level 6.3. Any higher level using tiles
// *could* use more than that and is hence not decodable.
if (ci.Level > 6*30+3)
{
return nullptr;
}
// ITU-T H.265 only specifies profile space 0.
if (ci.ProfileSpace != 0)
{
return nullptr;
}
// Main profile (8 bit)
if (ci.Profile == 1)
{
Codec = CurrentFormats->HaveFormat(FCodecFormatHelper::ECodecType::H265, 8);
}
// Main10 profile (10 bit)
else if (ci.Profile == 2)
{
Codec = CurrentFormats->HaveFormat(FCodecFormatHelper::ECodecType::H265, 10);
}
else
{
// Not supported.
return nullptr;
}
}
else if (ElectraDecodersUtil::ParseCodecVP9(ci, InCodecFormat, ElectraDecodersUtil::GetVariantValueUInt8Array(InOptions, TEXT("$vpcC_box"))))
{
// TODO:
}
else if (ElectraDecodersUtil::ParseCodecVP8(ci, InCodecFormat, ElectraDecodersUtil::GetVariantValueUInt8Array(InOptions, TEXT("$vpcC_box"))))
{
// Not supported at the moment. We do not have any device reporting this to be supported, so we could not properly handle this.
return nullptr;
}
if (Codec)
{
DecodeConfiguration.DecodeProfile = Codec->ProfileGUID;
OutSupport.NodeIndex = CurrentFormats->GetVideoDeviceNodeIndex();
OutSupport.Configuration = DecodeConfiguration;
OutSupport.Width = Width;
OutSupport.Height = Height;
// Use the first format that was reported back. We assume this is the best one possible.
OutSupport.DecodeFormat = Codec->PixelFormats[0];
OutSupport.FrameRate.Numerator = (uint32)fps_n;
OutSupport.FrameRate.Denominator = fps_n && fps_d ? fps_d : 0;
OutSupport.BitRate = (uint32)bps;
HRESULT Result = CurrentFormats->GetVideoDevice()->CheckFeatureSupport(D3D12_FEATURE_VIDEO_DECODE_SUPPORT, &OutSupport, sizeof(OutSupport));
if (Result == S_OK && (OutSupport.SupportFlags & D3D12_VIDEO_DECODE_SUPPORT_FLAG_SUPPORTED) == D3D12_VIDEO_DECODE_SUPPORT_FLAG_SUPPORTED)
{
// For simplicity we require decode tier 2
if (OutSupport.DecodeTier != D3D12_VIDEO_DECODE_TIER_2)
{
UE_LOG(LogD3D12VideoDecodersElectra, Log, TEXT("Decode tier 2 is needed, but tier %d was returned."), (int)OutSupport.DecodeTier);
Codec = nullptr;
}
// We don't support reference only allocations yet
if ((OutSupport.ConfigurationFlags & D3D12_VIDEO_DECODE_CONFIGURATION_FLAG_REFERENCE_ONLY_ALLOCATIONS_REQUIRED) != 0)
{
UE_LOG(LogD3D12VideoDecodersElectra, Log, TEXT("Decode reference only allocations are not supported, but configuration flags 0x%08x were returned."), (int)OutSupport.ConfigurationFlags);
Codec = nullptr;
}
// Do a custom platform capability check.
if (!FD3D12VideoDecoder::CheckPlatformDecodeCapabilities(OutSupport, ci, InOptions))
{
UE_LOG(LogD3D12VideoDecodersElectra, Log, TEXT("Platform rejected decoding of %d*%d @ %d/%d fps"), (int)Width, (int)Height, (int)fps_n, (int)fps_d);
Codec = nullptr;
}
}
else
{
UE_LOG(LogD3D12VideoDecodersElectra, Log, TEXT("Decoding of %d*%d @ %d/%d fps is not supported"), (int)Width, (int)Height, (int)fps_n, (int)fps_d);
Codec = nullptr;
}
}
return Codec;
}
int32 SupportsFormat(TMap<FString, FVariant>& OutFormatInfo, const FString& InCodecFormat, bool bInEncoder, const TMap<FString, FVariant>& InOptions) const override
{
if (bDoNotUseThisDecoder)
{
return 0;
}
// Encoder? Not supported here!
if (bInEncoder)
{
return 0;
}
// No formats, no support.
if (!CurrentFormats.IsValid() || !CurrentFormats->GetVideoDevice().IsValid())
{
return 0;
}
D3D12_FEATURE_DATA_VIDEO_DECODE_SUPPORT DecodeSupport {};
const FCodecFormatHelper::FCodecInfo* Codec = GetFormatIfSupported(DecodeSupport, InCodecFormat, InOptions);
return Codec ? 5 : 0;
}
static void GetPlatformConfigurationOptions(TMap<FString, FVariant>& OutOptions)
{
// TBD
OutOptions.Emplace(IElectraDecoderFeature::MinimumNumberOfOutputFrames, FVariant((int32)8));
OutOptions.Emplace(IElectraDecoderFeature::IsAdaptive, FVariant(false));
OutOptions.Emplace(IElectraDecoderFeature::SupportsDroppingOutput, FVariant(true));
OutOptions.Emplace(IElectraDecoderFeature::StartcodeToLength, FVariant(int32(0)));
}
void GetConfigurationOptions(TMap<FString, FVariant>& OutOptions) const override
{
GetPlatformConfigurationOptions(OutOptions);
}
TSharedPtr<IElectraDecoder, ESPMode::ThreadSafe> CreateDecoderForFormat(const FString& InCodecFormat, const TMap<FString, FVariant>& InOptions) override
{
if (bDoNotUseThisDecoder)
{
return nullptr;
}
HRESULT Result;
// Do this under lock as it may be possible that the D3D device changed and we have to rebuild the codec list.
FScopeLock lock(&AccessLock);
// Verify that we are using the same D3D device that was used in the initial format determination.
TRefCountPtr<ID3D12Device> D3DDevice;
int32 D3DDeviceVersion = 0;
if (!FElectraDecodersPlatformResources::GetD3DDeviceAndVersion((void**)D3DDevice.GetInitReference(), &D3DDeviceVersion))
{
UE_LOG(LogD3D12VideoDecodersElectra, Error, TEXT("Could not obtain the current RHI D3D device."));
return nullptr;
}
// Must be a D3D12 device.
if (D3DDeviceVersion != 12000)
{
UE_LOG(LogD3D12VideoDecodersElectra, Warning, TEXT("Current RHI D3D device is not a D3D12 device."));
return nullptr;
}
bool bRedoFromStart = false;
if (CurrentFormats.IsValid())
{
TRefCountPtr<ID3D12VideoDevice> VideoDevice;
if ((Result = D3DDevice->QueryInterface(__uuidof(ID3D12VideoDevice), (void**)VideoDevice.GetInitReference())) != S_OK)
{
UE_LOG(LogD3D12VideoDecodersElectra, Warning, TEXT("The current RHI device is not a video decoding capable device."));
return nullptr;
}
// Not the same video device any more?
if (VideoDevice != CurrentFormats->GetVideoDevice())
{
bRedoFromStart = true;
}
}
else
{
bRedoFromStart = true;
}
// Start over determining the supported formats?
if (bRedoFromStart)
{
if (!CurrentFormats.IsValid())
{
CurrentFormats = MakeUnique<FCodecFormatHelper>();
}
CurrentFormats->FindSupportedFormats(D3DDevice.GetReference());
}
D3D12_FEATURE_DATA_VIDEO_DECODE_SUPPORT DecodeSupport {};
const FCodecFormatHelper::FCodecInfo* Codec = GetFormatIfSupported(DecodeSupport, InCodecFormat, InOptions);
check(Codec);
if (!Codec)
{
UE_LOG(LogD3D12VideoDecodersElectra, Log, TEXT("The current RHI device does not support decoding this format."));
return nullptr;
}
TSharedPtr<FD3D12VideoDecoder, ESPMode::ThreadSafe> New;
switch(Codec->CodecType)
{
case FCodecFormatHelper::ECodecType::H264:
{
New = MakeShared<FD3D12VideoDecoder_H264>(*Codec, DecodeSupport, InOptions, D3DDevice, CurrentFormats->GetVideoDevice(), CurrentFormats->GetVideoDeviceNodeIndex());
break;
}
case FCodecFormatHelper::ECodecType::H265:
{
New = MakeShared<FD3D12VideoDecoder_H265>(*Codec, DecodeSupport, InOptions, D3DDevice, CurrentFormats->GetVideoDevice(), CurrentFormats->GetVideoDeviceNodeIndex());
break;
}
}
if (New.IsValid())
{
UE_LOG(LogD3D12VideoDecodersElectra, Verbose, TEXT("Created a D3D12 video decoder."));
}
return New;
}
};
FD3D12VideoDecoder::FD3D12VideoDecoder(const FCodecFormatHelper::FCodecInfo& InCodecInfo, const D3D12_FEATURE_DATA_VIDEO_DECODE_SUPPORT& InDecodeSupport, const TMap<FString, FVariant>& InOptions, const TRefCountPtr<ID3D12Device>& InD3D12Device, const TRefCountPtr<ID3D12VideoDevice>& InVideoDevice, uint32 InVideoDeviceNodeIndex)
: CodecInfo(InCodecInfo)
, DecodeSupport(InDecodeSupport)
, InitialCreationOptions(InOptions)
, D3D12Device(InD3D12Device)
, VideoDevice(InVideoDevice)
, VideoDeviceNodeIndex(InVideoDeviceNodeIndex)
{
}
FD3D12VideoDecoder::~FD3D12VideoDecoder()
{
// Note: It is the codec specific implementation's responsibility to check that the decoder
// has already been closed and/or do it. When we get here we cannot call into any
// derived classes methods any more as it has already been destroyed and the vtable
// has become invalid.
VideoDevice.SafeRelease();
D3D12Device.SafeRelease();
}
void FD3D12VideoDecoder::GetFeatures(TMap<FString, FVariant>& OutFeatures) const
{
FD3D12VideoDecoderFactory::GetPlatformConfigurationOptions(OutFeatures);
}
void FD3D12VideoDecoder::Close()
{
ResetToCleanStart();
// Set the error state that all subsequent calls will fail.
PostError(0, TEXT("Already closed"), ERRCODE_INTERNAL_ALREADY_CLOSED);
}
bool FD3D12VideoDecoder::ResetToCleanStart()
{
ReturnAllFrames();
if (VideoDecoderSync.IsValid())
{
VideoDecoderSync->AwaitCompletion(500);
}
if (DPB.IsValid())
{
// Return the "missing" frame if the decoder had to create one.
DPB->ReturnFrameToAvailableQueue(MoveTemp(MissingReferenceFrame));
DPB->ReleaseAllFrames(500);
DPB.Reset();
}
// Codec specific reset.
InternalResetToCleanStart();
RunningFrameNumLo = 0;
RunningFrameNumHi = 1;
bIsDraining = false;
TSharedPtr<FFrameDecodeResource, ESPMode::ThreadSafe> fdr;
while(AvailableFrameDecodeResourceQueue.Dequeue(fdr))
{
fdr->D3DDecoder.SafeRelease();
fdr->D3DDecoderHeap.SafeRelease();
}
VideoDecoder.SafeRelease();
VideoDecoderCommandList.SafeRelease();
VideoDecoderCommandAllocator.SafeRelease();
VideoDecoderCommandQueue.SafeRelease();
VideoDecoderSync.Reset();
CurrentConfig.Reset();
StatusReportFeedbackNumber = 0;
return true;
}
void FD3D12VideoDecoder::ReturnAllFrames()
{
auto ReturnFrames = [](TArray<TSharedPtr<FVideoDecoderOutputD3D12Electra, ESPMode::ThreadSafe>>& InList) -> void
{
while(!InList.IsEmpty())
{
if (InList[0]->OwningDPB.IsValid())
{
InList[0]->OwningDPB->ReturnFrameToAvailableQueue(MoveTemp(InList[0]->DecodedFrame));
InList[0]->OwningDPB.Reset();
}
InList.RemoveAt(0);
}
};
ReturnFrames(FramesGivenOutForOutput);
ReturnFrames(FramesReadyForOutput);
ReturnFrames(FramesInDecoder);
if (DPB.IsValid())
{
check(DPB->AvailableQueue.Num() + (MissingReferenceFrame.IsValid() ? 1 : 0) == DPB->Frames.Num());
}
}
IElectraDecoder::EDecoderError FD3D12VideoDecoder::ExecuteCommonDecode(const D3D12_VIDEO_DECODE_INPUT_STREAM_ARGUMENTS& InInputArgs, const D3D12_VIDEO_DECODE_OUTPUT_STREAM_ARGUMENTS& InOutputArgs)
{
HRESULT Result;
// All frames, the reference frames as well as the output frame are given in the list of reference frames.
// We can use that list to check all the frame's fences for readiness.
TSharedPtr<FDecodedFrame, ESPMode::ThreadSafe> TargetFrame;
for(uint32 i=0; i<InInputArgs.ReferenceFrames.NumTexture2Ds; ++i)
{
if (InInputArgs.ReferenceFrames.ppTexture2Ds[i])
{
check(InInputArgs.ReferenceFrames.pSubresources == nullptr || InInputArgs.ReferenceFrames.pSubresources[i] == 0);
TSharedPtr<FDecodedFrame, ESPMode::ThreadSafe> Frame = DPB->GetFrameForResource(InInputArgs.ReferenceFrames.ppTexture2Ds[i]);
check(Frame.IsValid());
if (!Frame.IsValid())
{
PostError(0, TEXT("ExecuteCommonDecode() did not find resource in reference frame list in the DPB"), ERRCODE_INTERNAL_FAILED_TO_DECODE);
return IElectraDecoder::EDecoderError::Error;
}
if (InInputArgs.ReferenceFrames.ppTexture2Ds[i] == InOutputArgs.pOutputTexture2D)
{
TargetFrame = Frame;
}
// Wait for the fence of the frame to be signaled.
// Do this with a timeout in case the outside code that works with these frames is stuck.
if (!Frame->Sync.AwaitCompletion(100))
{
UE_LOG(LogD3D12VideoDecodersElectra, Warning, TEXT("ExecuteCommonDecode() waited too long for a reference frame fence to be signaled. Trying again later."));
return IElectraDecoder::EDecoderError::NoBuffer;
}
}
}
// Check that the output frame really was in the list.
check(TargetFrame.IsValid());
if ((Result = VideoDecoderCommandAllocator->Reset()) != S_OK)
{
PostError(Result, TEXT("ExecuteCommonDecode() failed to reset command allocator"), ERRCODE_INTERNAL_FAILED_TO_DECODE);
return IElectraDecoder::EDecoderError::Error;
}
if ((Result = VideoDecoderCommandList->Reset(VideoDecoderCommandAllocator)) != S_OK)
{
PostError(Result, TEXT("ExecuteCommonDecode() failed to reset command list"), ERRCODE_INTERNAL_FAILED_TO_DECODE);
return IElectraDecoder::EDecoderError::Error;
}
const int32 kMaxBarriers = FFrameDecodeResource::kMaxRefFrames * 2;
check(InInputArgs.ReferenceFrames.NumTexture2Ds + 1 <= kMaxBarriers);
if (InInputArgs.ReferenceFrames.NumTexture2Ds + 1 > kMaxBarriers)
{
PostError(0, TEXT("ExecuteCommonDecode() out of barriers"), ERRCODE_INTERNAL_FAILED_TO_DECODE);
return IElectraDecoder::EDecoderError::Error;
}
TArray<ID3D12Resource*> TransitionedResources;
D3D12_RESOURCE_BARRIER Barriers[kMaxBarriers] {};
uint32 NumBarriers = 1;
// Transition the target frame to video-decode-write
Barriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
Barriers[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
Barriers[0].Transition.pResource = InOutputArgs.pOutputTexture2D;
Barriers[0].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
Barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_COMMON;
Barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_VIDEO_DECODE_WRITE;
TransitionedResources.Emplace(InOutputArgs.pOutputTexture2D);
for(uint32 i=0; i<InInputArgs.ReferenceFrames.NumTexture2Ds; ++i)
{
// Transition the reference frames to video-decode-read. Check that we do not transition the same resource
// more than once in case it appears in multiple reference frame slots.
if (InInputArgs.ReferenceFrames.ppTexture2Ds[i] && !TransitionedResources.Contains(InInputArgs.ReferenceFrames.ppTexture2Ds[i]))
{
Barriers[NumBarriers].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
Barriers[NumBarriers].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
Barriers[NumBarriers].Transition.pResource = InInputArgs.ReferenceFrames.ppTexture2Ds[i];
Barriers[NumBarriers].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
Barriers[NumBarriers].Transition.StateBefore = D3D12_RESOURCE_STATE_COMMON;
Barriers[NumBarriers].Transition.StateAfter = D3D12_RESOURCE_STATE_VIDEO_DECODE_READ;
TransitionedResources.Emplace(InInputArgs.ReferenceFrames.ppTexture2Ds[i]);
++NumBarriers;
}
}
VideoDecoderCommandList->ResourceBarrier(NumBarriers, Barriers);
VideoDecoderCommandList->DecodeFrame(VideoDecoder, &InOutputArgs, &InInputArgs);
// Reverse the transitions
for(uint32 i=0; i<NumBarriers; ++i)
{
Barriers[i].Transition.StateBefore = Barriers[i].Transition.StateAfter;
Barriers[i].Transition.StateAfter = D3D12_RESOURCE_STATE_COMMON;
}
VideoDecoderCommandList->ResourceBarrier(NumBarriers, Barriers);
if ((Result = VideoDecoderCommandList->Close()) != S_OK)
{
PostError(Result, TEXT("ExecuteCommonDecode() closing command list failed"), ERRCODE_INTERNAL_FAILED_TO_DECODE);
return IElectraDecoder::EDecoderError::Error;
}
// Execute the command list
ID3D12CommandList* dcmdl[1] = { VideoDecoderCommandList.GetReference() };
VideoDecoderCommandQueue->ExecuteCommandLists(1, dcmdl);
if ((Result = VideoDecoderCommandQueue->Signal(TargetFrame->Sync.GetFence(), TargetFrame->Sync.IncrementAndGetNewFenceValue())) != S_OK)
{
PostError(Result, TEXT("ExecuteCommonDecode() signaling target frame fence in command queue failed"), ERRCODE_INTERNAL_FAILED_TO_DECODE);
return IElectraDecoder::EDecoderError::Error;
}
if ((Result = VideoDecoderCommandQueue->Signal(VideoDecoderSync->GetFence(), VideoDecoderSync->IncrementAndGetNewFenceValue())) != S_OK)
{
PostError(Result, TEXT("ExecuteCommonDecode() signaling decoder fence in command queue failed"), ERRCODE_INTERNAL_FAILED_TO_DECODE);
return IElectraDecoder::EDecoderError::Error;
}
return IElectraDecoder::EDecoderError::None;
}
IElectraDecoder::EOutputStatus FD3D12VideoDecoder::HaveOutput()
{
if (LastError.IsSet())
{
return IElectraDecoder::EOutputStatus::Error;
}
if (FramesReadyForOutput.Num())
{
return IElectraDecoder::EOutputStatus::Available;
}
if (!VideoDecoder.IsValid())
{
return IElectraDecoder::EOutputStatus::NeedInput;
}
if (bIsDraining)
{
bIsDraining = false;
ReturnAllFrames();
return IElectraDecoder::EOutputStatus::EndOfData;
}
return IElectraDecoder::EOutputStatus::NeedInput;
}
TSharedPtr<IElectraDecoderOutput, ESPMode::ThreadSafe> FD3D12VideoDecoder::GetOutput()
{
if (FramesReadyForOutput.Num())
{
auto Out = FramesReadyForOutput[0];
FramesReadyForOutput.RemoveAt(0);
FramesGivenOutForOutput.Emplace(Out);
return Out;
}
return nullptr;
}
bool FD3D12VideoDecoder::InternalDecoderCreate()
{
check(D3D12Device.IsValid() && VideoDevice.IsValid());
if (!D3D12Device.IsValid() || !VideoDevice.IsValid())
{
return PostError(0, TEXT("No D3D video device set"), ERRCODE_INTERNAL_FAILED_TO_CREATE_DECODER);
}
check(VideoDeviceNodeIndex == DecodeSupport.NodeIndex);
const uint32 VideoDeviceNodeMask = GetNodeMask();
HRESULT Result;
TUniquePtr<FSyncObject> NewSync = MakeUnique<FSyncObject>();
if ((Result = NewSync->Create(D3D12Device, 0)) != S_OK)
{
return PostError(Result, TEXT("Creating sync object"), ERRCODE_INTERNAL_FAILED_TO_CREATE_DECODER);
}
TRefCountPtr<ID3D12CommandQueue> NewCommandQueue;
D3D12_COMMAND_QUEUE_DESC CommandQueueDesc {};
CommandQueueDesc.Type = D3D12_COMMAND_LIST_TYPE_VIDEO_DECODE;
CommandQueueDesc.NodeMask = VideoDeviceNodeMask;
if ((Result = D3D12Device->CreateCommandQueue(&CommandQueueDesc, __uuidof(ID3D12CommandQueue), (void**)NewCommandQueue.GetInitReference())) != S_OK)
{
return PostError(Result, TEXT("CreateCommandQueue() failed"), ERRCODE_INTERNAL_FAILED_TO_CREATE_DECODER);
}
TRefCountPtr<ID3D12CommandAllocator> NewCommandAllocator;
if ((Result = D3D12Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_VIDEO_DECODE, __uuidof(ID3D12CommandAllocator), (void**)NewCommandAllocator.GetInitReference())) != S_OK)
{
return PostError(Result, TEXT("CreateCommandAllocator() failed"), ERRCODE_INTERNAL_FAILED_TO_CREATE_DECODER);
}
TRefCountPtr<ID3D12VideoDecodeCommandList> NewDecodeCommandList;
if ((Result = D3D12Device->CreateCommandList(VideoDeviceNodeMask, D3D12_COMMAND_LIST_TYPE_VIDEO_DECODE, NewCommandAllocator.GetReference(), nullptr, __uuidof(ID3D12VideoDecodeCommandList), (void**)NewDecodeCommandList.GetInitReference())) != S_OK)
{
return PostError(Result, TEXT("CreateCommandList() failed"), ERRCODE_INTERNAL_FAILED_TO_CREATE_DECODER);
}
if ((Result = NewDecodeCommandList->Close()) != S_OK)
{
return PostError(Result, TEXT("CommandList->Close() failed"), ERRCODE_INTERNAL_FAILED_TO_CREATE_DECODER);
}
ID3D12CommandList* CommandLists[1] = { NewDecodeCommandList.GetReference() };
NewCommandQueue->ExecuteCommandLists(1, CommandLists);
if ((Result = NewCommandQueue->Signal(NewSync->GetFence(), NewSync->IncrementAndGetNewFenceValue())) != S_OK)
{
return PostError(Result, TEXT("CommandQueue->Signal() failed"), ERRCODE_INTERNAL_FAILED_TO_CREATE_DECODER);
}
bool bOk = NewSync->AwaitCompletion(INFINITE);
check(bOk);
TRefCountPtr<ID3D12VideoDecoder> NewDecoder;
D3D12_VIDEO_DECODER_DESC DecoderDesc {};
DecoderDesc.NodeMask = VideoDeviceNodeMask;
DecoderDesc.Configuration = DecodeSupport.Configuration;
if ((Result = VideoDevice->CreateVideoDecoder(&DecoderDesc, __uuidof(ID3D12VideoDecoder), (void**)NewDecoder.GetInitReference())) != S_OK)
{
return PostError(Result, TEXT("CreateVideoDecoder() failed"), ERRCODE_INTERNAL_FAILED_TO_CREATE_DECODER);
}
VideoDecoderSync = MoveTemp(NewSync);
VideoDecoderCommandQueue = NewCommandQueue;
VideoDecoderCommandAllocator = NewCommandAllocator;
VideoDecoderCommandList = NewDecodeCommandList;
VideoDecoder = NewDecoder;
return true;
}
bool FD3D12VideoDecoder::CreateDecoderHeap(int32 InDPBSize, int32 InMaxWidth, int32 InMaxHeight, int32 InImageSizeAlignment)
{
if (InDPBSize <= 0)
{
return PostError(0, TEXT("DPB size is invalid"), ERRCODE_INTERNAL_FAILED_TO_CREATE_DECODER);
}
HRESULT Result;
TRefCountPtr<ID3D12VideoDecoderHeap> NewHeap;
D3D12_VIDEO_DECODER_HEAP_DESC HeapDesc {};
HeapDesc.NodeMask = GetNodeMask();
HeapDesc.Configuration = DecodeSupport.Configuration;
const uint32 Alignment = (uint32) InImageSizeAlignment;
const uint32 AlignedWidth = Align(InMaxWidth, Alignment);
const uint32 AlignedHeight = Align(InMaxHeight, Alignment);
#if 1
HeapDesc.DecodeWidth = AlignedWidth;
HeapDesc.DecodeHeight = AlignedHeight;
#else
HeapDesc.DecodeWidth = DecodeSupport.Width;
HeapDesc.DecodeHeight = DecodeSupport.Height;
#endif
check(!CodecInfo.PixelFormats.IsEmpty());
HeapDesc.Format = CodecInfo.PixelFormats[0];
// best not to set those
//HeapDesc.FrameRate = DecodeSupport.FrameRate;
//HeapDesc.BitRate = DecodeSupport.BitRate;
HeapDesc.MaxDecodePictureBufferCount = InDPBSize;
if ((Result = VideoDevice->CreateVideoDecoderHeap(&HeapDesc, __uuidof(ID3D12VideoDecoderHeap), (void**)NewHeap.GetInitReference())) != S_OK)
{
return PostError(Result, TEXT("CreateVideoDecoderHeap() failed"), ERRCODE_INTERNAL_FAILED_TO_CREATE_BUFFER);
}
CurrentConfig.VideoDecoderHeap = NewHeap;
CurrentConfig.VideoDecoderDPBWidth = InMaxWidth;
CurrentConfig.VideoDecoderDPBHeight = InMaxHeight;
CurrentConfig.MaxNumInDPB = InDPBSize;
return true;
}
bool FD3D12VideoDecoder::CreateDPB(TSharedPtr<FDecodedPictureBuffer, ESPMode::ThreadSafe>& OutDPB, int32 InMaxWidth, int32 InMaxHeight, int32 InImageSizeAlignment, int32 InNumFrames)
{
if (InNumFrames <= 0)
{
return PostError(0, TEXT("Bad number of frames"), ERRCODE_INTERNAL_FAILED_TO_CREATE_DECODER);
}
if (InNumFrames > FFrameDecodeResource::kMaxRefFrames)
{
return PostError(0, TEXT("Too many frames requested than fit into managing structure."), ERRCODE_INTERNAL_FAILED_TO_CREATE_DECODER);
}
TSharedPtr<FDecodedPictureBuffer, ESPMode::ThreadSafe> newdpb = MakeShared<FDecodedPictureBuffer, ESPMode::ThreadSafe>();
newdpb->Frames.SetNum(InNumFrames);
D3D12_HEAP_PROPERTIES heapProps {};
heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
heapProps.CreationNodeMask =
heapProps.VisibleNodeMask = GetNodeMask();
const uint32 Alignment = (uint32) InImageSizeAlignment;
const uint32 AlignedWidth = Align(InMaxWidth, Alignment);
const uint32 AlignedHeight = Align(InMaxHeight, Alignment);
check(!CodecInfo.PixelFormats.IsEmpty());
D3D12_RESOURCE_DESC desc {};
desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
desc.Alignment = 0;
desc.Width = AlignedWidth;
desc.Height = AlignedHeight;
desc.DepthOrArraySize = 1;
desc.MipLevels = 1;
desc.Format = CodecInfo.PixelFormats[0];
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
HRESULT Result;
for(int32 i=0; i<InNumFrames; ++i)
{
newdpb->Frames[i] = MakeShared<FDecodedFrame, ESPMode::ThreadSafe>();
newdpb->Frames[i]->IndexInPictureBuffer = i;
if ((Result = D3D12Device->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_COMMON, nullptr, __uuidof(ID3D12Resource), (void**)newdpb->Frames[i]->Texture.GetInitReference())) != S_OK)
{
return PostError(Result, TEXT("CreateCommittedResource() failed while creating the DPB"), ERRCODE_INTERNAL_FAILED_TO_CREATE_BUFFER);
}
if ((Result = newdpb->Frames[i]->Sync.Create(D3D12Device, 0)) != S_OK)
{
return PostError(Result, TEXT("Creating fence failed while creating the DPB"), ERRCODE_INTERNAL_FAILED_TO_CREATE_BUFFER);
}
}
// Add all frames to the available queue.
for(auto& it : newdpb->Frames)
{
newdpb->AvailableQueue.Push(it);
}
OutDPB = MoveTemp(newdpb);
CurrentConfig.MaxDecodedWidth = InMaxWidth;
CurrentConfig.MaxDecodedHeight = InMaxHeight;
return true;
}
bool FD3D12VideoDecoder::PrepareBitstreamBuffer(const TSharedPtr<FFrameDecodeResource, ESPMode::ThreadSafe>& InFrameDecodeResourceToPrepare, uint32 InMaxInputBufferSize)
{
check(InFrameDecodeResourceToPrepare);
if (InFrameDecodeResourceToPrepare && (!InFrameDecodeResourceToPrepare->D3DBitstreamBuffer.IsValid() || InFrameDecodeResourceToPrepare->D3DBitstreamBufferAllocatedSize < InMaxInputBufferSize))
{
InFrameDecodeResourceToPrepare->D3DBitstreamBuffer.SafeRelease();
InFrameDecodeResourceToPrepare->D3DBitstreamBufferAllocatedSize = 0;
HRESULT Result;
D3D12_HEAP_PROPERTIES HeapProps {};
HeapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
HeapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
HeapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
HeapProps.CreationNodeMask =
HeapProps.VisibleNodeMask = GetNodeMask();
D3D12_RESOURCE_DESC desc {};
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER,
desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
desc.Width = InMaxInputBufferSize;
desc.Height = 1;
desc.DepthOrArraySize = 1;
desc.MipLevels = 1;
desc.Format = DXGI_FORMAT_UNKNOWN;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
if ((Result = D3D12Device->CreateCommittedResource(&HeapProps, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, __uuidof(ID3D12Resource), (void**)InFrameDecodeResourceToPrepare->D3DBitstreamBuffer.GetInitReference())) != S_OK)
{
return PostError(Result, TEXT("Bitstream buffer CreateCommittedResource() failed"), ERRCODE_INTERNAL_FAILED_TO_DECODE);
}
InFrameDecodeResourceToPrepare->D3DBitstreamBufferAllocatedSize = InMaxInputBufferSize;
}
return true;
}
static TSharedPtr<FD3D12VideoDecoderFactory, ESPMode::ThreadSafe> Self;
} // namespace ElectraVideoDecodersD3D12Video
/*********************************************************************************************************************/
/*********************************************************************************************************************/
/*********************************************************************************************************************/
void FD3D12VideoDecoder::Startup()
{
if (!ElectraVideoDecodersD3D12Video::bDisableThisDecoder)
{
// Make sure the codec factory module has been loaded.
FModuleManager::Get().LoadModule(TEXT("ElectraCodecFactory"));
TUniquePtr<ElectraVideoDecodersD3D12Video::FCodecFormatHelper> FormatHelper(new ElectraVideoDecodersD3D12Video::FCodecFormatHelper);
// Not a single supported format?
if (FormatHelper->FindSupportedFormats(nullptr) != 0)
{
// Create a factory with the current formats.
ElectraVideoDecodersD3D12Video::Self = MakeShared<ElectraVideoDecodersD3D12Video::FD3D12VideoDecoderFactory, ESPMode::ThreadSafe>(MoveTemp(FormatHelper));
// Register as modular feature.
IModularFeatures::Get().RegisterModularFeature(IElectraCodecFactoryModule::GetModularFeatureName(), ElectraVideoDecodersD3D12Video::Self.Get());
}
else
{
UE_LOG(LogD3D12VideoDecodersElectra, Log, TEXT("D3D12 video decoding will not be used since no supported format was found."));
}
}
else
{
UE_LOG(LogD3D12VideoDecodersElectra, Log, TEXT("D3D12 video decoding will not be used since it is disabled."));
}
}
void FD3D12VideoDecoder::Shutdown()
{
if (ElectraVideoDecodersD3D12Video::Self.IsValid())
{
IModularFeatures::Get().UnregisterModularFeature(IElectraCodecFactoryModule::GetModularFeatureName(), ElectraVideoDecodersD3D12Video::Self.Get());
ElectraVideoDecodersD3D12Video::Self.Reset();
}
}