// Copyright Epic Games, Inc. All Rights Reserved. #include "Video/Encoders/VideoEncoderNVENC.h" #include "Video/Encoders/Configs/VideoEncoderConfigAV1.h" #include "Video/Encoders/Configs/VideoEncoderConfigH264.h" #include "Video/Encoders/Configs/VideoEncoderConfigH265.h" #include "Video/Resources/Vulkan/VideoResourceVulkan.h" #if PLATFORM_WINDOWS #include "Video/Resources/D3D/VideoResourceD3D.h" #endif class FNVENCModule : public IModuleInterface { public: virtual void StartupModule() override { TFunction const&, GUID)> CheckCodecSupport = [](TSharedRef const& Device, GUID CodecGUID) { if(!Device->HasContext()) { return false; } if(!FAPI::Get().IsValid()) { return false; } NV_ENC_STRUCT(NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS, SessionParams); SessionParams.apiVersion = NVENCAPI_VERSION; SessionParams.deviceType = NV_ENC_DEVICE_TYPE_CUDA; SessionParams.device = Device->GetContext()->Raw; void* Encoder = nullptr; NVENCSTATUS Result = FAPI::Get().nvEncOpenEncodeSessionEx(&const_cast(SessionParams), &Encoder); if (Result != NV_ENC_SUCCESS) { return false; } uint32 Count = 0; Result = FAPI::Get().nvEncGetEncodeGUIDCount(Encoder, &Count); if (Result != NV_ENC_SUCCESS || Count == 0) { return false; } TArray GUIDs; GUIDs.SetNumZeroed(Count); Result = FAPI::Get().nvEncGetEncodeGUIDs(Encoder, GUIDs.GetData(), Count, &Count); if (Result != NV_ENC_SUCCESS) { return false; } bool bSupported = false; for (uint32 i = 0; i < Count; i++) { if (!FMemory::Memcmp(&GUIDs[i], &CodecGUID, sizeof(GUID))) { bSupported = true; break; } } Result = FAPI::Get().nvEncDestroyEncoder(Encoder); if (Result != NV_ENC_SUCCESS) { return false; } return bSupported; }; /* Register NVENC+CUDA H264 pathway for D3D12 and Vulkan. */ FVideoEncoder ::RegisterPermutationsOf ::With< FVideoResourceCUDA, #if PLATFORM_WINDOWS FVideoResourceD3D12, #endif FVideoResourceVulkan> ::And( [CheckCodecSupport](TSharedRef const& NewDevice, TSharedRef const& NewInstance) { // static local because we don't want to constantly create new encoder sessions every time we query codec support static bool bSupportsCodec = CheckCodecSupport(NewDevice, NV_ENC_CODEC_H264_GUID); return FAPI::Get().IsValid() && NewDevice->HasContext() && bSupportsCodec; }); /* Register NVENC+CUDA H265 pathway for D3D12 and Vulkan. */ FVideoEncoder ::RegisterPermutationsOf ::With< FVideoResourceCUDA, #if PLATFORM_WINDOWS FVideoResourceD3D12, #endif FVideoResourceVulkan> ::And( [CheckCodecSupport](TSharedRef const& NewDevice, TSharedRef const& NewInstance) { // static local because we don't want to constantly create new encoder sessions every time we query codec support static bool bSupportsCodec = CheckCodecSupport(NewDevice, NV_ENC_CODEC_HEVC_GUID); return FAPI::Get().IsValid() && NewDevice->HasContext() && bSupportsCodec; }); /* Register NVENC+CUDA AV1 pathway for D3D12 and Vulkan. */ FVideoEncoder ::RegisterPermutationsOf ::With< FVideoResourceCUDA, #if PLATFORM_WINDOWS FVideoResourceD3D12, #endif FVideoResourceVulkan> ::And( [CheckCodecSupport](TSharedRef const& NewDevice, TSharedRef const& NewInstance) { // static local because we don't want to constantly create new encoder sessions every time we query codec support static bool bSupportsCodec = CheckCodecSupport(NewDevice, NV_ENC_CODEC_AV1_GUID); return FAPI::Get().IsValid() && NewDevice->HasContext() && bSupportsCodec; }); /* Register NVENC + raw DX11 graphics device pathway for D3D11. * The reason we seperate these out in this way is because D3D11 + CUDA/NVENC does not encode UE textures properly due to how they are laid out. */ #if PLATFORM_WINDOWS FVideoEncoder ::RegisterPermutationsOf ::With ::And( [CheckCodecSupport](TSharedRef const& NewDevice, TSharedRef const& NewInstance) { // static local because we don't want to constantly create new encoder sessions every time we query codec support static bool bSupportsCodec = CheckCodecSupport(NewDevice, NV_ENC_CODEC_H264_GUID); return FAPI::Get().IsValid() && NewDevice->HasContext() && bSupportsCodec; }); FVideoEncoder ::RegisterPermutationsOf ::With ::And( [CheckCodecSupport](TSharedRef const& NewDevice, TSharedRef const& NewInstance) { // static local because we don't want to constantly create new encoder sessions every time we query codec support static bool bSupportsCodec = CheckCodecSupport(NewDevice, NV_ENC_CODEC_HEVC_GUID); return FAPI::Get().IsValid() && NewDevice->HasContext() && bSupportsCodec; }); FVideoEncoder ::RegisterPermutationsOf ::With ::And( [CheckCodecSupport](TSharedRef const& NewDevice, TSharedRef const& NewInstance) { // static local because we don't want to constantly create new encoder sessions every time we query codec support static bool bSupportsCodec = CheckCodecSupport(NewDevice, NV_ENC_CODEC_AV1_GUID); return FAPI::Get().IsValid() && NewDevice->HasContext() && bSupportsCodec; }); #endif // PLATFORM_WINDOWS } }; IMPLEMENT_MODULE(FNVENCModule, NVENC);