code
This commit is contained in:
72
Plugins/AndroidVulkanVideo_5.3/AndroidVulkanVideo.uplugin
Normal file
72
Plugins/AndroidVulkanVideo_5.3/AndroidVulkanVideo.uplugin
Normal file
@ -0,0 +1,72 @@
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"Version": 10,
|
||||
"VersionName": "v2.05",
|
||||
"FriendlyName": "DirectVideo Android",
|
||||
"Description": "Android video playback using Vulkan",
|
||||
"Category": "Rendering",
|
||||
"CreatedBy": "Joe Marshall",
|
||||
"CreatedByURL": "",
|
||||
"DocsURL": "",
|
||||
"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/342ae80f4bc04e4aa232991047532ff2",
|
||||
"SupportURL": "https://joemarshall.github.io/directvideo/",
|
||||
"EngineVersion": "5.3.0",
|
||||
"CanContainContent": true,
|
||||
"Installed": true,
|
||||
"SupportedTargetPlatforms": [
|
||||
"Win64",
|
||||
"Android"
|
||||
],
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "AndroidVulkanVideo",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "PostConfigInit",
|
||||
"PlatformAllowList": [
|
||||
"Android"
|
||||
],
|
||||
"TargetAllowList": [
|
||||
"Game",
|
||||
"Client"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "VulkanVideoMeshComponent",
|
||||
"Type": "RuntimeNoCommandlet",
|
||||
"LoadingPhase": "PostEngineInit",
|
||||
"PlatformAllowList": [
|
||||
"Android"
|
||||
],
|
||||
"TargetAllowList": [
|
||||
"Game",
|
||||
"Editor",
|
||||
"Client",
|
||||
"Server"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "AndroidVulkanVideoFactory",
|
||||
"Type": "RuntimeNoCommandlet",
|
||||
"LoadingPhase": "PostEngineInit",
|
||||
"PlatformAllowList": [
|
||||
"Android"
|
||||
],
|
||||
"TargetAllowList": [
|
||||
"Game",
|
||||
"Editor",
|
||||
"Client",
|
||||
"Server"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "AndroidVulkanVideoFactory",
|
||||
"Type": "Editor",
|
||||
"LoadingPhase": "PostEngineInit"
|
||||
},
|
||||
{
|
||||
"Name": "VulkanVideoMeshComponent",
|
||||
"Type": "Editor",
|
||||
"LoadingPhase": "PostEngineInit"
|
||||
}
|
||||
]
|
||||
}
|
BIN
Plugins/AndroidVulkanVideo_5.3/Resources/Icon128.png
(Stored with Git LFS)
Normal file
BIN
Plugins/AndroidVulkanVideo_5.3/Resources/Icon128.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -0,0 +1,88 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// Build AndroidVulkanVideo module
|
||||
// ------------------------------------------------
|
||||
|
||||
using UnrealBuildTool;
|
||||
using System.IO;
|
||||
using System;
|
||||
|
||||
public class AndroidVulkanVideo : ModuleRules
|
||||
{
|
||||
public AndroidVulkanVideo(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
CppStandard = CppStandardVersion.Cpp17;
|
||||
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add public include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add other private include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
"Projects"
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"CoreUObject", "Engine","RHI","VulkanRHI","AudioExtensions","MediaUtils"
|
||||
// ... add private dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Media"
|
||||
}
|
||||
);
|
||||
|
||||
PrivateIncludePathModuleNames.AddRange(
|
||||
new string[] {
|
||||
"Media","MediaUtils"
|
||||
});
|
||||
|
||||
if (Target.Platform == UnrealTargetPlatform.Android)
|
||||
{
|
||||
PrivateDependencyModuleNames.AddRange(new string[] { "Launch" });
|
||||
// AdditionalPropertiesForReceipt.Add("AndroidPlugin", Path.Combine(ModuleDirectory, "Copy_Lib_Android.xml"));
|
||||
AddEngineThirdPartyPrivateStaticDependencies(Target, "Vulkan");
|
||||
PublicSystemLibraries.Add("libmediandk");
|
||||
string overridelib_src_path = Path.GetFullPath("../overridelib/BuildOverrideLib.xml", ModuleDirectory);
|
||||
string overridelib_built_path = Path.GetFullPath("./overridelib_built/InstallOverrideLib.xml",ModuleDirectory);
|
||||
if(File.Exists(overridelib_src_path)){
|
||||
// build vulkan override layer from scratch if we have code
|
||||
AdditionalPropertiesForReceipt.Add("AndroidPlugin", overridelib_src_path);
|
||||
}else if(File.Exists(overridelib_built_path)){
|
||||
// otherwise copy pre-built vulkan override layer .so into project
|
||||
AdditionalPropertiesForReceipt.Add("AndroidPlugin", overridelib_built_path);
|
||||
}else{
|
||||
// ERROR
|
||||
throw new InvalidOperationException("Need "+overridelib_built_path+" to build");
|
||||
}
|
||||
}
|
||||
|
||||
bPrecompile = true;
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,716 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// The main IMediaPlayer implementation. This is a
|
||||
// shim which provides Unreal interfaces, and then
|
||||
// defers everything vulkan related to the main
|
||||
// AndroidVulkanVideoImpl class.
|
||||
// ------------------------------------------------
|
||||
|
||||
#include "AndroidVulkanMediaPlayer.h"
|
||||
#include "AndroidVulkanTextureSample.h"
|
||||
|
||||
#include "IVulkanImpl.h"
|
||||
|
||||
#include "VideoMediaSampleHolder.h"
|
||||
|
||||
#include "UnrealArchiveFileSource.h"
|
||||
#include "UnrealAudioOut.h"
|
||||
|
||||
#include "HAL/FileManager.h"
|
||||
#include "HAL/PlatformFilemanager.h"
|
||||
#include "IPlatformFilePak.h"
|
||||
#include "Misc/ConfigCacheIni.h" // for GConfig access
|
||||
|
||||
#include "UnrealLogging.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FAndroidVulkanVideoModule"
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
static void *implDLL = NULL;
|
||||
|
||||
// logging object passed to impl
|
||||
static UnrealLogger logger;
|
||||
|
||||
typedef IVulkanImpl *(*CreateImplType)();
|
||||
typedef void (*DestroyImplType)(IVulkanImpl *);
|
||||
static CreateImplType createImpl = NULL;
|
||||
static DestroyImplType destroyImpl = NULL;
|
||||
|
||||
FAndroidVulkanMediaPlayer::FAndroidVulkanMediaPlayer(IMediaEventSink &InEventSink)
|
||||
: SampleQueue(MakeShared<FVideoMediaSampleHolder, ESPMode::ThreadSafe>()),
|
||||
EventSink(InEventSink)
|
||||
{
|
||||
if (implDLL == NULL)
|
||||
{
|
||||
implDLL = dlopen("libVkLayer_OverrideLib.so", RTLD_NOW | RTLD_LOCAL);
|
||||
createImpl = (CreateImplType)(dlsym(implDLL, "createImpl"));
|
||||
destroyImpl = (DestroyImplType)(dlsym(implDLL, "destroyImpl"));
|
||||
}
|
||||
impl = createImpl();
|
||||
impl->setDataCallback(this);
|
||||
impl->setLogger(&logger);
|
||||
impl->setLooping(Looping);
|
||||
// get log format bitmask from defaultEngine.ini
|
||||
int32 LogVisibility;
|
||||
if (GConfig->GetInt(TEXT("DirectVideo"), TEXT("LogBitmask"), LogVisibility, GEngineIni))
|
||||
{
|
||||
logger.SetLogVisibilityBitmask((int64)LogVisibility);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.SetLogVisibilityBitmask(ILogger::LogTypes::ALL_LOGS_BITMASK);
|
||||
}
|
||||
// get output format from defaultEngine.ini
|
||||
FString OutFormat;
|
||||
EMediaTextureSampleFormat SampleFormat = EMediaTextureSampleFormat::CharBGR10A2;
|
||||
if (GConfig->GetString(TEXT("DirectVideo"), TEXT("OutputFormat"), OutFormat, GEngineIni))
|
||||
{
|
||||
if (OutFormat.Equals(TEXT("CharBGR10A2"), ESearchCase::IgnoreCase))
|
||||
{
|
||||
SampleFormat = EMediaTextureSampleFormat::CharBGR10A2;
|
||||
}
|
||||
else if (OutFormat.Equals(TEXT("CharBGRA"), ESearchCase::IgnoreCase))
|
||||
{
|
||||
SampleFormat = EMediaTextureSampleFormat::CharBGRA;
|
||||
}
|
||||
|
||||
else if (OutFormat.Equals(TEXT("CharRGBA"), ESearchCase::IgnoreCase))
|
||||
{
|
||||
SampleFormat = EMediaTextureSampleFormat::CharRGBA;
|
||||
}
|
||||
else if (OutFormat.Equals(TEXT("RGBA16"), ESearchCase::IgnoreCase))
|
||||
{
|
||||
SampleFormat = EMediaTextureSampleFormat::RGBA16;
|
||||
}
|
||||
|
||||
else if (OutFormat.Equals(TEXT("FloatRGB"), ESearchCase::IgnoreCase))
|
||||
{
|
||||
SampleFormat = EMediaTextureSampleFormat::FloatRGB;
|
||||
}
|
||||
else if (OutFormat.Equals(TEXT("FloatRGBA"), ESearchCase::IgnoreCase))
|
||||
{
|
||||
SampleFormat = EMediaTextureSampleFormat::FloatRGBA;
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, Error, "Bad sample format in defaultEngine.ini");
|
||||
}
|
||||
}
|
||||
AndroidVulkanTextureSample::SetVideoFormat(SampleFormat);
|
||||
|
||||
AudioOut = new UnrealAudioOut(NULL);
|
||||
impl->setAudioOut(AudioOut);
|
||||
PlayState = EMediaState::Closed;
|
||||
CurInfo.Empty();
|
||||
DelegateEnterBackground = FCoreDelegates::ApplicationWillEnterBackgroundDelegate.AddRaw(
|
||||
this, &FAndroidVulkanMediaPlayer::OnEnterBackground);
|
||||
DelegateEnterForeground = FCoreDelegates::ApplicationHasEnteredForegroundDelegate.AddRaw(
|
||||
this, &FAndroidVulkanMediaPlayer::OnEnterForeground);
|
||||
SeekIndex = 0;
|
||||
SentBlankFrame = false;
|
||||
Seeking = false;
|
||||
}
|
||||
|
||||
FAndroidVulkanMediaPlayer::~FAndroidVulkanMediaPlayer()
|
||||
{
|
||||
Close();
|
||||
SentBlankFrame = true;
|
||||
if (DelegateEnterBackground.IsValid())
|
||||
{
|
||||
FCoreDelegates::ApplicationWillEnterBackgroundDelegate.Remove(DelegateEnterBackground);
|
||||
}
|
||||
if (DelegateEnterForeground.IsValid())
|
||||
{
|
||||
FCoreDelegates::ApplicationHasEnteredForegroundDelegate.Remove(DelegateEnterForeground);
|
||||
}
|
||||
destroyImpl(impl);
|
||||
impl = NULL;
|
||||
delete AudioOut;
|
||||
AudioOut = NULL;
|
||||
}
|
||||
|
||||
// IMediaPlayer
|
||||
// --------------------------------
|
||||
void FAndroidVulkanMediaPlayer::Close()
|
||||
{
|
||||
SentBlankFrame = false;
|
||||
|
||||
if (PlayState != EMediaState::Closed)
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Media player close called");
|
||||
SampleQueue->FlushSamples();
|
||||
VideoSamplePool.ReleaseEverything();
|
||||
SeekIndex = 0;
|
||||
Seeking = false;
|
||||
EventSink.ReceiveMediaEvent(EMediaEvent::TracksChanged);
|
||||
EventSink.ReceiveMediaEvent(EMediaEvent::MediaClosed);
|
||||
PlayState = EMediaState::Closed;
|
||||
// close the impl last because it will
|
||||
// kill all the texture images that we release in the pool above
|
||||
impl->close();
|
||||
}
|
||||
}
|
||||
FString FAndroidVulkanMediaPlayer::GetInfo() const
|
||||
{
|
||||
return CurInfo;
|
||||
}
|
||||
FGuid FAndroidVulkanMediaPlayer::GetPlayerPluginGUID() const
|
||||
{
|
||||
static FGuid OurGUID(0x9bf2d7c6, 0xb2b84d26, 0xb6ae5a3a, 0xc9883569);
|
||||
return OurGUID;
|
||||
}
|
||||
FString FAndroidVulkanMediaPlayer::GetStats() const
|
||||
{
|
||||
return TEXT("Not implemented");
|
||||
}
|
||||
FString FAndroidVulkanMediaPlayer::GetUrl() const
|
||||
{
|
||||
return VideoURL;
|
||||
}
|
||||
bool FAndroidVulkanMediaPlayer::Open(const FString &Url, const IMediaOptions *Options)
|
||||
{
|
||||
Close();
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Try open Url {0}", Url);
|
||||
SentBlankFrame = false;
|
||||
bool started = false;
|
||||
FString fullPath = Url;
|
||||
if (fullPath.StartsWith("file://"))
|
||||
{
|
||||
fullPath = fullPath.RightChop(7);
|
||||
}
|
||||
if (fullPath.Contains("://"))
|
||||
{
|
||||
// a (non-file url)
|
||||
started = impl->startVideoURL(TCHAR_TO_UTF8(*fullPath), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// a file path - check if it is local or not
|
||||
if (fullPath.StartsWith("./"))
|
||||
{
|
||||
fullPath = FPaths::ProjectContentDir() + fullPath.RightChop(2);
|
||||
}
|
||||
FPaths::NormalizeFilename(fullPath);
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Start video {0}", fullPath);
|
||||
IAndroidPlatformFile &PlatformFile = IAndroidPlatformFile::GetPlatformPhysical();
|
||||
if (PlatformFile.FileExists(*fullPath))
|
||||
{
|
||||
int64 FileOffset = PlatformFile.FileStartOffset(*fullPath);
|
||||
int64 FileSize = PlatformFile.FileSize(*fullPath);
|
||||
FString FileRootPath = PlatformFile.FileRootPath(*fullPath);
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "File exists: {0} {1} {2} {3}", FileRootPath,
|
||||
FileOffset, FileSize, fullPath);
|
||||
started =
|
||||
impl->startVideoFile(TCHAR_TO_UTF8(*FileRootPath), FileOffset, FileSize, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// check if the file exists in an archive / encrypted etc.
|
||||
FPakPlatformFile *PakPlatformFile =
|
||||
(FPakPlatformFile *)(FPlatformFileManager::Get().FindPlatformFile(
|
||||
FPakPlatformFile::GetTypeName()));
|
||||
|
||||
TRefCountPtr<FPakFile> PakFile;
|
||||
FPakEntry FileEntry;
|
||||
if (PakPlatformFile != nullptr &&
|
||||
PakPlatformFile->FindFileInPakFiles(*fullPath, &PakFile, &FileEntry))
|
||||
{
|
||||
// we have the file in a pak file
|
||||
// use a custom media data source to read it
|
||||
// n.b. if pak file is uncompressed and the file isn't encrypted we could skip this
|
||||
// step but this should work whatever
|
||||
TSharedRef<FArchive, ESPMode::ThreadSafe> Archive =
|
||||
MakeShareable(IFileManager::Get().CreateFileReader(*fullPath));
|
||||
UnrealArchiveFileSource *fs = new UnrealArchiveFileSource(Archive);
|
||||
started = impl->startVideoCustomSource(fs, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (started)
|
||||
{
|
||||
PlayState = EMediaState::Stopped;
|
||||
EventSink.ReceiveMediaEvent(EMediaEvent::TracksChanged);
|
||||
EventSink.ReceiveMediaEvent(EMediaEvent::MediaOpened);
|
||||
UE_LOGFMT(LogDirectVideo, Verbose, "Opening {0}", *Url);
|
||||
SeekIndex = 0;
|
||||
Seeking = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// couldn't open
|
||||
UE_LOGFMT(LogDirectVideo, Error, "Can't find video file {0}", *Url);
|
||||
}
|
||||
return started;
|
||||
}
|
||||
bool FAndroidVulkanMediaPlayer::Open(const TSharedRef<FArchive, ESPMode::ThreadSafe> &Archive,
|
||||
const FString &OriginalUrl, const IMediaOptions *Options)
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, Error, "Opening archive {0} not supported", *OriginalUrl);
|
||||
return false;
|
||||
}
|
||||
void FAndroidVulkanMediaPlayer::SetGuid(const FGuid &Guid)
|
||||
{
|
||||
PlayerGUID = Guid;
|
||||
}
|
||||
|
||||
void FAndroidVulkanMediaPlayer::TickFetch(FTimespan DeltaTime, FTimespan Timecode)
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "TickFetch");
|
||||
}
|
||||
|
||||
void FAndroidVulkanMediaPlayer::TickInput(FTimespan DeltaTime, FTimespan Timecode)
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "TickInput start");
|
||||
|
||||
/* int64_t presTimeNs = impl->getTimeNS();
|
||||
SampleQueue->SetLastTimeStamp(
|
||||
FMediaTimeStamp(DeltaTime,
|
||||
#if UE_VERSION_OLDER_THAN(5, 3, 0)
|
||||
static_cast<int64>(SeekIndex) << 32));
|
||||
#else
|
||||
FMediaTimeStamp::MakeSequenceIndex(SeekIndex, 0)));
|
||||
#endif
|
||||
*/
|
||||
HasVideoThisFrame = false;
|
||||
VideoSamplePool.Tick();
|
||||
if (!SentBlankFrame && impl->numVideoTracks() == 0)
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "blank frame");
|
||||
SentBlankFrame = true;
|
||||
auto textureSample = VideoSamplePool.AcquireShared();
|
||||
textureSample->InitNoVideo();
|
||||
SampleQueue->AddVideo(textureSample);
|
||||
}
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "TickInput dt:{0} tc {1} num {2}", DeltaTime.GetTicks(),
|
||||
Timecode.GetTicks(), SampleQueue->NumVideoSamples());
|
||||
}
|
||||
|
||||
bool FAndroidVulkanMediaPlayer::GetPlayerFeatureFlag(EFeatureFlag flag) const
|
||||
{
|
||||
|
||||
switch (flag)
|
||||
{
|
||||
case EFeatureFlag::PlayerUsesInternalFlushOnSeek:
|
||||
return true;
|
||||
case EFeatureFlag::AlwaysPullNewestVideoFrame:
|
||||
return true;
|
||||
case EFeatureFlag::UsePlaybackTimingV2:
|
||||
return true;
|
||||
case EFeatureFlag::IsTrackSwitchSeamless:
|
||||
return true;
|
||||
case EFeatureFlag::UseRealtimeWithVideoOnly:
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Feature flag query");
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// IMediaControl
|
||||
// --------------------------------
|
||||
|
||||
bool FAndroidVulkanMediaPlayer::CanControl(EMediaControl Control) const
|
||||
{
|
||||
switch (Control)
|
||||
{
|
||||
case EMediaControl::Pause:
|
||||
return PlayState == EMediaState::Playing;
|
||||
case EMediaControl::Resume:
|
||||
return PlayState == EMediaState::Stopped;
|
||||
case EMediaControl::Seek:
|
||||
return PlayState != EMediaState::Closed;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
FTimespan FAndroidVulkanMediaPlayer::GetDuration() const
|
||||
{
|
||||
int64_t timeNS = impl->getDurationNS();
|
||||
if (timeNS >= 0 && PlayState != EMediaState::Closed)
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Get Duration {0}", int64(timeNS));
|
||||
return FTimespan(timeNS / 100LL);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Get Duration empty");
|
||||
return FTimespan::MaxValue();
|
||||
}
|
||||
}
|
||||
|
||||
float FAndroidVulkanMediaPlayer::GetRate() const
|
||||
{
|
||||
if (PlayState == EMediaState::Playing)
|
||||
{
|
||||
return impl->getRate();
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
EMediaState FAndroidVulkanMediaPlayer::GetState() const
|
||||
{
|
||||
return PlayState;
|
||||
}
|
||||
|
||||
EMediaStatus FAndroidVulkanMediaPlayer::GetStatus() const
|
||||
{
|
||||
// not supported yet
|
||||
return EMediaStatus::None;
|
||||
}
|
||||
|
||||
TRangeSet<float> FAndroidVulkanMediaPlayer::GetSupportedRates(EMediaRateThinning Thinning) const
|
||||
{
|
||||
TRangeSet<float> Retval;
|
||||
if (impl->numAudioTracks() > 0)
|
||||
{
|
||||
Retval.Add(TRange<float>(0.0f));
|
||||
Retval.Add(TRange<float>(1.0f));
|
||||
}
|
||||
else
|
||||
{
|
||||
Retval.Add(TRange<float>(TRange<float>::BoundsType::Inclusive(0.0f),
|
||||
TRange<float>::BoundsType::Inclusive(10.0f)));
|
||||
}
|
||||
return Retval;
|
||||
}
|
||||
|
||||
FTimespan FAndroidVulkanMediaPlayer::GetTime() const
|
||||
{
|
||||
int64_t timeNS = impl->getTimeNS();
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Get Time {0}", int64(timeNS));
|
||||
if (timeNS >= 0)
|
||||
{
|
||||
return FTimespan(timeNS / 100LL);
|
||||
}
|
||||
return FTimespan::Zero();
|
||||
}
|
||||
|
||||
bool FAndroidVulkanMediaPlayer::IsLooping() const
|
||||
{
|
||||
return Looping;
|
||||
}
|
||||
|
||||
bool FAndroidVulkanMediaPlayer::Seek(const FTimespan &Time)
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, Verbose, "Seek index:{0}", SeekIndex);
|
||||
Seeking = true;
|
||||
int64_t ticks = Time.GetTicks();
|
||||
int64_t nanoseconds = ticks * 100LL;
|
||||
impl->seek(nanoseconds);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FAndroidVulkanMediaPlayer::SetLooping(bool Loop)
|
||||
{
|
||||
Looping = Loop;
|
||||
impl->setLooping(Looping);
|
||||
return true;
|
||||
}
|
||||
bool FAndroidVulkanMediaPlayer::SetRate(float Rate)
|
||||
{
|
||||
bool retval = false;
|
||||
int iInitialState = (int)PlayState;
|
||||
switch (PlayState)
|
||||
{
|
||||
case EMediaState::Playing:
|
||||
if (Rate == 0.0)
|
||||
{
|
||||
impl->setPlaying(false);
|
||||
PlayState = EMediaState::Stopped;
|
||||
EventSink.ReceiveMediaEvent(EMediaEvent::PlaybackSuspended);
|
||||
retval = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return impl->setRate(Rate);
|
||||
retval = true;
|
||||
}
|
||||
break;
|
||||
case EMediaState::Stopped:
|
||||
if (Rate == 0.0)
|
||||
{
|
||||
retval = true;
|
||||
}
|
||||
else if (Rate > 0.0)
|
||||
{
|
||||
impl->setPlaying(true);
|
||||
PlayState = EMediaState::Playing;
|
||||
retval = impl->setRate(Rate);
|
||||
if (retval)
|
||||
{
|
||||
EventSink.ReceiveMediaEvent(EMediaEvent::PlaybackResumed);
|
||||
}
|
||||
}
|
||||
break;
|
||||
};
|
||||
int iFinalState = (int)PlayState;
|
||||
UE_LOGFMT(LogDirectVideo, Verbose, "Set rate {0} {1} state:{2}->{3}", Rate, retval,
|
||||
iInitialState, iFinalState);
|
||||
// return false;
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool FAndroidVulkanMediaPlayer::SetNativeVolume(float Volume)
|
||||
{
|
||||
return AudioOut->setVolume(Volume);
|
||||
}
|
||||
|
||||
// IMediaTracks
|
||||
// --------------------------------
|
||||
bool FAndroidVulkanMediaPlayer::GetAudioTrackFormat(int32 TrackIndex, int32 FormatIndex,
|
||||
FMediaAudioTrackFormat &OutFormat) const
|
||||
{
|
||||
if (FormatIndex != 0 || PlayState == EMediaState::Closed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int32 bitsPerSample;
|
||||
int32 channels;
|
||||
int32 rate;
|
||||
if (!impl->getAudioTrackFormat(TrackIndex, &bitsPerSample, &channels, &rate))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
OutFormat.BitsPerSample = bitsPerSample;
|
||||
OutFormat.NumChannels = channels;
|
||||
OutFormat.SampleRate = rate;
|
||||
OutFormat.TypeName = TEXT("Native");
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Get audio trackformat {0} {1} {2} {3}", TrackIndex,
|
||||
bitsPerSample, channels, rate);
|
||||
return true;
|
||||
}
|
||||
int32 FAndroidVulkanMediaPlayer::GetNumTracks(EMediaTrackType TrackType) const
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Get num tracks type: {0}", int(TrackType));
|
||||
// TODO: support audio / video only
|
||||
switch (TrackType)
|
||||
{
|
||||
case EMediaTrackType::Audio:
|
||||
return impl->numAudioTracks();
|
||||
case EMediaTrackType::Video:
|
||||
return impl->numVideoTracks();
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int32 FAndroidVulkanMediaPlayer::GetNumTrackFormats(EMediaTrackType TrackType,
|
||||
int32 TrackIndex) const
|
||||
{
|
||||
if (TrackIndex >= GetNumTracks(TrackType))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
switch (TrackType)
|
||||
{
|
||||
case EMediaTrackType::Audio:
|
||||
return 1;
|
||||
case EMediaTrackType::Video:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int32 FAndroidVulkanMediaPlayer::GetSelectedTrack(EMediaTrackType TrackType) const
|
||||
{
|
||||
if (TrackType == EMediaTrackType::Audio)
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Returning no selected audio track");
|
||||
return INDEX_NONE;
|
||||
}
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Returning selected track type {0}", int(TrackType));
|
||||
return 0;
|
||||
}
|
||||
|
||||
FText FAndroidVulkanMediaPlayer::GetTrackDisplayName(EMediaTrackType TrackType,
|
||||
int32 TrackIndex) const
|
||||
{
|
||||
// TODO: pass through display names
|
||||
|
||||
return FText::Format(LOCTEXT("TrackName", "Track {0} type {1}"), (int)TrackType, TrackIndex);
|
||||
}
|
||||
|
||||
int32 FAndroidVulkanMediaPlayer::GetTrackFormat(EMediaTrackType TrackType, int32 TrackIndex) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
FString FAndroidVulkanMediaPlayer::GetTrackLanguage(EMediaTrackType TrackType,
|
||||
int32 TrackIndex) const
|
||||
{
|
||||
// TODO - pass language through
|
||||
return TEXT("");
|
||||
}
|
||||
|
||||
FString FAndroidVulkanMediaPlayer::GetTrackName(EMediaTrackType TrackType, int32 TrackIndex) const
|
||||
{
|
||||
if (TrackIndex == 0 && PlayState != EMediaState::Closed)
|
||||
{
|
||||
return TEXT("TRACK");
|
||||
}
|
||||
else
|
||||
{
|
||||
return TEXT("");
|
||||
}
|
||||
}
|
||||
|
||||
bool FAndroidVulkanMediaPlayer::GetVideoTrackFormat(int32 TrackIndex, int32 FormatIndex,
|
||||
FMediaVideoTrackFormat &OutFormat) const
|
||||
{
|
||||
if (FormatIndex != 0 || PlayState == EMediaState::Closed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int w = 0, h = 0;
|
||||
float frameRate = 0;
|
||||
if (!impl->getVideoTrackFormat(TrackIndex, &w, &h, &frameRate))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
OutFormat.Dim.X = w;
|
||||
OutFormat.Dim.Y = h;
|
||||
OutFormat.FrameRate = frameRate;
|
||||
OutFormat.FrameRates = TRange<float>(frameRate);
|
||||
OutFormat.TypeName = TEXT("Vulkan Video Frame");
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Got out video format");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FAndroidVulkanMediaPlayer::SelectTrack(EMediaTrackType TrackType, int32 TrackIndex)
|
||||
{
|
||||
if (TrackType == EMediaTrackType::Audio)
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Not selecting audio track");
|
||||
return false;
|
||||
}
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Selecting track type {0} = {0}", int(TrackType),
|
||||
TrackIndex);
|
||||
return true;
|
||||
// return TrackIndex == 0 && PlayState != EMediaState::Closed;
|
||||
}
|
||||
|
||||
bool FAndroidVulkanMediaPlayer::SetTrackFormat(EMediaTrackType TrackType, int32 TrackIndex,
|
||||
int32 FormatIndex)
|
||||
{
|
||||
// todo: support multiple formats / track
|
||||
return TrackIndex == 0 && FormatIndex == 0 && PlayState != EMediaState::Closed;
|
||||
}
|
||||
|
||||
IMediaSamples &FAndroidVulkanMediaPlayer::GetSamples()
|
||||
{
|
||||
return *SampleQueue.Get();
|
||||
}
|
||||
|
||||
void FAndroidVulkanMediaPlayer::onVideoFrame(void *frameHwBuffer, int w, int h, int64_t presTimeNs)
|
||||
{
|
||||
if (PlayState == EMediaState::Closed)
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Releasing frame as we are closed");
|
||||
impl->releaseFrame(frameHwBuffer);
|
||||
return;
|
||||
}
|
||||
if (HasVideoThisFrame)
|
||||
{
|
||||
impl->releaseFrame(frameHwBuffer);
|
||||
return;
|
||||
}
|
||||
HasVideoThisFrame = true;
|
||||
auto textureSample = VideoSamplePool.AcquireShared();
|
||||
textureSample->Init(impl, frameHwBuffer, w, h,
|
||||
FMediaTimeStamp(FTimespan(presTimeNs / 100LL),
|
||||
|
||||
FMediaTimeStamp::MakeSequenceIndex(SeekIndex, 0)));
|
||||
|
||||
SampleQueue->AddVideo(textureSample);
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Add video sample");
|
||||
// UE_LOGFMT(LogDirectVideo, VeryVerbose, "Video samples {0}",
|
||||
// SampleQueue->NumVideoSamples());
|
||||
// NB: this needs to happen after the sample is in the queue, so that
|
||||
// the queue has something in when media player facade samples it for playback
|
||||
// time
|
||||
if (Seeking)
|
||||
{
|
||||
SeekIndex += 1;
|
||||
UE_LOGFMT(LogDirectVideo, Verbose, "Seek completed {0}", SeekIndex);
|
||||
Seeking = false;
|
||||
EventSink.ReceiveMediaEvent(EMediaEvent::SeekCompleted);
|
||||
}
|
||||
}
|
||||
|
||||
void FAndroidVulkanMediaPlayer::OnEnterBackground()
|
||||
{
|
||||
// going into backgroud - if playing, pause implementation player
|
||||
if (PlayState == EMediaState::Playing)
|
||||
{
|
||||
impl->setPlaying(false);
|
||||
EventSink.ReceiveMediaEvent(EMediaEvent::PlaybackSuspended);
|
||||
UE_LOGFMT(LogDirectVideo, Verbose, "Enter suspend");
|
||||
}
|
||||
}
|
||||
void FAndroidVulkanMediaPlayer::OnEnterForeground()
|
||||
{
|
||||
// back into foreground - if playing, start
|
||||
if (PlayState == EMediaState::Playing)
|
||||
{
|
||||
impl->setPlaying(true);
|
||||
EventSink.ReceiveMediaEvent(EMediaEvent::PlaybackResumed);
|
||||
UE_LOGFMT(LogDirectVideo, Verbose, "Enter foreground");
|
||||
}
|
||||
}
|
||||
|
||||
void FAndroidVulkanMediaPlayer::ProcessVideoSamples()
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Process video samples");
|
||||
}
|
||||
|
||||
void *FAndroidVulkanMediaPlayer::getVkDeviceProcAddr(const char *name)
|
||||
{
|
||||
void *result = static_cast<void *>(
|
||||
(static_cast<struct IVulkanDynamicRHI *>(GDynamicRHI))->RHIGetVkDeviceProcAddr(name));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
VkDevice FAndroidVulkanMediaPlayer::getVkDevice()
|
||||
{
|
||||
IVulkanDynamicRHI *rhi = static_cast<struct IVulkanDynamicRHI *>(GDynamicRHI);
|
||||
if (rhi == NULL)
|
||||
{
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
return rhi->RHIGetVkDevice();
|
||||
}
|
||||
|
||||
const VkAllocationCallbacks *FAndroidVulkanMediaPlayer::getVkAllocationCallbacks()
|
||||
{
|
||||
IVulkanDynamicRHI *rhi = static_cast<struct IVulkanDynamicRHI *>(GDynamicRHI);
|
||||
if (rhi == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return rhi->RHIGetVkAllocationCallbacks();
|
||||
}
|
||||
|
||||
VkPhysicalDevice FAndroidVulkanMediaPlayer::getNativePhysicalDevice()
|
||||
{
|
||||
return static_cast<VkPhysicalDevice>(GDynamicRHI->RHIGetNativePhysicalDevice());
|
||||
}
|
||||
|
||||
void FAndroidVulkanMediaPlayer::onPlaybackEnd(bool looping)
|
||||
{
|
||||
EventSink.ReceiveMediaEvent(EMediaEvent::PlaybackEndReached);
|
||||
if (!looping)
|
||||
{
|
||||
PlayState = EMediaState::Stopped;
|
||||
EventSink.ReceiveMediaEvent(EMediaEvent::PlaybackSuspended);
|
||||
}
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
@ -0,0 +1,399 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// Vulkan texture sample implementation.
|
||||
//
|
||||
// Actual texture handling is handled in
|
||||
// FRenderOnRHIThreadCommand::Execute via calls to
|
||||
// IVulkanImpl
|
||||
// ------------------------------------------------
|
||||
|
||||
#include "AndroidVulkanTextureSample.h"
|
||||
|
||||
#include "UnrealLogging.h"
|
||||
|
||||
#include "Android/AndroidApplication.h"
|
||||
#include "IVulkanDynamicRHI.h"
|
||||
|
||||
#include "IVulkanImpl.h"
|
||||
#include "SceneUtils.h"
|
||||
|
||||
#include "RenderingThread.h"
|
||||
|
||||
void AndroidVulkanTextureSample::FRenderOnRHIThreadCommand(const FTexture2DRHIRef &DstTexture)
|
||||
{
|
||||
FScopeLock Lock(&AccessLock);
|
||||
if (Impl == NULL)
|
||||
return;
|
||||
IVulkanDynamicRHI *RHI = static_cast<struct IVulkanDynamicRHI *>(GDynamicRHI);
|
||||
FVulkanRHIImageViewInfo TexInfo = RHI->RHIGetImageViewInfo(DstTexture.GetReference());
|
||||
VkCommandBuffer cmdBuffer = RHI->RHIGetActiveVkCommandBuffer();
|
||||
// 2) impl class
|
||||
// 3) VkImageView etc. from InDstTexture
|
||||
int w = TexInfo.Width;
|
||||
int h = TexInfo.Height;
|
||||
int Samples = DstTexture->GetDesc().NumSamples;
|
||||
Impl->renderFrameToImageView(cmdBuffer, FrameHWImage, TexInfo.ImageView, TexInfo.Image,
|
||||
TexInfo.Format, w, h, Samples);
|
||||
}
|
||||
|
||||
void AndroidVulkanTextureSample::FRenderMeshOnRHIThreadCommand(const FTexture2DRHIRef &DstTexture,
|
||||
void *MeshID, int Samples)
|
||||
{
|
||||
FScopeLock Lock(&AccessLock);
|
||||
if (Impl == NULL)
|
||||
return;
|
||||
IVulkanDynamicRHI *RHI = static_cast<struct IVulkanDynamicRHI *>(GDynamicRHI);
|
||||
FVulkanRHIImageViewInfo TexInfo = RHI->RHIGetImageViewInfo(DstTexture.GetReference());
|
||||
VkCommandBuffer cmdBuffer = RHI->RHIGetActiveVkCommandBuffer();
|
||||
int w = TexInfo.Width;
|
||||
int h = TexInfo.Height;
|
||||
int DrawViews = DstTexture->GetDesc().ArraySize;
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Render in existing pass: Samples: {0} views: {1}",
|
||||
Samples, DrawViews);
|
||||
// render to upside down viewport for consistency with unreal rendering
|
||||
Impl->renderFrameToMeshInExistingPass(cmdBuffer, FrameHWImage, TexInfo.Image, 0, h, w, -h,
|
||||
Samples, DrawViews, MeshID);
|
||||
}
|
||||
|
||||
void AndroidVulkanTextureSample::FUpdateMeshOnRHIThreadCommand(
|
||||
const FTexture2DRHIRef &DstTexture, const TArray<VertexData> &PositionAndUV,
|
||||
const TArray<uint32> &Indices, const ShaderModelMatrix &Matrix, void *MeshID)
|
||||
{
|
||||
FScopeLock Lock(&AccessLock);
|
||||
if (Impl == NULL)
|
||||
return;
|
||||
IVulkanDynamicRHI *RHI = static_cast<struct IVulkanDynamicRHI *>(GDynamicRHI);
|
||||
VkCommandBuffer cmdBuffer = RHI->RHIGetActiveVkCommandBuffer();
|
||||
Impl->loadMesh(cmdBuffer, PositionAndUV.GetData(), PositionAndUV.Num(), Indices.GetData(),
|
||||
Indices.Num(), MeshID);
|
||||
Impl->updateModelMatrix(cmdBuffer, Matrix, MeshID);
|
||||
}
|
||||
|
||||
void AndroidVulkanTextureSample::FUpdateMatricesOnRHIThreadCommand(
|
||||
const FTexture2DRHIRef &DstTexture, const ShaderViewMatrices &Matrices, void *MeshID)
|
||||
{
|
||||
FScopeLock Lock(&AccessLock);
|
||||
if (Impl == NULL)
|
||||
return;
|
||||
IVulkanDynamicRHI *RHI = static_cast<struct IVulkanDynamicRHI *>(GDynamicRHI);
|
||||
FVulkanRHIImageViewInfo TexInfo = RHI->RHIGetImageViewInfo(DstTexture.GetReference());
|
||||
VkCommandBuffer cmdBuffer = RHI->RHIGetActiveVkCommandBuffer();
|
||||
|
||||
Impl->updateViewMatrices(cmdBuffer, Matrices, TexInfo.Image);
|
||||
}
|
||||
|
||||
void AndroidVulkanTextureSample::FInitMeshRenderingCommand(const FTexture2DRHIRef &DstTexture,
|
||||
void *MeshID, int Samples)
|
||||
{
|
||||
FScopeLock Lock(&AccessLock);
|
||||
IVulkanDynamicRHI *RHI = static_cast<struct IVulkanDynamicRHI *>(GDynamicRHI);
|
||||
FVulkanRHIImageViewInfo TexInfo = RHI->RHIGetImageViewInfo(DstTexture.GetReference());
|
||||
VkCommandBuffer cmdBuffer = RHI->RHIGetActiveVkCommandBuffer();
|
||||
int w = TexInfo.Width;
|
||||
int h = TexInfo.Height;
|
||||
int DrawViews = DstTexture->GetDesc().ArraySize;
|
||||
Impl->initializeFrameObjectsOutsidePass(cmdBuffer, FrameHWImage, TexInfo.Image, w, h, MeshID,
|
||||
Samples, DrawViews);
|
||||
}
|
||||
|
||||
AndroidVulkanTextureSample::AndroidVulkanTextureSample()
|
||||
{
|
||||
Impl = NULL;
|
||||
FrameHWImage = NULL;
|
||||
NumSamples = 1;
|
||||
|
||||
static const auto NumSamplesVar =
|
||||
IConsoleManager::Get().FindConsoleVariable(TEXT("r.MSAACount"));
|
||||
static const auto AAType =
|
||||
IConsoleManager::Get().FindConsoleVariable(TEXT("r.Mobile.AntiAliasing"));
|
||||
|
||||
if (AAType != NULL && NumSamplesVar != NULL)
|
||||
{
|
||||
EAntiAliasingMethod MobileAntiAliasing = EAntiAliasingMethod(AAType->GetInt());
|
||||
if (MobileAntiAliasing == EAntiAliasingMethod::AAM_MSAA)
|
||||
{
|
||||
NumSamples = NumSamplesVar->GetInt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidVulkanTextureSample::InitNoVideo()
|
||||
{
|
||||
FScopeLock Lock(&AccessLock);
|
||||
Clear();
|
||||
this->Dimension = FIntPoint(2, 2);
|
||||
this->FrameHWImage = NULL;
|
||||
this->SampleTime = FMediaTimeStamp(FTimespan::Zero());
|
||||
IsEmptyTexture = true;
|
||||
}
|
||||
|
||||
void AndroidVulkanTextureSample::Init(IVulkanImpl *impl, void *hwImage, int w, int h,
|
||||
FMediaTimeStamp sampleTime)
|
||||
{
|
||||
FScopeLock Lock(&AccessLock);
|
||||
Clear();
|
||||
this->Impl = impl;
|
||||
this->Dimension = FIntPoint(w, h);
|
||||
this->FrameHWImage = hwImage;
|
||||
this->SampleTime = sampleTime;
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Create texture sample {0} {1}x{2} {3} ", this,
|
||||
Dimension.X, Dimension.Y, sampleTime.Time);
|
||||
IsEmptyTexture = false;
|
||||
}
|
||||
|
||||
AndroidVulkanTextureSample::~AndroidVulkanTextureSample()
|
||||
{
|
||||
}
|
||||
|
||||
FIntPoint AndroidVulkanTextureSample::GetDim() const
|
||||
{
|
||||
return Dimension;
|
||||
}
|
||||
|
||||
FIntPoint AndroidVulkanTextureSample::GetOutputDim() const
|
||||
{
|
||||
return Dimension;
|
||||
}
|
||||
uint32 AndroidVulkanTextureSample::GetStride() const
|
||||
{
|
||||
return Dimension.X;
|
||||
}
|
||||
FRHITexture *AndroidVulkanTextureSample::GetTexture() const
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
IMediaTextureSampleConverter *AndroidVulkanTextureSample::GetMediaTextureSampleConverter()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
bool AndroidVulkanTextureSample::Convert(FTexture2DRHIRef &InDstTexture,
|
||||
const FConversionHints &Hints)
|
||||
{
|
||||
FRHICommandListImmediate &RHICmdList = FRHICommandListExecutor::GetImmediateCommandList();
|
||||
return ConvertInternal(RHICmdList, InDstTexture, Hints);
|
||||
}
|
||||
|
||||
bool AndroidVulkanTextureSample::ConvertInternal(FRHICommandListImmediate &RHICmdList,
|
||||
FTexture2DRHIRef &InDstTexture,
|
||||
const FConversionHints &Hints)
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "ConvertInternal");
|
||||
FScopeLock Lock(&AccessLock);
|
||||
if (IsEmptyTexture)
|
||||
{
|
||||
// just let texture clear itself to clear colour
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Getting clear texture");
|
||||
Shown = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Impl == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "doing texture conversion");
|
||||
|
||||
FTexture2DRHIRef CopyInDstTexture = InDstTexture;
|
||||
RHICmdList.EnqueueLambda(TEXT("ConvertTexture"),
|
||||
([this, CopyInDstTexture](FRHICommandList &RHICommandList) {
|
||||
this->FRenderOnRHIThreadCommand(CopyInDstTexture);
|
||||
}));
|
||||
|
||||
if (!Fence.IsValid())
|
||||
{
|
||||
Fence = RHICreateGPUFence(TEXT("VVRFrameFence"));
|
||||
}
|
||||
Fence->Clear();
|
||||
RHICmdList.WriteGPUFence(Fence);
|
||||
Shown = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
bool AndroidVulkanTextureSample::InitFrameForMeshRendering(FTexture2DRHIRef InDstTexture,
|
||||
FRHICommandList &RHICmdList,
|
||||
void *MeshID)
|
||||
{
|
||||
FScopeLock Lock(&AccessLock);
|
||||
if (Impl == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (IsEmptyTexture)
|
||||
{
|
||||
// TODO: no frame, do nothing?
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "init frame for rendering");
|
||||
RHICmdList.EnqueueLambda(
|
||||
TEXT("InitFrame"), ([this, InDstTexture, MeshID](FRHICommandList &RHICommandList) {
|
||||
this->FInitMeshRenderingCommand(InDstTexture, MeshID, NumSamples);
|
||||
}));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool AndroidVulkanTextureSample::UpdateMesh(FTexture2DRHIRef InDstTexture,
|
||||
FRHICommandList &RHICmdList,
|
||||
TArray<VertexData> PositionAndUV,
|
||||
TArray<uint32> Indices, ShaderModelMatrix Matrix,
|
||||
void *MeshID)
|
||||
{
|
||||
if (Impl != NULL && !Impl->hasMesh(MeshID))
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "setting texture mesh");
|
||||
|
||||
RHICmdList.EnqueueLambda(TEXT("SetMesh"), ([this, InDstTexture, PositionAndUV, Indices,
|
||||
Matrix, MeshID](FRHICommandList &RHICmdList) {
|
||||
this->FUpdateMeshOnRHIThreadCommand(
|
||||
InDstTexture, PositionAndUV, Indices, Matrix, MeshID);
|
||||
}));
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AndroidVulkanTextureSample::UpdateViewMatrices(FTexture2DRHIRef InDstTexture,
|
||||
FRHICommandList &RHICmdList,
|
||||
ShaderViewMatrices Matrices, void *MeshID)
|
||||
{
|
||||
// in impl, we need to
|
||||
// a) load data to mesh uniform buffers if not already loaded
|
||||
// then b) render to that mesh
|
||||
FScopeLock Lock(&AccessLock);
|
||||
if (Impl == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "updating matrices");
|
||||
RHICmdList.EnqueueLambda(TEXT("UpdateMatrices"),
|
||||
([this, InDstTexture, Matrices, MeshID](FRHICommandList &RHICmdList) {
|
||||
this->FUpdateMatricesOnRHIThreadCommand(InDstTexture, Matrices,
|
||||
MeshID);
|
||||
}));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AndroidVulkanTextureSample::RenderToMesh(FTexture2DRHIRef InDstTexture,
|
||||
FRHICommandList &RHICmdList, void *MeshID)
|
||||
{
|
||||
// in impl, we need to
|
||||
// a) load data to mesh uniform buffers if not already loaded
|
||||
// then b) render to that mesh
|
||||
FScopeLock Lock(&AccessLock);
|
||||
if (IsEmptyTexture)
|
||||
{
|
||||
// need to render to a clear colour...
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Getting clear texture");
|
||||
Shown = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Impl == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "writing texture mesh Immediate:{0},{1},{2}",
|
||||
RHICmdList.IsImmediate(), RHICmdList.Bypass(), RHICmdList.IsExecuting());
|
||||
|
||||
RHICmdList.EnqueueLambda(
|
||||
TEXT("RenderMesh"), ([this, InDstTexture, MeshID](FRHICommandList &RHICmdList) {
|
||||
this->FRenderMeshOnRHIThreadCommand(InDstTexture, MeshID, NumSamples);
|
||||
}));
|
||||
|
||||
if (!Fence.IsValid())
|
||||
{
|
||||
Fence = RHICreateGPUFence(TEXT("VVRFrameFence"));
|
||||
}
|
||||
Fence->Clear();
|
||||
RHICmdList.WriteGPUFence(Fence);
|
||||
Shown = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
FMediaTimeStamp AndroidVulkanTextureSample::GetTime() const
|
||||
{
|
||||
return SampleTime;
|
||||
}
|
||||
|
||||
void AndroidVulkanTextureSample::InitializePoolable()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
void AndroidVulkanTextureSample::ClearIfShown()
|
||||
{
|
||||
if (Shown && IsReadyForReuse())
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
}
|
||||
|
||||
bool AndroidVulkanTextureSample::IsReadyForReuse()
|
||||
{
|
||||
FScopeLock Lock(&AccessLock);
|
||||
if (this->FrameHWImage != NULL && Fence.IsValid() && !Fence->Poll())
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Fence not set yet");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void AndroidVulkanTextureSample::Clear()
|
||||
{
|
||||
FScopeLock Lock(&AccessLock);
|
||||
if (this->Impl != NULL && this->FrameHWImage != NULL)
|
||||
{
|
||||
UE_LOG(LogDirectVideo, VeryVerbose, TEXT("Release texture sample %x"), this->FrameHWImage);
|
||||
this->Impl->releaseFrame(this->FrameHWImage);
|
||||
}
|
||||
this->FrameHWImage = NULL;
|
||||
this->Impl = NULL;
|
||||
this->Shown = false;
|
||||
}
|
||||
|
||||
void AndroidVulkanTextureSample::ShutdownPoolable()
|
||||
{
|
||||
bool needsWait = false;
|
||||
{
|
||||
FScopeLock Lock(&AccessLock);
|
||||
if (this->FrameHWImage != NULL && Fence.IsValid() && !Fence->Poll())
|
||||
{
|
||||
needsWait = true;
|
||||
}
|
||||
}
|
||||
if (needsWait)
|
||||
{
|
||||
UE_LOG(LogDirectVideo, VeryVerbose, TEXT("Shutdown poolable %x"), this->FrameHWImage);
|
||||
if (IsInGameThread())
|
||||
{
|
||||
FlushRenderingCommands();
|
||||
}
|
||||
}
|
||||
Clear();
|
||||
}
|
||||
|
||||
void AndroidVulkanTextureSample::ImplDeleted()
|
||||
{
|
||||
FScopeLock Lock(&AccessLock);
|
||||
this->Impl = NULL;
|
||||
}
|
||||
|
||||
EMediaTextureSampleFormat AndroidVulkanTextureSample::VideoTextureFormat =
|
||||
EMediaTextureSampleFormat::CharBGR10A2;
|
@ -0,0 +1,71 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// Vulkan video player module. Handles starting up
|
||||
// the correct vulkan extensions, and enabling
|
||||
// the override layer which we use to enable
|
||||
// SamplerYCbCrConversion feature properly.
|
||||
// ------------------------------------------------
|
||||
|
||||
#include "AndroidVulkanVideo.h"
|
||||
// #include "Core.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
// #include "Interfaces/IPluginManager.h"
|
||||
#if PLATFORM_ANDROID
|
||||
#include "AndroidVulkanMediaPlayer.h"
|
||||
#include "IVulkanDynamicRHI.h"
|
||||
#endif
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FAndroidVulkanVideoModule"
|
||||
|
||||
class FAndroidVulkanVideoModule : public IAndroidVulkanVideoModule
|
||||
{
|
||||
|
||||
void StartupModule()
|
||||
{
|
||||
// This code will execute after your module is loaded into memory; the exact timing is
|
||||
// specified in the .uplugin file per-module
|
||||
|
||||
// Get the base directory of this plugin
|
||||
// FString BaseDir = IPluginManager::Get().FindPlugin("AndroidVulkanVideo")->GetBaseDir();
|
||||
//
|
||||
#if PLATFORM_ANDROID
|
||||
// setup vulkan extensions that we need
|
||||
const TArray<const ANSICHAR *> DeviceExtensions = {
|
||||
"VK_KHR_external_memory",
|
||||
"VK_ANDROID_external_memory_android_hardware_buffer",
|
||||
"VK_KHR_sampler_ycbcr_conversion",
|
||||
"VK_KHR_bind_memory2",
|
||||
"VK_EXT_queue_family_foreign",
|
||||
"VK_KHR_dedicated_allocation",
|
||||
"VK_KHR_get_physical_device_properties2"};
|
||||
const TArray<const ANSICHAR *> DeviceLayers = {};
|
||||
|
||||
const TArray<const ANSICHAR *> InstanceExtensions = {
|
||||
VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME};
|
||||
const TArray<const ANSICHAR *> InstanceLayers = {"VkLayer_OverrideLib"};
|
||||
|
||||
IVulkanDynamicRHI::AddEnabledDeviceExtensionsAndLayers(DeviceExtensions, DeviceLayers);
|
||||
IVulkanDynamicRHI::AddEnabledInstanceExtensionsAndLayers(InstanceExtensions,
|
||||
InstanceLayers);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ShutdownModule()
|
||||
{
|
||||
// This function may be called during shutdown to clean up your module. For modules that
|
||||
// support dynamic reloading, we call this function before unloading the module.
|
||||
|
||||
// Free the dll handle
|
||||
}
|
||||
|
||||
TSharedPtr<IMediaPlayer, ESPMode::ThreadSafe> CreatePlayer(IMediaEventSink &EventSink)
|
||||
{
|
||||
return TSharedPtr<IMediaPlayer, ESPMode::ThreadSafe>(
|
||||
new FAndroidVulkanMediaPlayer(EventSink));
|
||||
}
|
||||
};
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FAndroidVulkanVideoModule, AndroidVulkanVideo)
|
@ -0,0 +1,38 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// interface for audio output classes. This is used
|
||||
// so that AndroidVulkanVideoImpl doesn't need to
|
||||
// know about Unreal, so I can keep Unreal and
|
||||
// Android code separate.
|
||||
// ------------------------------------------------
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
class IAudioOut
|
||||
{
|
||||
public:
|
||||
virtual ~IAudioOut() {};
|
||||
virtual void init(int sampleRate, int channels) = 0;
|
||||
virtual void initSilent() = 0;
|
||||
virtual void close() = 0;
|
||||
virtual void sendBuffer(uint8_t *buf, int bufSize, int64_t presentationTimeNS,
|
||||
bool reset = false) = 0;
|
||||
virtual int64_t getPresentationTimeNS() = 0; // current presentation time
|
||||
virtual bool setVolume(float level) = 0;
|
||||
virtual void setPlaying(bool playing) = 0;
|
||||
virtual bool setRate(float rate) = 0;
|
||||
|
||||
virtual int64_t getQueuedTimeNS() = 0;
|
||||
|
||||
virtual void onSeek(int64_t newPresentationTimeNS, bool resetAudio) = 0;
|
||||
virtual void onHasVideoTime(int64_t newPresentationTimeNS) = 0;
|
||||
|
||||
typedef std::chrono::nanoseconds NsDuration;
|
||||
typedef std::chrono::time_point<std::chrono::steady_clock, NsDuration> NsTime;
|
||||
|
||||
virtual NsTime getWaitTimeForPresentationTime(int64_t presentationTimeNS,
|
||||
int64_t maxDuration) = 0;
|
||||
};
|
@ -0,0 +1,19 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// A custom file source - used for reading encrypted
|
||||
// or compressed video files.
|
||||
// ------------------------------------------------
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class ICustomMediaFileSource
|
||||
{
|
||||
public:
|
||||
virtual ~ICustomMediaFileSource() {};
|
||||
virtual int64_t getAvailableSize(uint64_t offset) = 0;
|
||||
virtual int64_t getSize() = 0;
|
||||
virtual int64_t readAt(uint64_t offset, void *buffer, uint64_t size) = 0;
|
||||
};
|
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include <cinttypes>
|
||||
|
||||
class ILogger
|
||||
{
|
||||
public:
|
||||
virtual void LogWithType(int64_t type, const char *fmt, ...) = 0;
|
||||
|
||||
enum LogTypes
|
||||
{
|
||||
ERROR = 1 << 0,
|
||||
TEXTURE_IMPL = 1 << 1,
|
||||
TEXTURE_IMPL_VV = 1 << 2,
|
||||
DECODER = 1 << 3,
|
||||
DECODER_VV = 1 << 4,
|
||||
VULKAN_DYNAMIC = 1 << 5,
|
||||
VULKAN_DYNAMIC_VV = 1 << 6,
|
||||
LOGTYPE_MAX = 1 << 7,
|
||||
ALL_LOGS_BITMASK = 0xff
|
||||
};
|
||||
};
|
@ -0,0 +1,82 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// Interface used by Android specific implementation class
|
||||
// This does all handling of Android hardware
|
||||
// buffers, Vulkan texture images etc.
|
||||
//
|
||||
// It lives in libVkLayer_OverrideLib.so
|
||||
// along with a vulkan override layer to handle
|
||||
// initialisation of the Vulkan device.
|
||||
//
|
||||
// ------------------------------------------------
|
||||
#pragma once
|
||||
|
||||
#include "IAndroidVulkanVideoAVCallback.h"
|
||||
#include "IAudioOut.h"
|
||||
#include "ICustomMediaFileSource.h"
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
|
||||
class ILogger;
|
||||
#include "IVulkanVertexData.h"
|
||||
|
||||
class IVulkanImpl
|
||||
{
|
||||
public:
|
||||
virtual bool initializeFrameObjectsOutsidePass(VkCommandBuffer cmdBuffer, void *frameImage,
|
||||
VkImage outImage, int w, int h, void *meshID,
|
||||
int samples, int drawViews) = 0;
|
||||
virtual bool renderFrameToMeshInExistingPass(VkCommandBuffer cmdBuffer, void *frameImage,
|
||||
VkImage outImage, int x, int y, int w, int h,
|
||||
int samples, int drawViews, void *meshID) = 0;
|
||||
virtual bool updateViewMatrices(VkCommandBuffer cmdBuffer, const ShaderViewMatrices &matrices,
|
||||
VkImage targetImage) = 0;
|
||||
virtual bool updateModelMatrix(VkCommandBuffer cmdBuffer, const ShaderModelMatrix &matrix,
|
||||
void *meshID) = 0;
|
||||
virtual bool loadMesh(VkCommandBuffer cmdBuffer, const VertexData *positionAndUV,
|
||||
int vertexCount, const uint32_t *indices, int indexCount,
|
||||
void *meshID) = 0;
|
||||
virtual bool hasMesh(void *meshID) const = 0;
|
||||
virtual bool unloadMesh(void *meshID) = 0;
|
||||
|
||||
virtual bool renderFrameToImageView(VkCommandBuffer cmdBuffer, void *frameImage,
|
||||
VkImageView view, VkImage image, VkFormat format, int w,
|
||||
int h, int samples) = 0;
|
||||
virtual void releaseFrame(void *frameHwBuffer) = 0;
|
||||
|
||||
virtual void setDataCallback(IAndroidVulkanVideoAVCallback *cb) = 0;
|
||||
virtual void setLogger(ILogger *logger) = 0;
|
||||
|
||||
virtual bool startVideoCustomSource(ICustomMediaFileSource *source, bool autoplay) = 0;
|
||||
virtual bool startVideoURL(char *videoURL, bool autoplay) = 0;
|
||||
virtual bool startVideoFile(char *fname, int64_t offset, int64_t length, bool autoplay) = 0;
|
||||
virtual bool hasVideo(int *width, int *height) = 0;
|
||||
|
||||
virtual bool getVideoTrackFormat(int idx, int *width, int *height, float *fps) = 0;
|
||||
virtual bool getAudioTrackFormat(int idx, int *bitsPerSample, int *channels, int *rate) = 0;
|
||||
|
||||
// set audio out handler - typically has to be done once per
|
||||
// run, as it is re-initialized per video
|
||||
virtual void setAudioOut(IAudioOut *audioOut) = 0;
|
||||
|
||||
// set the output texture
|
||||
virtual void setOutputImageView(VkImageView view, VkImage image, VkFormat format, int w, int h,
|
||||
int samples) = 0;
|
||||
|
||||
virtual void close() = 0;
|
||||
|
||||
virtual void setPlaying(bool playing) = 0;
|
||||
virtual bool getPlaying() const = 0;
|
||||
virtual void seek(int64_t nsTimestamp) = 0;
|
||||
virtual bool setRate(float rate) = 0;
|
||||
virtual float getRate() const = 0;
|
||||
virtual int64_t getTimeNS() const = 0;
|
||||
virtual int64_t getDurationNS() const = 0;
|
||||
virtual int numAudioTracks() const = 0;
|
||||
virtual int numVideoTracks() const = 0;
|
||||
|
||||
virtual void setLooping(bool looping) = 0;
|
||||
virtual bool getLooping() const = 0;
|
||||
};
|
@ -0,0 +1,46 @@
|
||||
#include "UnrealArchiveFileSource.h"
|
||||
|
||||
UnrealArchiveFileSource::UnrealArchiveFileSource(
|
||||
const TSharedRef<FArchive, ESPMode::ThreadSafe> inArchive)
|
||||
: archive(inArchive)
|
||||
{
|
||||
}
|
||||
|
||||
int64_t UnrealArchiveFileSource::getAvailableSize(uint64_t offset)
|
||||
{
|
||||
FScopeLock lockCS(&threadLock);
|
||||
return getSize() - offset;
|
||||
}
|
||||
|
||||
int64_t UnrealArchiveFileSource::getSize()
|
||||
{
|
||||
FScopeLock lockCS(&threadLock);
|
||||
return archive->TotalSize();
|
||||
}
|
||||
|
||||
int64_t UnrealArchiveFileSource::readAt(uint64_t position, void *buffer, uint64_t readSize)
|
||||
{
|
||||
FScopeLock lockCS(&threadLock);
|
||||
int64_t fileSize = archive->TotalSize();
|
||||
if (fileSize < position)
|
||||
{
|
||||
return -1; // EOF
|
||||
}
|
||||
|
||||
int64_t curPos = archive->Tell();
|
||||
if (curPos != position)
|
||||
{
|
||||
archive->Seek(position);
|
||||
}
|
||||
|
||||
if (position + readSize > fileSize)
|
||||
{
|
||||
readSize = fileSize - position;
|
||||
}
|
||||
|
||||
if (readSize > 0)
|
||||
{
|
||||
archive->Serialize(buffer, readSize);
|
||||
}
|
||||
return readSize;
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// Read files from Unreal archive
|
||||
// ------------------------------------------------
|
||||
#pragma once
|
||||
|
||||
#include "ICustomMediaFileSource.h"
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
class UnrealArchiveFileSource : public ICustomMediaFileSource
|
||||
{
|
||||
public:
|
||||
explicit UnrealArchiveFileSource(const TSharedRef<FArchive, ESPMode::ThreadSafe> inArchive);
|
||||
virtual ~UnrealArchiveFileSource() override {};
|
||||
virtual int64_t getAvailableSize(uint64_t offset) override;
|
||||
virtual int64_t getSize() override;
|
||||
virtual int64_t readAt(uint64_t offset, void *buffer, uint64_t size) override;
|
||||
|
||||
private:
|
||||
TSharedRef<FArchive, ESPMode::ThreadSafe> archive;
|
||||
FCriticalSection threadLock;
|
||||
};
|
@ -0,0 +1,423 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// Unreal audio output implementation.
|
||||
// ------------------------------------------------
|
||||
|
||||
#include "UnrealAudioOut.h"
|
||||
|
||||
#include "AudioDevice.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "Components/AudioComponent.h"
|
||||
#include "Runtime/Engine/Classes/Sound/AudioSettings.h"
|
||||
#include <time.h>
|
||||
|
||||
#include "Engine/engine.h"
|
||||
|
||||
#include "UnrealLogging.h"
|
||||
|
||||
static int64_t _getNSTime()
|
||||
{
|
||||
#if PLATFORM_ANDROID
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return static_cast<int64_t>(ts.tv_nsec) + (1000000000LL * static_cast<int64_t>(ts.tv_sec));
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int64_t _scaleTime(int64_t val, float scalar)
|
||||
{
|
||||
double dt = double(val) * double(scalar);
|
||||
return int64_t(dt);
|
||||
}
|
||||
|
||||
void USoundWaveProceduralWithTiming::ResetAudio()
|
||||
{
|
||||
USoundWaveProcedural::ResetAudio();
|
||||
QueueSilence(4096);
|
||||
}
|
||||
|
||||
void USoundWaveProceduralWithTiming::QueueSilence(int64_t bytes)
|
||||
{
|
||||
UE_LOG(LogDirectVideo, VeryVerbose, TEXT("Queue Silence %d"), bytes);
|
||||
uint8_t silence[4096];
|
||||
memset(silence, 0, 4096);
|
||||
int64_t count = 4096;
|
||||
while (bytes > 0)
|
||||
{
|
||||
if (bytes < 4096)
|
||||
count = bytes;
|
||||
bytes -= count;
|
||||
QueueAudio(silence, count);
|
||||
}
|
||||
}
|
||||
|
||||
int32 USoundWaveProceduralWithTiming::GeneratePCMData(uint8 *PCMData, const int32 SamplesNeeded)
|
||||
{
|
||||
lastBufSendTime = _getNSTime();
|
||||
|
||||
if (!paused)
|
||||
{
|
||||
return USoundWaveProcedural::GeneratePCMData(PCMData, SamplesNeeded);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(PCMData, 0, SampleByteSize * SamplesNeeded);
|
||||
return SamplesNeeded;
|
||||
}
|
||||
}
|
||||
|
||||
UnrealAudioOut::UnrealAudioOut(UActorComponent *pOwner)
|
||||
: audioSender(NULL), audioComponent(NULL), hasTimeOffset(false), presentationTimeOffset(0),
|
||||
pausedPresentationTime(-1), numChannels(0), sampleRate(0), playbackRate(1.0f),
|
||||
afterSeek(false), isPlaying(false), owner(pOwner)
|
||||
{
|
||||
}
|
||||
|
||||
void UnrealAudioOut::close()
|
||||
{
|
||||
afterSeek = false;
|
||||
if (audioComponent.IsValid())
|
||||
{
|
||||
audioComponent->Stop();
|
||||
audioComponent->SetSound(NULL);
|
||||
}
|
||||
audioSender = NULL;
|
||||
}
|
||||
|
||||
void UnrealAudioOut::init(int rate, int channels)
|
||||
{
|
||||
close();
|
||||
// this object gets garbage collected at the right time because it is
|
||||
// assigned to a component below
|
||||
playbackRate = 1.0f;
|
||||
audioSender = NewObject<USoundWaveProceduralWithTiming>();
|
||||
audioSender->SetSampleRate(rate);
|
||||
audioSender->NumChannels = channels;
|
||||
audioSender->Duration = INDEFINITELY_LOOPING_DURATION;
|
||||
audioSender->bLooping = false;
|
||||
audioSender->bCanProcessAsync = true;
|
||||
audioSender->paused = false;
|
||||
|
||||
if (!audioComponent.IsValid())
|
||||
{
|
||||
if (owner)
|
||||
{
|
||||
AActor *actor = owner->GetOwner<AActor>();
|
||||
if (actor)
|
||||
{
|
||||
audioComponent = actor->FindComponentByClass<UAudioComponent>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (audioComponent.IsValid())
|
||||
{
|
||||
audioComponent->SetSound(audioSender.Get());
|
||||
}
|
||||
else
|
||||
{
|
||||
FAudioDeviceHandle audioDevice = GEngine->GetMainAudioDevice();
|
||||
audioComponent = audioDevice->CreateComponent(audioSender.Get());
|
||||
if (audioComponent.IsValid())
|
||||
{
|
||||
audioComponent->bIsUISound = true;
|
||||
audioComponent->bAllowSpatialization = false;
|
||||
audioComponent->SetVolumeMultiplier(1.0f);
|
||||
audioComponent->AddToRoot();
|
||||
audioComponent->Play();
|
||||
// hold onto strong pointer to audiocomponent so it doesn't get destroyed until
|
||||
// we do
|
||||
|
||||
audioComponentWeCreated = *audioComponent;
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, Error, "Unable to create audio component!");
|
||||
}
|
||||
}
|
||||
if (audioComponent.IsValid())
|
||||
{
|
||||
audioComponent->Play();
|
||||
}
|
||||
|
||||
numChannels = channels;
|
||||
sampleRate = rate;
|
||||
hasTimeOffset = false;
|
||||
presentationTimeOffset = 0;
|
||||
isPlaying = true;
|
||||
UE_LOG(LogDirectVideo, Verbose, TEXT("Audio component: channels %d sampleRate %d"), channels,
|
||||
sampleRate);
|
||||
}
|
||||
|
||||
void UnrealAudioOut::initSilent()
|
||||
{
|
||||
close();
|
||||
UE_LOGFMT(LogDirectVideo, Verbose, "Audio init silent");
|
||||
playbackRate = 1.0f;
|
||||
hasTimeOffset = false;
|
||||
}
|
||||
|
||||
UnrealAudioOut::~UnrealAudioOut()
|
||||
{
|
||||
if (audioComponentWeCreated)
|
||||
{
|
||||
audioComponent->RemoveFromRoot();
|
||||
}
|
||||
UE_LOGFMT(LogDirectVideo, Verbose, "Audio out destroyed");
|
||||
}
|
||||
|
||||
void UnrealAudioOut::sendBuffer(uint8_t *buf, int bufSize, int64_t presentationTimeNS, bool reset)
|
||||
{
|
||||
if (buf == NULL || bufSize == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto pinnedSender = audioSender.Get();
|
||||
if (!pinnedSender)
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, Error, "No audio sender");
|
||||
return;
|
||||
}
|
||||
if (reset)
|
||||
{
|
||||
// reset audio time clock
|
||||
// and clear audio buffers
|
||||
pinnedSender->ResetAudio();
|
||||
}
|
||||
|
||||
int64_t lastSendTime = pinnedSender->lastBufSendTime;
|
||||
if (lastSendTime != 0)
|
||||
{
|
||||
int64_t samplesInSender = pinnedSender->GetAvailableAudioByteCount() / (numChannels * 2);
|
||||
if (samplesInSender <= 0)
|
||||
{
|
||||
pinnedSender->QueueSilence(4096);
|
||||
samplesInSender = 4096 / (numChannels * 2L);
|
||||
}
|
||||
int64_t timeForBuffer = (1000000000LL * samplesInSender) / static_cast<int64_t>(sampleRate);
|
||||
|
||||
int64_t thisOutputTimeNS = lastSendTime + timeForBuffer;
|
||||
|
||||
if (afterSeek)
|
||||
{
|
||||
// we've seeked and we've seen a video frame already
|
||||
// so add silence if required so that we can sync on the
|
||||
// already displayed video frame(s)
|
||||
afterSeek = false;
|
||||
int64_t newOffset = presentationTimeNS - thisOutputTimeNS;
|
||||
if (newOffset > (presentationTimeOffset + 10000000L))
|
||||
{
|
||||
UE_LOG(LogDirectVideo, VeryVerbose, TEXT("After seek silence"));
|
||||
// queue silence to make things line up
|
||||
int64_t difference = newOffset - presentationTimeOffset;
|
||||
int64_t silenceSamples =
|
||||
(difference * static_cast<int64_t>(sampleRate)) / 1000000000LL;
|
||||
pinnedSender->QueueSilence(silenceSamples * 2L * numChannels);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
presentationTimeOffset = presentationTimeNS - thisOutputTimeNS;
|
||||
}
|
||||
hasTimeOffset = true;
|
||||
UE_LOG(LogDirectVideo, VeryVerbose,
|
||||
TEXT("Adjusting offset (%d) - now %ld timeForBuffer %ld lastSendTime %ld "
|
||||
"thisSendTime %ld presentationTimeNS %ld offset %ld"),
|
||||
bufSize, _getNSTime(), timeForBuffer, lastSendTime, thisOutputTimeNS,
|
||||
presentationTimeNS, presentationTimeOffset);
|
||||
}
|
||||
pinnedSender->QueueAudio(buf, bufSize);
|
||||
}
|
||||
|
||||
int64_t UnrealAudioOut::getPresentationTimeNS()
|
||||
{
|
||||
if (!hasTimeOffset)
|
||||
{
|
||||
if (!audioSender)
|
||||
{
|
||||
// silent - initialize time to zero on first query
|
||||
// or else we miss a frame on startup
|
||||
presentationTimeOffset = -_scaleTime(_getNSTime(), playbackRate);
|
||||
hasTimeOffset = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// with audio - initialize
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
int64_t retVal = _scaleTime(_getNSTime(), playbackRate) + presentationTimeOffset;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
IAudioOut::NsTime UnrealAudioOut::getWaitTimeForPresentationTime(int64_t presentationTimeNS,
|
||||
int64_t maxDurationNS)
|
||||
{
|
||||
int64_t thisTime = getPresentationTimeNS();
|
||||
int64_t duration = presentationTimeNS - thisTime;
|
||||
// give a little slack for processing time
|
||||
duration = (duration * 100L) / 98L;
|
||||
if (maxDurationNS > 0 && duration > maxDurationNS)
|
||||
{
|
||||
duration = maxDurationNS;
|
||||
}
|
||||
NsTime curTime = std::chrono::steady_clock::now();
|
||||
if (duration < 0)
|
||||
{
|
||||
return curTime;
|
||||
}
|
||||
|
||||
NsTime waitUntilTime = curTime + NsDuration(_scaleTime(duration, playbackRate));
|
||||
return waitUntilTime;
|
||||
}
|
||||
|
||||
bool UnrealAudioOut::setVolume(float volume)
|
||||
{
|
||||
if (!audioComponent.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (volume > 1.0)
|
||||
volume = 1.0;
|
||||
if (volume < 0.0)
|
||||
volume = 0.0;
|
||||
|
||||
audioComponent->SetVolumeMultiplier(volume);
|
||||
return true;
|
||||
}
|
||||
|
||||
void UnrealAudioOut::setPlaying(bool playing)
|
||||
{
|
||||
if (playing == isPlaying)
|
||||
{
|
||||
return;
|
||||
}
|
||||
isPlaying = playing;
|
||||
if (playing)
|
||||
{
|
||||
// starting after a pause
|
||||
// reset presentation time offset
|
||||
if (pausedPresentationTime != -1 && hasTimeOffset)
|
||||
{
|
||||
presentationTimeOffset =
|
||||
pausedPresentationTime - _scaleTime(_getNSTime(), playbackRate);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// pausing, save current prestime for restore
|
||||
pausedPresentationTime = getPresentationTimeNS();
|
||||
}
|
||||
if (audioSender)
|
||||
{
|
||||
audioSender->paused = !playing;
|
||||
}
|
||||
}
|
||||
|
||||
void UnrealAudioOut::onSeek(int64_t newPresentationTimeNs, bool resetAudio)
|
||||
{
|
||||
if (!audioSender)
|
||||
{
|
||||
presentationTimeOffset = newPresentationTimeNs - _scaleTime(_getNSTime(), playbackRate);
|
||||
UE_LOG(LogDirectVideo, VeryVerbose, TEXT("Seeking: pt %ld update offset %ld (rate=%f)"),
|
||||
newPresentationTimeNs, presentationTimeOffset, playbackRate);
|
||||
}
|
||||
else
|
||||
{
|
||||
// clear audio buffers
|
||||
if (resetAudio)
|
||||
{
|
||||
// after a seek, we may receive either audio or video frames
|
||||
// first. Here we clear the audio buffers, and reset the time
|
||||
// offset so that first of either audio or video frame received
|
||||
// will reset the clock
|
||||
audioSender->ResetAudio();
|
||||
hasTimeOffset = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we are looping - don't clear the audio buffer
|
||||
// and make a rough guess at the clock so that video
|
||||
// frames will still send out correctly and then resync clock when first audio frames
|
||||
// received
|
||||
int64_t lastSendTime = audioSender->lastBufSendTime;
|
||||
if (lastSendTime == 0)
|
||||
{
|
||||
// not actually sent anything yet - assume it will send pretty much now
|
||||
lastSendTime = _getNSTime();
|
||||
}
|
||||
int64_t samplesInSender = audioSender->GetAvailableAudioByteCount() / (numChannels * 2);
|
||||
int64_t timeForBuffer =
|
||||
(1000000000LL * samplesInSender) / static_cast<int64_t>(sampleRate);
|
||||
// when the next buffer (i.e. the one at newPresentationTimeNs)
|
||||
// will get sent at (in clock time )
|
||||
int64_t thisOutputTimeNS = lastSendTime + timeForBuffer;
|
||||
presentationTimeOffset =
|
||||
newPresentationTimeNs - _scaleTime(thisOutputTimeNS, playbackRate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool UnrealAudioOut::setRate(float newRate)
|
||||
{
|
||||
if (audioSender)
|
||||
{
|
||||
if (newRate != 1.0 && newRate != 0.0)
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, Error,
|
||||
"Trying to set playback rate on video with audio, not supported yet");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (newRate > 0)
|
||||
{
|
||||
if (hasTimeOffset)
|
||||
{
|
||||
int64_t presTime = getPresentationTimeNS();
|
||||
playbackRate = newRate;
|
||||
onSeek(presTime, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
playbackRate = newRate;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t UnrealAudioOut::getQueuedTimeNS()
|
||||
{
|
||||
if (audioSender)
|
||||
{
|
||||
int64_t samplesInSender = audioSender->GetAvailableAudioByteCount() / (numChannels * 2);
|
||||
int64_t timeTotal = (1000000000L * samplesInSender) / sampleRate;
|
||||
return timeTotal;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void UnrealAudioOut::onHasVideoTime(int64_t newPresentationTimeNS)
|
||||
{
|
||||
if (audioSender && !hasTimeOffset)
|
||||
{
|
||||
// if we haven't sent any audio yet, then set current time to this frame time,
|
||||
// and let first updating of offset add silence
|
||||
presentationTimeOffset = newPresentationTimeNS - _scaleTime(_getNSTime(), playbackRate);
|
||||
hasTimeOffset = true;
|
||||
afterSeek = true;
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// Unreal IAudioOut implementation. This uses a
|
||||
// USoundWaveProcedural to do the output, and also
|
||||
// a UAudioComponent object. If given a parent
|
||||
// actor object which has an existing
|
||||
// UAudioComponent, it uses that. Otherwise it
|
||||
// makes a new UAudioComponent and holds a strong
|
||||
// object pointer to it until this class is
|
||||
// destroyed.
|
||||
// ------------------------------------------------
|
||||
#pragma once
|
||||
|
||||
#include "Sound/SoundWaveProcedural.h"
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
|
||||
#include "IAudioOut.h"
|
||||
|
||||
#include "UnrealAudioOut.generated.h"
|
||||
|
||||
class USoundWaveProceduralWithTiming;
|
||||
class UAudioComponent;
|
||||
class UActorComponent;
|
||||
|
||||
UCLASS(MinimalAPI)
|
||||
class USoundWaveProceduralWithTiming : public USoundWaveProcedural
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
virtual int32 GeneratePCMData(uint8 *PCMData, const int32 SamplesNeeded) override;
|
||||
virtual void ResetAudio();
|
||||
virtual void QueueSilence(int64_t bytes);
|
||||
bool paused;
|
||||
bool hasTiming;
|
||||
int64_t lastBufSendTime;
|
||||
};
|
||||
|
||||
class UnrealAudioOut : public IAudioOut
|
||||
{
|
||||
public:
|
||||
explicit UnrealAudioOut(UActorComponent *owner);
|
||||
virtual ~UnrealAudioOut() override;
|
||||
virtual void init(int sampleRate, int channels) override;
|
||||
virtual void initSilent() override;
|
||||
virtual void close() override;
|
||||
virtual void sendBuffer(uint8_t *buf, int bufSize, int64_t presentationTimeNS,
|
||||
bool reset = false) override;
|
||||
virtual int64_t getPresentationTimeNS() override; // current presentation time
|
||||
virtual IAudioOut::NsTime getWaitTimeForPresentationTime(int64_t presentationTimeNS,
|
||||
int64_t maxDuration) override;
|
||||
virtual bool setVolume(float volume) override;
|
||||
virtual void setPlaying(bool playing) override;
|
||||
virtual bool setRate(float rate) override;
|
||||
virtual void onSeek(int64_t newPresentationTimeNS, bool resetAudio) override;
|
||||
virtual int64_t getQueuedTimeNS() override;
|
||||
virtual void onHasVideoTime(int64_t newPresentationTimeNS) override;
|
||||
|
||||
private:
|
||||
TObjectPtr<USoundWaveProceduralWithTiming> audioSender;
|
||||
UActorComponent *owner;
|
||||
TWeakObjectPtr<UAudioComponent> audioComponent;
|
||||
TObjectPtr<UAudioComponent> audioComponentWeCreated;
|
||||
bool hasTimeOffset;
|
||||
int64_t presentationTimeOffset;
|
||||
int numChannels;
|
||||
int sampleRate;
|
||||
bool isPlaying;
|
||||
|
||||
bool afterSeek;
|
||||
|
||||
float playbackRate;
|
||||
|
||||
int64_t pausedPresentationTime;
|
||||
};
|
@ -0,0 +1,10 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// Unreal specific log category definition.
|
||||
// ------------------------------------------------
|
||||
|
||||
#include "UnrealLogging.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogDirectVideo);
|
@ -0,0 +1,93 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// Unreal specific log category definition.
|
||||
// ------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ILogger.h"
|
||||
|
||||
#include "Logging/StructuredLog.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogDirectVideo, Log, All);
|
||||
|
||||
class UnrealLogger : public ILogger
|
||||
{
|
||||
public:
|
||||
UnrealLogger() : very_verbose_bitmask(0), verbose_bitmask(0), bitmask(0), error_bitmask(0)
|
||||
{
|
||||
}
|
||||
|
||||
void LogWithType(int64_t type, const char *fmt, ...) override
|
||||
{
|
||||
if ((type & bitmask) == 0)
|
||||
{
|
||||
// don't format the string unless we're going to output it
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
va_list arg_ptr;
|
||||
char buffer[1024] = "";
|
||||
va_start(arg_ptr, fmt);
|
||||
vsnprintf(buffer, 1023, fmt, arg_ptr);
|
||||
va_end(arg_ptr);
|
||||
if ((type & error_bitmask) != 0)
|
||||
{
|
||||
// error
|
||||
UE_LOG(LogDirectVideo, Error, TEXT("%s"), ANSI_TO_TCHAR(buffer));
|
||||
}
|
||||
else if ((type & very_verbose_bitmask) != 0)
|
||||
{
|
||||
UE_LOG(LogDirectVideo, VeryVerbose, TEXT("%s"), ANSI_TO_TCHAR(buffer));
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogDirectVideo, Verbose, TEXT("%s"), ANSI_TO_TCHAR(buffer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetLogVisibilityBitmask(int64_t newBitmask)
|
||||
{
|
||||
very_verbose_bitmask = 0;
|
||||
error_bitmask = 1;
|
||||
verbose_bitmask = 0;
|
||||
bitmask = newBitmask;
|
||||
bool veryVerbose = true;
|
||||
for (int64_t bit = 1; bit < ILogger::LogTypes::LOGTYPE_MAX; bit *= 2)
|
||||
{
|
||||
if (veryVerbose)
|
||||
{
|
||||
very_verbose_bitmask |= bit;
|
||||
}
|
||||
else
|
||||
{
|
||||
verbose_bitmask |= bit;
|
||||
}
|
||||
veryVerbose = !veryVerbose;
|
||||
}
|
||||
if (!UE_LOG_ACTIVE(LogDirectVideo, Verbose))
|
||||
{
|
||||
bitmask &= (~verbose_bitmask);
|
||||
verbose_bitmask = 0;
|
||||
}
|
||||
if (!UE_LOG_ACTIVE(LogDirectVideo, VeryVerbose))
|
||||
{
|
||||
bitmask &= (~very_verbose_bitmask);
|
||||
very_verbose_bitmask = 0;
|
||||
}
|
||||
if (!UE_LOG_ACTIVE(LogDirectVideo, Error))
|
||||
{
|
||||
bitmask = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
int64_t very_verbose_bitmask;
|
||||
int64_t verbose_bitmask;
|
||||
int64_t bitmask;
|
||||
int64_t error_bitmask;
|
||||
};
|
@ -0,0 +1,155 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// An implementation of IMediaSamples for the new
|
||||
// v2 video timing in Unreal media player.
|
||||
//
|
||||
// With new timing, whilst in theory IMediaSamples
|
||||
// is a queue, in use only the most recent sample
|
||||
// is ever read.
|
||||
//
|
||||
// Given we know that, this class dumps any
|
||||
// previous samples immediately, which allows
|
||||
// the underlying hardware image to be released as
|
||||
// soon as possible, which makes video decoding
|
||||
// work more efficiently.
|
||||
// ------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "AndroidVulkanTextureSample.h"
|
||||
#include "IMediaSamples.h"
|
||||
|
||||
#include "UnrealLogging.h"
|
||||
// for v2 video timing, the sample queue just
|
||||
// holds a single image
|
||||
class FVideoMediaSampleHolder : public IMediaSamples
|
||||
{
|
||||
|
||||
public:
|
||||
void AddVideo(TSharedRef<AndroidVulkanTextureSample, ESPMode::ThreadSafe> &InSample)
|
||||
{
|
||||
if (VideoSample.IsValid())
|
||||
{
|
||||
// force sample texture clear because
|
||||
// we're not going to display it
|
||||
VideoSample->Clear();
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Clearing videoframe {0}",
|
||||
VideoSample.GetSharedReferenceCount());
|
||||
}
|
||||
VideoSample = InSample;
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "CSet video frame {0}", VideoSample.IsValid());
|
||||
}
|
||||
|
||||
virtual bool FetchVideo(
|
||||
TRange<FTimespan> TimeRange,
|
||||
TSharedPtr<IMediaTextureSample, ESPMode::ThreadSafe> &OutSample) override
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "BFetch video frame {0}", VideoSample.IsValid());
|
||||
if (VideoSample.IsValid())
|
||||
{
|
||||
OutSample = VideoSample;
|
||||
VideoSample.Reset();
|
||||
return true;
|
||||
}
|
||||
return false; // override in child classes, if supported
|
||||
}
|
||||
virtual bool FetchVideo(
|
||||
TRange<FMediaTimeStamp> TimeRange,
|
||||
TSharedPtr<IMediaTextureSample, ESPMode::ThreadSafe> &OutSample) override
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "AFetch video frame {0}", VideoSample.IsValid());
|
||||
if (VideoSample.IsValid())
|
||||
{
|
||||
OutSample = VideoSample;
|
||||
VideoSample.Reset();
|
||||
return true;
|
||||
}
|
||||
return false; // override in child classes, if supported
|
||||
}
|
||||
|
||||
virtual EFetchBestSampleResult FetchBestVideoSampleForTimeRange(
|
||||
const TRange<FMediaTimeStamp> &TimeRange,
|
||||
TSharedPtr<IMediaTextureSample, ESPMode::ThreadSafe> &OutSample, bool bReverse)
|
||||
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Best sample {0}", VideoSample.IsValid());
|
||||
if (VideoSample.IsValid())
|
||||
{
|
||||
OutSample = VideoSample;
|
||||
VideoSample.Reset();
|
||||
return EFetchBestSampleResult::Ok;
|
||||
}
|
||||
else
|
||||
{
|
||||
return EFetchBestSampleResult::NoSample;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void FlushSamples() override
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Flush samples {0}", VideoSample.IsValid());
|
||||
// override in child classes, if supported
|
||||
VideoSample.Reset();
|
||||
FlushCount += 1;
|
||||
}
|
||||
|
||||
virtual uint32 GetFlushCount()
|
||||
{
|
||||
return FlushCount;
|
||||
}
|
||||
|
||||
virtual bool PeekVideoSampleTime(FMediaTimeStamp &TimeStamp) override
|
||||
{
|
||||
if (VideoSample.IsValid())
|
||||
{
|
||||
TimeStamp = VideoSample->GetTime();
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Peek timestamp {0}",
|
||||
TimeStamp.Time.GetTotalSeconds());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// UE_LOGFMT(LogDirectVideo, VeryVerbose, "Peek timestamp returning zero");
|
||||
// TimeStamp =
|
||||
// FMediaTimeStamp(FTimespan(0LL),FMediaTimeStamp::MakeSequenceIndex(0, 0));
|
||||
// return true;
|
||||
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Peek timestamp returning false");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
virtual int32 NumVideoSamples() const override
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Query num samples {0}", VideoSample.IsValid());
|
||||
if (VideoSample.IsValid())
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool DiscardVideoSamples(const TRange<FMediaTimeStamp> &TimeRange, bool bReverse) override
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "Discard video samples {0}", VideoSample.IsValid());
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual uint32 PurgeOutdatedVideoSamples(const FMediaTimeStamp &ReferenceTime, bool bReversed,
|
||||
FTimespan MaxAge) override
|
||||
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideo, VeryVerbose, "PurgeOutdatedVideoSamples {0} {1}",
|
||||
VideoSample.IsValid(), ReferenceTime.Time.GetTicks() * 100);
|
||||
return 0;
|
||||
};
|
||||
|
||||
TSharedPtr<AndroidVulkanTextureSample, ESPMode::ThreadSafe> VideoSample;
|
||||
|
||||
uint32 FlushCount = 0;
|
||||
};
|
@ -0,0 +1,835 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// Autogenerated shader spv header file.
|
||||
// ------------------------------------------------
|
||||
|
||||
#include <stdint.h>
|
||||
// Shader generated from shader.2.2.frag.spv
|
||||
const uint8_t shader_shader_2_2_frag[] = {
|
||||
3, 2, 35, 7, 0, 3, 1, 0, 10, 0, 13, 0, 41, 0, 0, 0, 0, 0, 0, 0, 17, 0,
|
||||
2, 0, 1, 0, 0, 0, 11, 0, 6, 0, 1, 0, 0, 0, 71, 76, 83, 76, 46, 115, 116, 100,
|
||||
46, 52, 53, 48, 0, 0, 0, 0, 14, 0, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 15, 0,
|
||||
7, 0, 4, 0, 0, 0, 4, 0, 0, 0, 109, 97, 105, 110, 0, 0, 0, 0, 9, 0, 0, 0,
|
||||
17, 0, 0, 0, 16, 0, 3, 0, 4, 0, 0, 0, 7, 0, 0, 0, 71, 0, 4, 0, 9, 0,
|
||||
0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 71, 0, 4, 0, 13, 0, 0, 0, 34, 0, 0, 0,
|
||||
0, 0, 0, 0, 71, 0, 4, 0, 13, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 71, 0,
|
||||
4, 0, 17, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 19, 0, 2, 0, 2, 0, 0, 0,
|
||||
33, 0, 3, 0, 3, 0, 0, 0, 2, 0, 0, 0, 22, 0, 3, 0, 6, 0, 0, 0, 32, 0,
|
||||
0, 0, 23, 0, 4, 0, 7, 0, 0, 0, 6, 0, 0, 0, 4, 0, 0, 0, 32, 0, 4, 0,
|
||||
8, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0, 59, 0, 4, 0, 8, 0, 0, 0, 9, 0,
|
||||
0, 0, 3, 0, 0, 0, 25, 0, 9, 0, 10, 0, 0, 0, 6, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 27, 0,
|
||||
3, 0, 11, 0, 0, 0, 10, 0, 0, 0, 32, 0, 4, 0, 12, 0, 0, 0, 0, 0, 0, 0,
|
||||
11, 0, 0, 0, 59, 0, 4, 0, 12, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 23, 0,
|
||||
4, 0, 15, 0, 0, 0, 6, 0, 0, 0, 2, 0, 0, 0, 32, 0, 4, 0, 16, 0, 0, 0,
|
||||
1, 0, 0, 0, 15, 0, 0, 0, 59, 0, 4, 0, 16, 0, 0, 0, 17, 0, 0, 0, 1, 0,
|
||||
0, 0, 43, 0, 4, 0, 6, 0, 0, 0, 22, 0, 0, 0, 205, 204, 12, 64, 23, 0, 4, 0,
|
||||
23, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 21, 0, 4, 0, 29, 0, 0, 0, 32, 0,
|
||||
0, 0, 0, 0, 0, 0, 43, 0, 4, 0, 29, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0,
|
||||
32, 0, 4, 0, 31, 0, 0, 0, 3, 0, 0, 0, 6, 0, 0, 0, 43, 0, 4, 0, 29, 0,
|
||||
0, 0, 34, 0, 0, 0, 1, 0, 0, 0, 43, 0, 4, 0, 29, 0, 0, 0, 37, 0, 0, 0,
|
||||
2, 0, 0, 0, 44, 0, 6, 0, 23, 0, 0, 0, 40, 0, 0, 0, 22, 0, 0, 0, 22, 0,
|
||||
0, 0, 22, 0, 0, 0, 54, 0, 5, 0, 2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0,
|
||||
3, 0, 0, 0, 248, 0, 2, 0, 5, 0, 0, 0, 61, 0, 4, 0, 11, 0, 0, 0, 14, 0,
|
||||
0, 0, 13, 0, 0, 0, 61, 0, 4, 0, 15, 0, 0, 0, 18, 0, 0, 0, 17, 0, 0, 0,
|
||||
87, 0, 5, 0, 7, 0, 0, 0, 19, 0, 0, 0, 14, 0, 0, 0, 18, 0, 0, 0, 62, 0,
|
||||
3, 0, 9, 0, 0, 0, 19, 0, 0, 0, 61, 0, 4, 0, 7, 0, 0, 0, 24, 0, 0, 0,
|
||||
9, 0, 0, 0, 79, 0, 8, 0, 23, 0, 0, 0, 25, 0, 0, 0, 24, 0, 0, 0, 24, 0,
|
||||
0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 12, 0, 7, 0, 23, 0, 0, 0,
|
||||
28, 0, 0, 0, 1, 0, 0, 0, 26, 0, 0, 0, 25, 0, 0, 0, 40, 0, 0, 0, 65, 0,
|
||||
5, 0, 31, 0, 0, 0, 32, 0, 0, 0, 9, 0, 0, 0, 30, 0, 0, 0, 81, 0, 5, 0,
|
||||
6, 0, 0, 0, 33, 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, 62, 0, 3, 0, 32, 0,
|
||||
0, 0, 33, 0, 0, 0, 65, 0, 5, 0, 31, 0, 0, 0, 35, 0, 0, 0, 9, 0, 0, 0,
|
||||
34, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 36, 0, 0, 0, 28, 0, 0, 0, 1, 0,
|
||||
0, 0, 62, 0, 3, 0, 35, 0, 0, 0, 36, 0, 0, 0, 65, 0, 5, 0, 31, 0, 0, 0,
|
||||
38, 0, 0, 0, 9, 0, 0, 0, 37, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 39, 0,
|
||||
0, 0, 28, 0, 0, 0, 2, 0, 0, 0, 62, 0, 3, 0, 38, 0, 0, 0, 39, 0, 0, 0,
|
||||
253, 0, 1, 0, 56, 0, 1, 0};
|
||||
// Shader generated from shader.2.4.frag.spv
|
||||
const uint8_t shader_shader_2_4_frag[] = {
|
||||
3, 2, 35, 7, 0, 3, 1, 0, 10, 0, 13, 0, 55, 0, 0, 0, 0, 0, 0, 0, 17, 0,
|
||||
2, 0, 1, 0, 0, 0, 11, 0, 6, 0, 1, 0, 0, 0, 71, 76, 83, 76, 46, 115, 116, 100,
|
||||
46, 52, 53, 48, 0, 0, 0, 0, 14, 0, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 15, 0,
|
||||
7, 0, 4, 0, 0, 0, 4, 0, 0, 0, 109, 97, 105, 110, 0, 0, 0, 0, 9, 0, 0, 0,
|
||||
17, 0, 0, 0, 16, 0, 3, 0, 4, 0, 0, 0, 7, 0, 0, 0, 71, 0, 4, 0, 9, 0,
|
||||
0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 71, 0, 4, 0, 13, 0, 0, 0, 34, 0, 0, 0,
|
||||
0, 0, 0, 0, 71, 0, 4, 0, 13, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 71, 0,
|
||||
4, 0, 17, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 19, 0, 2, 0, 2, 0, 0, 0,
|
||||
33, 0, 3, 0, 3, 0, 0, 0, 2, 0, 0, 0, 22, 0, 3, 0, 6, 0, 0, 0, 32, 0,
|
||||
0, 0, 23, 0, 4, 0, 7, 0, 0, 0, 6, 0, 0, 0, 4, 0, 0, 0, 32, 0, 4, 0,
|
||||
8, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0, 59, 0, 4, 0, 8, 0, 0, 0, 9, 0,
|
||||
0, 0, 3, 0, 0, 0, 25, 0, 9, 0, 10, 0, 0, 0, 6, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 27, 0,
|
||||
3, 0, 11, 0, 0, 0, 10, 0, 0, 0, 32, 0, 4, 0, 12, 0, 0, 0, 0, 0, 0, 0,
|
||||
11, 0, 0, 0, 59, 0, 4, 0, 12, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 23, 0,
|
||||
4, 0, 15, 0, 0, 0, 6, 0, 0, 0, 2, 0, 0, 0, 32, 0, 4, 0, 16, 0, 0, 0,
|
||||
1, 0, 0, 0, 15, 0, 0, 0, 59, 0, 4, 0, 16, 0, 0, 0, 17, 0, 0, 0, 1, 0,
|
||||
0, 0, 43, 0, 4, 0, 6, 0, 0, 0, 22, 0, 0, 0, 154, 153, 25, 64, 23, 0, 4, 0,
|
||||
23, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 21, 0, 4, 0, 29, 0, 0, 0, 32, 0,
|
||||
0, 0, 0, 0, 0, 0, 43, 0, 4, 0, 29, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0,
|
||||
32, 0, 4, 0, 31, 0, 0, 0, 3, 0, 0, 0, 6, 0, 0, 0, 43, 0, 4, 0, 29, 0,
|
||||
0, 0, 34, 0, 0, 0, 1, 0, 0, 0, 43, 0, 4, 0, 29, 0, 0, 0, 37, 0, 0, 0,
|
||||
2, 0, 0, 0, 43, 0, 4, 0, 6, 0, 0, 0, 42, 0, 0, 0, 129, 128, 128, 61, 43, 0,
|
||||
4, 0, 6, 0, 0, 0, 45, 0, 0, 0, 133, 10, 149, 63, 44, 0, 6, 0, 23, 0, 0, 0,
|
||||
53, 0, 0, 0, 22, 0, 0, 0, 22, 0, 0, 0, 22, 0, 0, 0, 44, 0, 6, 0, 23, 0,
|
||||
0, 0, 54, 0, 0, 0, 42, 0, 0, 0, 42, 0, 0, 0, 42, 0, 0, 0, 54, 0, 5, 0,
|
||||
2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 248, 0, 2, 0, 5, 0,
|
||||
0, 0, 61, 0, 4, 0, 11, 0, 0, 0, 14, 0, 0, 0, 13, 0, 0, 0, 61, 0, 4, 0,
|
||||
15, 0, 0, 0, 18, 0, 0, 0, 17, 0, 0, 0, 87, 0, 5, 0, 7, 0, 0, 0, 19, 0,
|
||||
0, 0, 14, 0, 0, 0, 18, 0, 0, 0, 62, 0, 3, 0, 9, 0, 0, 0, 19, 0, 0, 0,
|
||||
61, 0, 4, 0, 7, 0, 0, 0, 24, 0, 0, 0, 9, 0, 0, 0, 79, 0, 8, 0, 23, 0,
|
||||
0, 0, 25, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
|
||||
2, 0, 0, 0, 12, 0, 7, 0, 23, 0, 0, 0, 28, 0, 0, 0, 1, 0, 0, 0, 26, 0,
|
||||
0, 0, 25, 0, 0, 0, 53, 0, 0, 0, 65, 0, 5, 0, 31, 0, 0, 0, 32, 0, 0, 0,
|
||||
9, 0, 0, 0, 30, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 33, 0, 0, 0, 28, 0,
|
||||
0, 0, 0, 0, 0, 0, 62, 0, 3, 0, 32, 0, 0, 0, 33, 0, 0, 0, 65, 0, 5, 0,
|
||||
31, 0, 0, 0, 35, 0, 0, 0, 9, 0, 0, 0, 34, 0, 0, 0, 81, 0, 5, 0, 6, 0,
|
||||
0, 0, 36, 0, 0, 0, 28, 0, 0, 0, 1, 0, 0, 0, 62, 0, 3, 0, 35, 0, 0, 0,
|
||||
36, 0, 0, 0, 65, 0, 5, 0, 31, 0, 0, 0, 38, 0, 0, 0, 9, 0, 0, 0, 37, 0,
|
||||
0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 39, 0, 0, 0, 28, 0, 0, 0, 2, 0, 0, 0,
|
||||
62, 0, 3, 0, 38, 0, 0, 0, 39, 0, 0, 0, 61, 0, 4, 0, 7, 0, 0, 0, 40, 0,
|
||||
0, 0, 9, 0, 0, 0, 79, 0, 8, 0, 23, 0, 0, 0, 41, 0, 0, 0, 40, 0, 0, 0,
|
||||
40, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 131, 0, 5, 0, 23, 0,
|
||||
0, 0, 44, 0, 0, 0, 41, 0, 0, 0, 54, 0, 0, 0, 142, 0, 5, 0, 23, 0, 0, 0,
|
||||
46, 0, 0, 0, 44, 0, 0, 0, 45, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 48, 0,
|
||||
0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 62, 0, 3, 0, 32, 0, 0, 0, 48, 0, 0, 0,
|
||||
81, 0, 5, 0, 6, 0, 0, 0, 50, 0, 0, 0, 46, 0, 0, 0, 1, 0, 0, 0, 62, 0,
|
||||
3, 0, 35, 0, 0, 0, 50, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 52, 0, 0, 0,
|
||||
46, 0, 0, 0, 2, 0, 0, 0, 62, 0, 3, 0, 38, 0, 0, 0, 52, 0, 0, 0, 253, 0,
|
||||
1, 0, 56, 0, 1, 0};
|
||||
// Shader generated from shader.2.8.frag.spv
|
||||
const uint8_t shader_shader_2_8_frag[] = {
|
||||
3, 2, 35, 7, 0, 3, 1, 0, 10, 0, 13, 0, 41, 0, 0, 0, 0, 0, 0, 0, 17, 0,
|
||||
2, 0, 1, 0, 0, 0, 11, 0, 6, 0, 1, 0, 0, 0, 71, 76, 83, 76, 46, 115, 116, 100,
|
||||
46, 52, 53, 48, 0, 0, 0, 0, 14, 0, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 15, 0,
|
||||
7, 0, 4, 0, 0, 0, 4, 0, 0, 0, 109, 97, 105, 110, 0, 0, 0, 0, 9, 0, 0, 0,
|
||||
17, 0, 0, 0, 16, 0, 3, 0, 4, 0, 0, 0, 7, 0, 0, 0, 71, 0, 4, 0, 9, 0,
|
||||
0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 71, 0, 4, 0, 13, 0, 0, 0, 34, 0, 0, 0,
|
||||
0, 0, 0, 0, 71, 0, 4, 0, 13, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 71, 0,
|
||||
4, 0, 17, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 19, 0, 2, 0, 2, 0, 0, 0,
|
||||
33, 0, 3, 0, 3, 0, 0, 0, 2, 0, 0, 0, 22, 0, 3, 0, 6, 0, 0, 0, 32, 0,
|
||||
0, 0, 23, 0, 4, 0, 7, 0, 0, 0, 6, 0, 0, 0, 4, 0, 0, 0, 32, 0, 4, 0,
|
||||
8, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0, 59, 0, 4, 0, 8, 0, 0, 0, 9, 0,
|
||||
0, 0, 3, 0, 0, 0, 25, 0, 9, 0, 10, 0, 0, 0, 6, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 27, 0,
|
||||
3, 0, 11, 0, 0, 0, 10, 0, 0, 0, 32, 0, 4, 0, 12, 0, 0, 0, 0, 0, 0, 0,
|
||||
11, 0, 0, 0, 59, 0, 4, 0, 12, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 23, 0,
|
||||
4, 0, 15, 0, 0, 0, 6, 0, 0, 0, 2, 0, 0, 0, 32, 0, 4, 0, 16, 0, 0, 0,
|
||||
1, 0, 0, 0, 15, 0, 0, 0, 59, 0, 4, 0, 16, 0, 0, 0, 17, 0, 0, 0, 1, 0,
|
||||
0, 0, 43, 0, 4, 0, 6, 0, 0, 0, 22, 0, 0, 0, 51, 51, 51, 64, 23, 0, 4, 0,
|
||||
23, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 21, 0, 4, 0, 29, 0, 0, 0, 32, 0,
|
||||
0, 0, 0, 0, 0, 0, 43, 0, 4, 0, 29, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0,
|
||||
32, 0, 4, 0, 31, 0, 0, 0, 3, 0, 0, 0, 6, 0, 0, 0, 43, 0, 4, 0, 29, 0,
|
||||
0, 0, 34, 0, 0, 0, 1, 0, 0, 0, 43, 0, 4, 0, 29, 0, 0, 0, 37, 0, 0, 0,
|
||||
2, 0, 0, 0, 44, 0, 6, 0, 23, 0, 0, 0, 40, 0, 0, 0, 22, 0, 0, 0, 22, 0,
|
||||
0, 0, 22, 0, 0, 0, 54, 0, 5, 0, 2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0,
|
||||
3, 0, 0, 0, 248, 0, 2, 0, 5, 0, 0, 0, 61, 0, 4, 0, 11, 0, 0, 0, 14, 0,
|
||||
0, 0, 13, 0, 0, 0, 61, 0, 4, 0, 15, 0, 0, 0, 18, 0, 0, 0, 17, 0, 0, 0,
|
||||
87, 0, 5, 0, 7, 0, 0, 0, 19, 0, 0, 0, 14, 0, 0, 0, 18, 0, 0, 0, 62, 0,
|
||||
3, 0, 9, 0, 0, 0, 19, 0, 0, 0, 61, 0, 4, 0, 7, 0, 0, 0, 24, 0, 0, 0,
|
||||
9, 0, 0, 0, 79, 0, 8, 0, 23, 0, 0, 0, 25, 0, 0, 0, 24, 0, 0, 0, 24, 0,
|
||||
0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 12, 0, 7, 0, 23, 0, 0, 0,
|
||||
28, 0, 0, 0, 1, 0, 0, 0, 26, 0, 0, 0, 25, 0, 0, 0, 40, 0, 0, 0, 65, 0,
|
||||
5, 0, 31, 0, 0, 0, 32, 0, 0, 0, 9, 0, 0, 0, 30, 0, 0, 0, 81, 0, 5, 0,
|
||||
6, 0, 0, 0, 33, 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, 62, 0, 3, 0, 32, 0,
|
||||
0, 0, 33, 0, 0, 0, 65, 0, 5, 0, 31, 0, 0, 0, 35, 0, 0, 0, 9, 0, 0, 0,
|
||||
34, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 36, 0, 0, 0, 28, 0, 0, 0, 1, 0,
|
||||
0, 0, 62, 0, 3, 0, 35, 0, 0, 0, 36, 0, 0, 0, 65, 0, 5, 0, 31, 0, 0, 0,
|
||||
38, 0, 0, 0, 9, 0, 0, 0, 37, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 39, 0,
|
||||
0, 0, 28, 0, 0, 0, 2, 0, 0, 0, 62, 0, 3, 0, 38, 0, 0, 0, 39, 0, 0, 0,
|
||||
253, 0, 1, 0, 56, 0, 1, 0};
|
||||
// Shader generated from shader.frag.spv
|
||||
const uint8_t shader_shader_frag[] = {
|
||||
3, 2, 35, 7, 0, 0, 1, 0, 11, 0, 8, 0, 44, 0, 0, 0, 0, 0, 0,
|
||||
0, 17, 0, 2, 0, 1, 0, 0, 0, 11, 0, 6, 0, 1, 0, 0, 0, 71, 76,
|
||||
83, 76, 46, 115, 116, 100, 46, 52, 53, 48, 0, 0, 0, 0, 14, 0, 3, 0, 0,
|
||||
0, 0, 0, 1, 0, 0, 0, 15, 0, 7, 0, 4, 0, 0, 0, 4, 0, 0, 0,
|
||||
109, 97, 105, 110, 0, 0, 0, 0, 9, 0, 0, 0, 17, 0, 0, 0, 16, 0, 3,
|
||||
0, 4, 0, 0, 0, 7, 0, 0, 0, 3, 0, 3, 0, 2, 0, 0, 0, 194, 1,
|
||||
0, 0, 5, 0, 4, 0, 4, 0, 0, 0, 109, 97, 105, 110, 0, 0, 0, 0, 5,
|
||||
0, 4, 0, 9, 0, 0, 0, 99, 111, 108, 111, 114, 0, 0, 0, 5, 0, 3, 0,
|
||||
13, 0, 0, 0, 116, 101, 120, 0, 5, 0, 5, 0, 17, 0, 0, 0, 116, 101, 120,
|
||||
95, 99, 111, 111, 114, 100, 0, 0, 0, 5, 0, 5, 0, 23, 0, 0, 0, 83, 104,
|
||||
97, 100, 101, 114, 73, 110, 102, 111, 0, 0, 6, 0, 5, 0, 23, 0, 0, 0, 0,
|
||||
0, 0, 0, 103, 97, 109, 109, 97, 0, 0, 0, 5, 0, 6, 0, 25, 0, 0, 0,
|
||||
116, 101, 120, 116, 117, 114, 101, 95, 105, 110, 102, 111, 0, 0, 0, 0, 71, 0, 4,
|
||||
0, 9, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 71, 0, 4, 0, 13, 0,
|
||||
0, 0, 34, 0, 0, 0, 0, 0, 0, 0, 71, 0, 4, 0, 13, 0, 0, 0, 33,
|
||||
0, 0, 0, 0, 0, 0, 0, 71, 0, 4, 0, 17, 0, 0, 0, 30, 0, 0, 0,
|
||||
0, 0, 0, 0, 72, 0, 5, 0, 23, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0,
|
||||
0, 0, 0, 0, 0, 71, 0, 3, 0, 23, 0, 0, 0, 2, 0, 0, 0, 71, 0,
|
||||
4, 0, 25, 0, 0, 0, 34, 0, 0, 0, 0, 0, 0, 0, 71, 0, 4, 0, 25,
|
||||
0, 0, 0, 33, 0, 0, 0, 1, 0, 0, 0, 19, 0, 2, 0, 2, 0, 0, 0,
|
||||
33, 0, 3, 0, 3, 0, 0, 0, 2, 0, 0, 0, 22, 0, 3, 0, 6, 0, 0,
|
||||
0, 32, 0, 0, 0, 23, 0, 4, 0, 7, 0, 0, 0, 6, 0, 0, 0, 4, 0,
|
||||
0, 0, 32, 0, 4, 0, 8, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0, 59,
|
||||
0, 4, 0, 8, 0, 0, 0, 9, 0, 0, 0, 3, 0, 0, 0, 25, 0, 9, 0,
|
||||
10, 0, 0, 0, 6, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 27, 0, 3, 0, 11, 0,
|
||||
0, 0, 10, 0, 0, 0, 32, 0, 4, 0, 12, 0, 0, 0, 0, 0, 0, 0, 11,
|
||||
0, 0, 0, 59, 0, 4, 0, 12, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0,
|
||||
23, 0, 4, 0, 15, 0, 0, 0, 6, 0, 0, 0, 2, 0, 0, 0, 32, 0, 4,
|
||||
0, 16, 0, 0, 0, 1, 0, 0, 0, 15, 0, 0, 0, 59, 0, 4, 0, 16, 0,
|
||||
0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 23, 0, 4, 0, 20, 0, 0, 0, 6,
|
||||
0, 0, 0, 3, 0, 0, 0, 30, 0, 3, 0, 23, 0, 0, 0, 6, 0, 0, 0,
|
||||
32, 0, 4, 0, 24, 0, 0, 0, 2, 0, 0, 0, 23, 0, 0, 0, 59, 0, 4,
|
||||
0, 24, 0, 0, 0, 25, 0, 0, 0, 2, 0, 0, 0, 21, 0, 4, 0, 26, 0,
|
||||
0, 0, 32, 0, 0, 0, 1, 0, 0, 0, 43, 0, 4, 0, 26, 0, 0, 0, 27,
|
||||
0, 0, 0, 0, 0, 0, 0, 32, 0, 4, 0, 28, 0, 0, 0, 2, 0, 0, 0,
|
||||
6, 0, 0, 0, 21, 0, 4, 0, 33, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0,
|
||||
0, 43, 0, 4, 0, 33, 0, 0, 0, 34, 0, 0, 0, 0, 0, 0, 0, 32, 0,
|
||||
4, 0, 35, 0, 0, 0, 3, 0, 0, 0, 6, 0, 0, 0, 43, 0, 4, 0, 33,
|
||||
0, 0, 0, 38, 0, 0, 0, 1, 0, 0, 0, 43, 0, 4, 0, 33, 0, 0, 0,
|
||||
41, 0, 0, 0, 2, 0, 0, 0, 54, 0, 5, 0, 2, 0, 0, 0, 4, 0, 0,
|
||||
0, 0, 0, 0, 0, 3, 0, 0, 0, 248, 0, 2, 0, 5, 0, 0, 0, 61, 0,
|
||||
4, 0, 11, 0, 0, 0, 14, 0, 0, 0, 13, 0, 0, 0, 61, 0, 4, 0, 15,
|
||||
0, 0, 0, 18, 0, 0, 0, 17, 0, 0, 0, 87, 0, 5, 0, 7, 0, 0, 0,
|
||||
19, 0, 0, 0, 14, 0, 0, 0, 18, 0, 0, 0, 62, 0, 3, 0, 9, 0, 0,
|
||||
0, 19, 0, 0, 0, 61, 0, 4, 0, 7, 0, 0, 0, 21, 0, 0, 0, 9, 0,
|
||||
0, 0, 79, 0, 8, 0, 20, 0, 0, 0, 22, 0, 0, 0, 21, 0, 0, 0, 21,
|
||||
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 65, 0, 5, 0,
|
||||
28, 0, 0, 0, 29, 0, 0, 0, 25, 0, 0, 0, 27, 0, 0, 0, 61, 0, 4,
|
||||
0, 6, 0, 0, 0, 30, 0, 0, 0, 29, 0, 0, 0, 80, 0, 6, 0, 20, 0,
|
||||
0, 0, 31, 0, 0, 0, 30, 0, 0, 0, 30, 0, 0, 0, 30, 0, 0, 0, 12,
|
||||
0, 7, 0, 20, 0, 0, 0, 32, 0, 0, 0, 1, 0, 0, 0, 26, 0, 0, 0,
|
||||
22, 0, 0, 0, 31, 0, 0, 0, 65, 0, 5, 0, 35, 0, 0, 0, 36, 0, 0,
|
||||
0, 9, 0, 0, 0, 34, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 37, 0,
|
||||
0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 62, 0, 3, 0, 36, 0, 0, 0, 37,
|
||||
0, 0, 0, 65, 0, 5, 0, 35, 0, 0, 0, 39, 0, 0, 0, 9, 0, 0, 0,
|
||||
38, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 40, 0, 0, 0, 32, 0, 0,
|
||||
0, 1, 0, 0, 0, 62, 0, 3, 0, 39, 0, 0, 0, 40, 0, 0, 0, 65, 0,
|
||||
5, 0, 35, 0, 0, 0, 42, 0, 0, 0, 9, 0, 0, 0, 41, 0, 0, 0, 81,
|
||||
0, 5, 0, 6, 0, 0, 0, 43, 0, 0, 0, 32, 0, 0, 0, 2, 0, 0, 0,
|
||||
62, 0, 3, 0, 42, 0, 0, 0, 43, 0, 0, 0, 253, 0, 1, 0, 56, 0, 1,
|
||||
0};
|
||||
// Shader generated from shader.hlg.frag.spv
|
||||
const uint8_t shader_shader_hlg_frag[] = {
|
||||
3, 2, 35, 7, 0, 3, 1, 0, 10, 0, 13, 0, 164, 0, 0, 0, 0, 0, 0, 0,
|
||||
17, 0, 2, 0, 1, 0, 0, 0, 11, 0, 6, 0, 1, 0, 0, 0, 71, 76, 83, 76,
|
||||
46, 115, 116, 100, 46, 52, 53, 48, 0, 0, 0, 0, 14, 0, 3, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 15, 0, 7, 0, 4, 0, 0, 0, 4, 0, 0, 0, 109, 97, 105, 110,
|
||||
0, 0, 0, 0, 40, 0, 0, 0, 48, 0, 0, 0, 16, 0, 3, 0, 4, 0, 0, 0,
|
||||
7, 0, 0, 0, 71, 0, 4, 0, 40, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0,
|
||||
71, 0, 4, 0, 44, 0, 0, 0, 34, 0, 0, 0, 0, 0, 0, 0, 71, 0, 4, 0,
|
||||
44, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 71, 0, 4, 0, 48, 0, 0, 0,
|
||||
30, 0, 0, 0, 0, 0, 0, 0, 19, 0, 2, 0, 2, 0, 0, 0, 33, 0, 3, 0,
|
||||
3, 0, 0, 0, 2, 0, 0, 0, 22, 0, 3, 0, 6, 0, 0, 0, 32, 0, 0, 0,
|
||||
43, 0, 4, 0, 6, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 63, 20, 0, 2, 0,
|
||||
14, 0, 0, 0, 43, 0, 4, 0, 6, 0, 0, 0, 25, 0, 0, 0, 32, 192, 145, 62,
|
||||
43, 0, 4, 0, 6, 0, 0, 0, 27, 0, 0, 0, 79, 86, 15, 63, 23, 0, 4, 0,
|
||||
38, 0, 0, 0, 6, 0, 0, 0, 4, 0, 0, 0, 32, 0, 4, 0, 39, 0, 0, 0,
|
||||
3, 0, 0, 0, 38, 0, 0, 0, 59, 0, 4, 0, 39, 0, 0, 0, 40, 0, 0, 0,
|
||||
3, 0, 0, 0, 25, 0, 9, 0, 41, 0, 0, 0, 6, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
27, 0, 3, 0, 42, 0, 0, 0, 41, 0, 0, 0, 32, 0, 4, 0, 43, 0, 0, 0,
|
||||
0, 0, 0, 0, 42, 0, 0, 0, 59, 0, 4, 0, 43, 0, 0, 0, 44, 0, 0, 0,
|
||||
0, 0, 0, 0, 23, 0, 4, 0, 46, 0, 0, 0, 6, 0, 0, 0, 2, 0, 0, 0,
|
||||
32, 0, 4, 0, 47, 0, 0, 0, 1, 0, 0, 0, 46, 0, 0, 0, 59, 0, 4, 0,
|
||||
47, 0, 0, 0, 48, 0, 0, 0, 1, 0, 0, 0, 21, 0, 4, 0, 52, 0, 0, 0,
|
||||
32, 0, 0, 0, 0, 0, 0, 0, 43, 0, 4, 0, 52, 0, 0, 0, 53, 0, 0, 0,
|
||||
0, 0, 0, 0, 32, 0, 4, 0, 54, 0, 0, 0, 3, 0, 0, 0, 6, 0, 0, 0,
|
||||
43, 0, 4, 0, 52, 0, 0, 0, 59, 0, 0, 0, 1, 0, 0, 0, 43, 0, 4, 0,
|
||||
52, 0, 0, 0, 64, 0, 0, 0, 2, 0, 0, 0, 23, 0, 4, 0, 68, 0, 0, 0,
|
||||
6, 0, 0, 0, 3, 0, 0, 0, 43, 0, 4, 0, 6, 0, 0, 0, 78, 0, 0, 0,
|
||||
205, 204, 76, 63, 44, 0, 6, 0, 68, 0, 0, 0, 79, 0, 0, 0, 78, 0, 0, 0,
|
||||
78, 0, 0, 0, 78, 0, 0, 0, 43, 0, 4, 0, 6, 0, 0, 0, 89, 0, 0, 0,
|
||||
129, 128, 128, 61, 43, 0, 4, 0, 6, 0, 0, 0, 92, 0, 0, 0, 133, 10, 149, 63,
|
||||
44, 0, 6, 0, 68, 0, 0, 0, 160, 0, 0, 0, 89, 0, 0, 0, 89, 0, 0, 0,
|
||||
89, 0, 0, 0, 43, 0, 4, 0, 6, 0, 0, 0, 161, 0, 0, 0, 41, 240, 178, 64,
|
||||
43, 0, 4, 0, 6, 0, 0, 0, 162, 0, 0, 0, 171, 170, 170, 61, 43, 0, 4, 0,
|
||||
6, 0, 0, 0, 163, 0, 0, 0, 171, 170, 170, 62, 54, 0, 5, 0, 2, 0, 0, 0,
|
||||
4, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 248, 0, 2, 0, 5, 0, 0, 0,
|
||||
61, 0, 4, 0, 42, 0, 0, 0, 45, 0, 0, 0, 44, 0, 0, 0, 61, 0, 4, 0,
|
||||
46, 0, 0, 0, 49, 0, 0, 0, 48, 0, 0, 0, 87, 0, 5, 0, 38, 0, 0, 0,
|
||||
50, 0, 0, 0, 45, 0, 0, 0, 49, 0, 0, 0, 62, 0, 3, 0, 40, 0, 0, 0,
|
||||
50, 0, 0, 0, 65, 0, 5, 0, 54, 0, 0, 0, 55, 0, 0, 0, 40, 0, 0, 0,
|
||||
53, 0, 0, 0, 61, 0, 4, 0, 6, 0, 0, 0, 56, 0, 0, 0, 55, 0, 0, 0,
|
||||
188, 0, 5, 0, 14, 0, 0, 0, 104, 0, 0, 0, 56, 0, 0, 0, 13, 0, 0, 0,
|
||||
247, 0, 3, 0, 117, 0, 0, 0, 0, 0, 0, 0, 250, 0, 4, 0, 104, 0, 0, 0,
|
||||
105, 0, 0, 0, 110, 0, 0, 0, 248, 0, 2, 0, 105, 0, 0, 0, 133, 0, 5, 0,
|
||||
6, 0, 0, 0, 108, 0, 0, 0, 56, 0, 0, 0, 56, 0, 0, 0, 133, 0, 5, 0,
|
||||
6, 0, 0, 0, 109, 0, 0, 0, 108, 0, 0, 0, 163, 0, 0, 0, 249, 0, 2, 0,
|
||||
117, 0, 0, 0, 248, 0, 2, 0, 110, 0, 0, 0, 131, 0, 5, 0, 6, 0, 0, 0,
|
||||
112, 0, 0, 0, 56, 0, 0, 0, 27, 0, 0, 0, 133, 0, 5, 0, 6, 0, 0, 0,
|
||||
113, 0, 0, 0, 112, 0, 0, 0, 161, 0, 0, 0, 12, 0, 6, 0, 6, 0, 0, 0,
|
||||
114, 0, 0, 0, 1, 0, 0, 0, 27, 0, 0, 0, 113, 0, 0, 0, 129, 0, 5, 0,
|
||||
6, 0, 0, 0, 115, 0, 0, 0, 25, 0, 0, 0, 114, 0, 0, 0, 133, 0, 5, 0,
|
||||
6, 0, 0, 0, 116, 0, 0, 0, 115, 0, 0, 0, 162, 0, 0, 0, 249, 0, 2, 0,
|
||||
117, 0, 0, 0, 248, 0, 2, 0, 117, 0, 0, 0, 245, 0, 7, 0, 6, 0, 0, 0,
|
||||
157, 0, 0, 0, 109, 0, 0, 0, 105, 0, 0, 0, 116, 0, 0, 0, 110, 0, 0, 0,
|
||||
65, 0, 5, 0, 54, 0, 0, 0, 60, 0, 0, 0, 40, 0, 0, 0, 59, 0, 0, 0,
|
||||
61, 0, 4, 0, 6, 0, 0, 0, 61, 0, 0, 0, 60, 0, 0, 0, 188, 0, 5, 0,
|
||||
14, 0, 0, 0, 123, 0, 0, 0, 61, 0, 0, 0, 13, 0, 0, 0, 247, 0, 3, 0,
|
||||
136, 0, 0, 0, 0, 0, 0, 0, 250, 0, 4, 0, 123, 0, 0, 0, 124, 0, 0, 0,
|
||||
129, 0, 0, 0, 248, 0, 2, 0, 124, 0, 0, 0, 133, 0, 5, 0, 6, 0, 0, 0,
|
||||
127, 0, 0, 0, 61, 0, 0, 0, 61, 0, 0, 0, 133, 0, 5, 0, 6, 0, 0, 0,
|
||||
128, 0, 0, 0, 127, 0, 0, 0, 163, 0, 0, 0, 249, 0, 2, 0, 136, 0, 0, 0,
|
||||
248, 0, 2, 0, 129, 0, 0, 0, 131, 0, 5, 0, 6, 0, 0, 0, 131, 0, 0, 0,
|
||||
61, 0, 0, 0, 27, 0, 0, 0, 133, 0, 5, 0, 6, 0, 0, 0, 132, 0, 0, 0,
|
||||
131, 0, 0, 0, 161, 0, 0, 0, 12, 0, 6, 0, 6, 0, 0, 0, 133, 0, 0, 0,
|
||||
1, 0, 0, 0, 27, 0, 0, 0, 132, 0, 0, 0, 129, 0, 5, 0, 6, 0, 0, 0,
|
||||
134, 0, 0, 0, 25, 0, 0, 0, 133, 0, 0, 0, 133, 0, 5, 0, 6, 0, 0, 0,
|
||||
135, 0, 0, 0, 134, 0, 0, 0, 162, 0, 0, 0, 249, 0, 2, 0, 136, 0, 0, 0,
|
||||
248, 0, 2, 0, 136, 0, 0, 0, 245, 0, 7, 0, 6, 0, 0, 0, 158, 0, 0, 0,
|
||||
128, 0, 0, 0, 124, 0, 0, 0, 135, 0, 0, 0, 129, 0, 0, 0, 65, 0, 5, 0,
|
||||
54, 0, 0, 0, 65, 0, 0, 0, 40, 0, 0, 0, 64, 0, 0, 0, 61, 0, 4, 0,
|
||||
6, 0, 0, 0, 66, 0, 0, 0, 65, 0, 0, 0, 188, 0, 5, 0, 14, 0, 0, 0,
|
||||
142, 0, 0, 0, 66, 0, 0, 0, 13, 0, 0, 0, 247, 0, 3, 0, 155, 0, 0, 0,
|
||||
0, 0, 0, 0, 250, 0, 4, 0, 142, 0, 0, 0, 143, 0, 0, 0, 148, 0, 0, 0,
|
||||
248, 0, 2, 0, 143, 0, 0, 0, 133, 0, 5, 0, 6, 0, 0, 0, 146, 0, 0, 0,
|
||||
66, 0, 0, 0, 66, 0, 0, 0, 133, 0, 5, 0, 6, 0, 0, 0, 147, 0, 0, 0,
|
||||
146, 0, 0, 0, 163, 0, 0, 0, 249, 0, 2, 0, 155, 0, 0, 0, 248, 0, 2, 0,
|
||||
148, 0, 0, 0, 131, 0, 5, 0, 6, 0, 0, 0, 150, 0, 0, 0, 66, 0, 0, 0,
|
||||
27, 0, 0, 0, 133, 0, 5, 0, 6, 0, 0, 0, 151, 0, 0, 0, 150, 0, 0, 0,
|
||||
161, 0, 0, 0, 12, 0, 6, 0, 6, 0, 0, 0, 152, 0, 0, 0, 1, 0, 0, 0,
|
||||
27, 0, 0, 0, 151, 0, 0, 0, 129, 0, 5, 0, 6, 0, 0, 0, 153, 0, 0, 0,
|
||||
25, 0, 0, 0, 152, 0, 0, 0, 133, 0, 5, 0, 6, 0, 0, 0, 154, 0, 0, 0,
|
||||
153, 0, 0, 0, 162, 0, 0, 0, 249, 0, 2, 0, 155, 0, 0, 0, 248, 0, 2, 0,
|
||||
155, 0, 0, 0, 245, 0, 7, 0, 6, 0, 0, 0, 159, 0, 0, 0, 147, 0, 0, 0,
|
||||
143, 0, 0, 0, 154, 0, 0, 0, 148, 0, 0, 0, 62, 0, 3, 0, 55, 0, 0, 0,
|
||||
157, 0, 0, 0, 62, 0, 3, 0, 60, 0, 0, 0, 158, 0, 0, 0, 62, 0, 3, 0,
|
||||
65, 0, 0, 0, 159, 0, 0, 0, 61, 0, 4, 0, 38, 0, 0, 0, 76, 0, 0, 0,
|
||||
40, 0, 0, 0, 79, 0, 8, 0, 68, 0, 0, 0, 77, 0, 0, 0, 76, 0, 0, 0,
|
||||
76, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 12, 0, 7, 0,
|
||||
68, 0, 0, 0, 80, 0, 0, 0, 1, 0, 0, 0, 26, 0, 0, 0, 77, 0, 0, 0,
|
||||
79, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 82, 0, 0, 0, 80, 0, 0, 0,
|
||||
0, 0, 0, 0, 62, 0, 3, 0, 55, 0, 0, 0, 82, 0, 0, 0, 81, 0, 5, 0,
|
||||
6, 0, 0, 0, 84, 0, 0, 0, 80, 0, 0, 0, 1, 0, 0, 0, 62, 0, 3, 0,
|
||||
60, 0, 0, 0, 84, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 86, 0, 0, 0,
|
||||
80, 0, 0, 0, 2, 0, 0, 0, 62, 0, 3, 0, 65, 0, 0, 0, 86, 0, 0, 0,
|
||||
61, 0, 4, 0, 38, 0, 0, 0, 87, 0, 0, 0, 40, 0, 0, 0, 79, 0, 8, 0,
|
||||
68, 0, 0, 0, 88, 0, 0, 0, 87, 0, 0, 0, 87, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 2, 0, 0, 0, 131, 0, 5, 0, 68, 0, 0, 0, 91, 0, 0, 0,
|
||||
88, 0, 0, 0, 160, 0, 0, 0, 142, 0, 5, 0, 68, 0, 0, 0, 93, 0, 0, 0,
|
||||
91, 0, 0, 0, 92, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 95, 0, 0, 0,
|
||||
93, 0, 0, 0, 0, 0, 0, 0, 62, 0, 3, 0, 55, 0, 0, 0, 95, 0, 0, 0,
|
||||
81, 0, 5, 0, 6, 0, 0, 0, 97, 0, 0, 0, 93, 0, 0, 0, 1, 0, 0, 0,
|
||||
62, 0, 3, 0, 60, 0, 0, 0, 97, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0,
|
||||
99, 0, 0, 0, 93, 0, 0, 0, 2, 0, 0, 0, 62, 0, 3, 0, 65, 0, 0, 0,
|
||||
99, 0, 0, 0, 253, 0, 1, 0, 56, 0, 1, 0};
|
||||
// Shader generated from shader.linear.frag.spv
|
||||
const uint8_t shader_shader_linear_frag[] = {
|
||||
3, 2, 35, 7, 0, 3, 1, 0, 10, 0, 13, 0, 40, 0, 0, 0, 0, 0, 0, 0, 17,
|
||||
0, 2, 0, 1, 0, 0, 0, 11, 0, 6, 0, 1, 0, 0, 0, 71, 76, 83, 76, 46, 115,
|
||||
116, 100, 46, 52, 53, 48, 0, 0, 0, 0, 14, 0, 3, 0, 0, 0, 0, 0, 1, 0, 0,
|
||||
0, 15, 0, 7, 0, 4, 0, 0, 0, 4, 0, 0, 0, 109, 97, 105, 110, 0, 0, 0, 0,
|
||||
9, 0, 0, 0, 17, 0, 0, 0, 16, 0, 3, 0, 4, 0, 0, 0, 7, 0, 0, 0, 71,
|
||||
0, 4, 0, 9, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 71, 0, 4, 0, 13, 0,
|
||||
0, 0, 34, 0, 0, 0, 0, 0, 0, 0, 71, 0, 4, 0, 13, 0, 0, 0, 33, 0, 0,
|
||||
0, 0, 0, 0, 0, 71, 0, 4, 0, 17, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0,
|
||||
19, 0, 2, 0, 2, 0, 0, 0, 33, 0, 3, 0, 3, 0, 0, 0, 2, 0, 0, 0, 22,
|
||||
0, 3, 0, 6, 0, 0, 0, 32, 0, 0, 0, 23, 0, 4, 0, 7, 0, 0, 0, 6, 0,
|
||||
0, 0, 4, 0, 0, 0, 32, 0, 4, 0, 8, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0,
|
||||
0, 59, 0, 4, 0, 8, 0, 0, 0, 9, 0, 0, 0, 3, 0, 0, 0, 25, 0, 9, 0,
|
||||
10, 0, 0, 0, 6, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 27, 0, 3, 0, 11, 0, 0, 0, 10, 0,
|
||||
0, 0, 32, 0, 4, 0, 12, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 59, 0, 4,
|
||||
0, 12, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 23, 0, 4, 0, 15, 0, 0, 0,
|
||||
6, 0, 0, 0, 2, 0, 0, 0, 32, 0, 4, 0, 16, 0, 0, 0, 1, 0, 0, 0, 15,
|
||||
0, 0, 0, 59, 0, 4, 0, 16, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 23, 0,
|
||||
4, 0, 20, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 43, 0, 4, 0, 6, 0, 0,
|
||||
0, 23, 0, 0, 0, 129, 128, 128, 61, 43, 0, 4, 0, 6, 0, 0, 0, 26, 0, 0, 0,
|
||||
133, 10, 149, 63, 21, 0, 4, 0, 28, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 43,
|
||||
0, 4, 0, 28, 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, 0, 32, 0, 4, 0, 30, 0,
|
||||
0, 0, 3, 0, 0, 0, 6, 0, 0, 0, 43, 0, 4, 0, 28, 0, 0, 0, 33, 0, 0,
|
||||
0, 1, 0, 0, 0, 43, 0, 4, 0, 28, 0, 0, 0, 36, 0, 0, 0, 2, 0, 0, 0,
|
||||
44, 0, 6, 0, 20, 0, 0, 0, 39, 0, 0, 0, 23, 0, 0, 0, 23, 0, 0, 0, 23,
|
||||
0, 0, 0, 54, 0, 5, 0, 2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 0,
|
||||
0, 0, 248, 0, 2, 0, 5, 0, 0, 0, 61, 0, 4, 0, 11, 0, 0, 0, 14, 0, 0,
|
||||
0, 13, 0, 0, 0, 61, 0, 4, 0, 15, 0, 0, 0, 18, 0, 0, 0, 17, 0, 0, 0,
|
||||
87, 0, 5, 0, 7, 0, 0, 0, 19, 0, 0, 0, 14, 0, 0, 0, 18, 0, 0, 0, 62,
|
||||
0, 3, 0, 9, 0, 0, 0, 19, 0, 0, 0, 61, 0, 4, 0, 7, 0, 0, 0, 21, 0,
|
||||
0, 0, 9, 0, 0, 0, 79, 0, 8, 0, 20, 0, 0, 0, 22, 0, 0, 0, 21, 0, 0,
|
||||
0, 21, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 131, 0, 5, 0,
|
||||
20, 0, 0, 0, 25, 0, 0, 0, 22, 0, 0, 0, 39, 0, 0, 0, 142, 0, 5, 0, 20,
|
||||
0, 0, 0, 27, 0, 0, 0, 25, 0, 0, 0, 26, 0, 0, 0, 65, 0, 5, 0, 30, 0,
|
||||
0, 0, 31, 0, 0, 0, 9, 0, 0, 0, 29, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0,
|
||||
0, 32, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 0, 62, 0, 3, 0, 31, 0, 0, 0,
|
||||
32, 0, 0, 0, 65, 0, 5, 0, 30, 0, 0, 0, 34, 0, 0, 0, 9, 0, 0, 0, 33,
|
||||
0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 35, 0, 0, 0, 27, 0, 0, 0, 1, 0,
|
||||
0, 0, 62, 0, 3, 0, 34, 0, 0, 0, 35, 0, 0, 0, 65, 0, 5, 0, 30, 0, 0,
|
||||
0, 37, 0, 0, 0, 9, 0, 0, 0, 36, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0,
|
||||
38, 0, 0, 0, 27, 0, 0, 0, 2, 0, 0, 0, 62, 0, 3, 0, 37, 0, 0, 0, 38,
|
||||
0, 0, 0, 253, 0, 1, 0, 56, 0, 1, 0};
|
||||
// Shader generated from shader.rec.709.frag.spv
|
||||
const uint8_t shader_shader_rec_709_frag[] = {
|
||||
3, 2, 35, 7, 0, 3, 1, 0, 10, 0, 13, 0, 37, 0, 0, 0, 0, 0, 0, 0, 17, 0,
|
||||
2, 0, 1, 0, 0, 0, 11, 0, 6, 0, 1, 0, 0, 0, 71, 76, 83, 76, 46, 115, 116, 100,
|
||||
46, 52, 53, 48, 0, 0, 0, 0, 14, 0, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 15, 0,
|
||||
7, 0, 4, 0, 0, 0, 4, 0, 0, 0, 109, 97, 105, 110, 0, 0, 0, 0, 9, 0, 0, 0,
|
||||
17, 0, 0, 0, 16, 0, 3, 0, 4, 0, 0, 0, 7, 0, 0, 0, 71, 0, 3, 0, 9, 0,
|
||||
0, 0, 0, 0, 0, 0, 71, 0, 4, 0, 9, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0,
|
||||
71, 0, 4, 0, 13, 0, 0, 0, 34, 0, 0, 0, 0, 0, 0, 0, 71, 0, 4, 0, 13, 0,
|
||||
0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 71, 0, 3, 0, 17, 0, 0, 0, 0, 0, 0, 0,
|
||||
71, 0, 4, 0, 17, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 71, 0, 3, 0, 18, 0,
|
||||
0, 0, 0, 0, 0, 0, 71, 0, 3, 0, 21, 0, 0, 0, 0, 0, 0, 0, 71, 0, 3, 0,
|
||||
22, 0, 0, 0, 0, 0, 0, 0, 71, 0, 3, 0, 25, 0, 0, 0, 0, 0, 0, 0, 19, 0,
|
||||
2, 0, 2, 0, 0, 0, 33, 0, 3, 0, 3, 0, 0, 0, 2, 0, 0, 0, 22, 0, 3, 0,
|
||||
6, 0, 0, 0, 32, 0, 0, 0, 23, 0, 4, 0, 7, 0, 0, 0, 6, 0, 0, 0, 4, 0,
|
||||
0, 0, 32, 0, 4, 0, 8, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0, 59, 0, 4, 0,
|
||||
8, 0, 0, 0, 9, 0, 0, 0, 3, 0, 0, 0, 25, 0, 9, 0, 10, 0, 0, 0, 6, 0,
|
||||
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 27, 0, 3, 0, 11, 0, 0, 0, 10, 0, 0, 0, 32, 0, 4, 0, 12, 0,
|
||||
0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 59, 0, 4, 0, 12, 0, 0, 0, 13, 0, 0, 0,
|
||||
0, 0, 0, 0, 23, 0, 4, 0, 15, 0, 0, 0, 6, 0, 0, 0, 2, 0, 0, 0, 32, 0,
|
||||
4, 0, 16, 0, 0, 0, 1, 0, 0, 0, 15, 0, 0, 0, 59, 0, 4, 0, 16, 0, 0, 0,
|
||||
17, 0, 0, 0, 1, 0, 0, 0, 23, 0, 4, 0, 20, 0, 0, 0, 6, 0, 0, 0, 3, 0,
|
||||
0, 0, 43, 0, 4, 0, 6, 0, 0, 0, 23, 0, 0, 0, 145, 237, 4, 64, 44, 0, 6, 0,
|
||||
20, 0, 0, 0, 24, 0, 0, 0, 23, 0, 0, 0, 23, 0, 0, 0, 23, 0, 0, 0, 21, 0,
|
||||
4, 0, 26, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 43, 0, 4, 0, 26, 0, 0, 0,
|
||||
27, 0, 0, 0, 0, 0, 0, 0, 32, 0, 4, 0, 28, 0, 0, 0, 3, 0, 0, 0, 6, 0,
|
||||
0, 0, 43, 0, 4, 0, 26, 0, 0, 0, 31, 0, 0, 0, 1, 0, 0, 0, 43, 0, 4, 0,
|
||||
26, 0, 0, 0, 34, 0, 0, 0, 2, 0, 0, 0, 54, 0, 5, 0, 2, 0, 0, 0, 4, 0,
|
||||
0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 248, 0, 2, 0, 5, 0, 0, 0, 61, 0, 4, 0,
|
||||
11, 0, 0, 0, 14, 0, 0, 0, 13, 0, 0, 0, 61, 0, 4, 0, 15, 0, 0, 0, 18, 0,
|
||||
0, 0, 17, 0, 0, 0, 87, 0, 5, 0, 7, 0, 0, 0, 19, 0, 0, 0, 14, 0, 0, 0,
|
||||
18, 0, 0, 0, 62, 0, 3, 0, 9, 0, 0, 0, 19, 0, 0, 0, 61, 0, 4, 0, 7, 0,
|
||||
0, 0, 21, 0, 0, 0, 9, 0, 0, 0, 79, 0, 8, 0, 20, 0, 0, 0, 22, 0, 0, 0,
|
||||
21, 0, 0, 0, 21, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 12, 0,
|
||||
7, 0, 20, 0, 0, 0, 25, 0, 0, 0, 1, 0, 0, 0, 26, 0, 0, 0, 22, 0, 0, 0,
|
||||
24, 0, 0, 0, 65, 0, 5, 0, 28, 0, 0, 0, 29, 0, 0, 0, 9, 0, 0, 0, 27, 0,
|
||||
0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 30, 0, 0, 0, 25, 0, 0, 0, 0, 0, 0, 0,
|
||||
62, 0, 3, 0, 29, 0, 0, 0, 30, 0, 0, 0, 65, 0, 5, 0, 28, 0, 0, 0, 32, 0,
|
||||
0, 0, 9, 0, 0, 0, 31, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 33, 0, 0, 0,
|
||||
25, 0, 0, 0, 1, 0, 0, 0, 62, 0, 3, 0, 32, 0, 0, 0, 33, 0, 0, 0, 65, 0,
|
||||
5, 0, 28, 0, 0, 0, 35, 0, 0, 0, 9, 0, 0, 0, 34, 0, 0, 0, 81, 0, 5, 0,
|
||||
6, 0, 0, 0, 36, 0, 0, 0, 25, 0, 0, 0, 2, 0, 0, 0, 62, 0, 3, 0, 35, 0,
|
||||
0, 0, 36, 0, 0, 0, 253, 0, 1, 0, 56, 0, 1, 0};
|
||||
// Shader generated from shader.srgb.frag.spv
|
||||
const uint8_t shader_shader_srgb_frag[] = {
|
||||
3, 2, 35, 7, 0, 3, 1, 0, 10, 0, 13, 0, 150, 0, 0, 0, 0, 0, 0, 0,
|
||||
17, 0, 2, 0, 1, 0, 0, 0, 11, 0, 6, 0, 1, 0, 0, 0, 71, 76, 83, 76,
|
||||
46, 115, 116, 100, 46, 52, 53, 48, 0, 0, 0, 0, 14, 0, 3, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 15, 0, 7, 0, 4, 0, 0, 0, 4, 0, 0, 0, 109, 97, 105, 110,
|
||||
0, 0, 0, 0, 35, 0, 0, 0, 43, 0, 0, 0, 16, 0, 3, 0, 4, 0, 0, 0,
|
||||
7, 0, 0, 0, 71, 0, 4, 0, 35, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0,
|
||||
71, 0, 4, 0, 39, 0, 0, 0, 34, 0, 0, 0, 0, 0, 0, 0, 71, 0, 4, 0,
|
||||
39, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 71, 0, 4, 0, 43, 0, 0, 0,
|
||||
30, 0, 0, 0, 0, 0, 0, 0, 19, 0, 2, 0, 2, 0, 0, 0, 33, 0, 3, 0,
|
||||
3, 0, 0, 0, 2, 0, 0, 0, 22, 0, 3, 0, 6, 0, 0, 0, 32, 0, 0, 0,
|
||||
43, 0, 4, 0, 6, 0, 0, 0, 13, 0, 0, 0, 230, 174, 37, 61, 20, 0, 2, 0,
|
||||
14, 0, 0, 0, 43, 0, 4, 0, 6, 0, 0, 0, 24, 0, 0, 0, 174, 71, 97, 61,
|
||||
43, 0, 4, 0, 6, 0, 0, 0, 28, 0, 0, 0, 154, 153, 25, 64, 23, 0, 4, 0,
|
||||
33, 0, 0, 0, 6, 0, 0, 0, 4, 0, 0, 0, 32, 0, 4, 0, 34, 0, 0, 0,
|
||||
3, 0, 0, 0, 33, 0, 0, 0, 59, 0, 4, 0, 34, 0, 0, 0, 35, 0, 0, 0,
|
||||
3, 0, 0, 0, 25, 0, 9, 0, 36, 0, 0, 0, 6, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
27, 0, 3, 0, 37, 0, 0, 0, 36, 0, 0, 0, 32, 0, 4, 0, 38, 0, 0, 0,
|
||||
0, 0, 0, 0, 37, 0, 0, 0, 59, 0, 4, 0, 38, 0, 0, 0, 39, 0, 0, 0,
|
||||
0, 0, 0, 0, 23, 0, 4, 0, 41, 0, 0, 0, 6, 0, 0, 0, 2, 0, 0, 0,
|
||||
32, 0, 4, 0, 42, 0, 0, 0, 1, 0, 0, 0, 41, 0, 0, 0, 59, 0, 4, 0,
|
||||
42, 0, 0, 0, 43, 0, 0, 0, 1, 0, 0, 0, 23, 0, 4, 0, 46, 0, 0, 0,
|
||||
6, 0, 0, 0, 3, 0, 0, 0, 21, 0, 4, 0, 53, 0, 0, 0, 32, 0, 0, 0,
|
||||
0, 0, 0, 0, 43, 0, 4, 0, 53, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0,
|
||||
43, 0, 4, 0, 53, 0, 0, 0, 59, 0, 0, 0, 1, 0, 0, 0, 43, 0, 4, 0,
|
||||
53, 0, 0, 0, 64, 0, 0, 0, 2, 0, 0, 0, 32, 0, 4, 0, 74, 0, 0, 0,
|
||||
3, 0, 0, 0, 6, 0, 0, 0, 43, 0, 4, 0, 6, 0, 0, 0, 83, 0, 0, 0,
|
||||
129, 128, 128, 61, 43, 0, 4, 0, 6, 0, 0, 0, 86, 0, 0, 0, 133, 10, 149, 63,
|
||||
44, 0, 6, 0, 46, 0, 0, 0, 147, 0, 0, 0, 83, 0, 0, 0, 83, 0, 0, 0,
|
||||
83, 0, 0, 0, 43, 0, 4, 0, 6, 0, 0, 0, 148, 0, 0, 0, 111, 167, 114, 63,
|
||||
43, 0, 4, 0, 6, 0, 0, 0, 149, 0, 0, 0, 145, 131, 158, 61, 54, 0, 5, 0,
|
||||
2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 248, 0, 2, 0,
|
||||
5, 0, 0, 0, 61, 0, 4, 0, 37, 0, 0, 0, 40, 0, 0, 0, 39, 0, 0, 0,
|
||||
61, 0, 4, 0, 41, 0, 0, 0, 44, 0, 0, 0, 43, 0, 0, 0, 87, 0, 5, 0,
|
||||
33, 0, 0, 0, 45, 0, 0, 0, 40, 0, 0, 0, 44, 0, 0, 0, 62, 0, 3, 0,
|
||||
35, 0, 0, 0, 45, 0, 0, 0, 61, 0, 4, 0, 33, 0, 0, 0, 49, 0, 0, 0,
|
||||
35, 0, 0, 0, 79, 0, 8, 0, 46, 0, 0, 0, 50, 0, 0, 0, 49, 0, 0, 0,
|
||||
49, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 12, 0, 6, 0,
|
||||
46, 0, 0, 0, 51, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 50, 0, 0, 0,
|
||||
81, 0, 5, 0, 6, 0, 0, 0, 56, 0, 0, 0, 51, 0, 0, 0, 0, 0, 0, 0,
|
||||
188, 0, 5, 0, 14, 0, 0, 0, 98, 0, 0, 0, 56, 0, 0, 0, 13, 0, 0, 0,
|
||||
247, 0, 3, 0, 107, 0, 0, 0, 0, 0, 0, 0, 250, 0, 4, 0, 98, 0, 0, 0,
|
||||
99, 0, 0, 0, 102, 0, 0, 0, 248, 0, 2, 0, 99, 0, 0, 0, 133, 0, 5, 0,
|
||||
6, 0, 0, 0, 101, 0, 0, 0, 56, 0, 0, 0, 149, 0, 0, 0, 249, 0, 2, 0,
|
||||
107, 0, 0, 0, 248, 0, 2, 0, 102, 0, 0, 0, 129, 0, 5, 0, 6, 0, 0, 0,
|
||||
104, 0, 0, 0, 56, 0, 0, 0, 24, 0, 0, 0, 133, 0, 5, 0, 6, 0, 0, 0,
|
||||
105, 0, 0, 0, 104, 0, 0, 0, 148, 0, 0, 0, 12, 0, 7, 0, 6, 0, 0, 0,
|
||||
106, 0, 0, 0, 1, 0, 0, 0, 26, 0, 0, 0, 105, 0, 0, 0, 28, 0, 0, 0,
|
||||
249, 0, 2, 0, 107, 0, 0, 0, 248, 0, 2, 0, 107, 0, 0, 0, 245, 0, 7, 0,
|
||||
6, 0, 0, 0, 142, 0, 0, 0, 101, 0, 0, 0, 99, 0, 0, 0, 106, 0, 0, 0,
|
||||
102, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 61, 0, 0, 0, 51, 0, 0, 0,
|
||||
1, 0, 0, 0, 188, 0, 5, 0, 14, 0, 0, 0, 113, 0, 0, 0, 61, 0, 0, 0,
|
||||
13, 0, 0, 0, 247, 0, 3, 0, 122, 0, 0, 0, 0, 0, 0, 0, 250, 0, 4, 0,
|
||||
113, 0, 0, 0, 114, 0, 0, 0, 117, 0, 0, 0, 248, 0, 2, 0, 114, 0, 0, 0,
|
||||
133, 0, 5, 0, 6, 0, 0, 0, 116, 0, 0, 0, 61, 0, 0, 0, 149, 0, 0, 0,
|
||||
249, 0, 2, 0, 122, 0, 0, 0, 248, 0, 2, 0, 117, 0, 0, 0, 129, 0, 5, 0,
|
||||
6, 0, 0, 0, 119, 0, 0, 0, 61, 0, 0, 0, 24, 0, 0, 0, 133, 0, 5, 0,
|
||||
6, 0, 0, 0, 120, 0, 0, 0, 119, 0, 0, 0, 148, 0, 0, 0, 12, 0, 7, 0,
|
||||
6, 0, 0, 0, 121, 0, 0, 0, 1, 0, 0, 0, 26, 0, 0, 0, 120, 0, 0, 0,
|
||||
28, 0, 0, 0, 249, 0, 2, 0, 122, 0, 0, 0, 248, 0, 2, 0, 122, 0, 0, 0,
|
||||
245, 0, 7, 0, 6, 0, 0, 0, 144, 0, 0, 0, 116, 0, 0, 0, 114, 0, 0, 0,
|
||||
121, 0, 0, 0, 117, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 66, 0, 0, 0,
|
||||
51, 0, 0, 0, 2, 0, 0, 0, 188, 0, 5, 0, 14, 0, 0, 0, 128, 0, 0, 0,
|
||||
66, 0, 0, 0, 13, 0, 0, 0, 247, 0, 3, 0, 137, 0, 0, 0, 0, 0, 0, 0,
|
||||
250, 0, 4, 0, 128, 0, 0, 0, 129, 0, 0, 0, 132, 0, 0, 0, 248, 0, 2, 0,
|
||||
129, 0, 0, 0, 133, 0, 5, 0, 6, 0, 0, 0, 131, 0, 0, 0, 66, 0, 0, 0,
|
||||
149, 0, 0, 0, 249, 0, 2, 0, 137, 0, 0, 0, 248, 0, 2, 0, 132, 0, 0, 0,
|
||||
129, 0, 5, 0, 6, 0, 0, 0, 134, 0, 0, 0, 66, 0, 0, 0, 24, 0, 0, 0,
|
||||
133, 0, 5, 0, 6, 0, 0, 0, 135, 0, 0, 0, 134, 0, 0, 0, 148, 0, 0, 0,
|
||||
12, 0, 7, 0, 6, 0, 0, 0, 136, 0, 0, 0, 1, 0, 0, 0, 26, 0, 0, 0,
|
||||
135, 0, 0, 0, 28, 0, 0, 0, 249, 0, 2, 0, 137, 0, 0, 0, 248, 0, 2, 0,
|
||||
137, 0, 0, 0, 245, 0, 7, 0, 6, 0, 0, 0, 146, 0, 0, 0, 131, 0, 0, 0,
|
||||
129, 0, 0, 0, 136, 0, 0, 0, 132, 0, 0, 0, 80, 0, 6, 0, 46, 0, 0, 0,
|
||||
68, 0, 0, 0, 142, 0, 0, 0, 144, 0, 0, 0, 146, 0, 0, 0, 61, 0, 4, 0,
|
||||
33, 0, 0, 0, 69, 0, 0, 0, 35, 0, 0, 0, 79, 0, 8, 0, 46, 0, 0, 0,
|
||||
70, 0, 0, 0, 69, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
|
||||
2, 0, 0, 0, 12, 0, 6, 0, 46, 0, 0, 0, 71, 0, 0, 0, 1, 0, 0, 0,
|
||||
6, 0, 0, 0, 70, 0, 0, 0, 133, 0, 5, 0, 46, 0, 0, 0, 73, 0, 0, 0,
|
||||
71, 0, 0, 0, 68, 0, 0, 0, 65, 0, 5, 0, 74, 0, 0, 0, 75, 0, 0, 0,
|
||||
35, 0, 0, 0, 54, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 76, 0, 0, 0,
|
||||
73, 0, 0, 0, 0, 0, 0, 0, 62, 0, 3, 0, 75, 0, 0, 0, 76, 0, 0, 0,
|
||||
65, 0, 5, 0, 74, 0, 0, 0, 77, 0, 0, 0, 35, 0, 0, 0, 59, 0, 0, 0,
|
||||
81, 0, 5, 0, 6, 0, 0, 0, 78, 0, 0, 0, 73, 0, 0, 0, 1, 0, 0, 0,
|
||||
62, 0, 3, 0, 77, 0, 0, 0, 78, 0, 0, 0, 65, 0, 5, 0, 74, 0, 0, 0,
|
||||
79, 0, 0, 0, 35, 0, 0, 0, 64, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0,
|
||||
80, 0, 0, 0, 73, 0, 0, 0, 2, 0, 0, 0, 62, 0, 3, 0, 79, 0, 0, 0,
|
||||
80, 0, 0, 0, 61, 0, 4, 0, 33, 0, 0, 0, 81, 0, 0, 0, 35, 0, 0, 0,
|
||||
79, 0, 8, 0, 46, 0, 0, 0, 82, 0, 0, 0, 81, 0, 0, 0, 81, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 131, 0, 5, 0, 46, 0, 0, 0,
|
||||
85, 0, 0, 0, 82, 0, 0, 0, 147, 0, 0, 0, 142, 0, 5, 0, 46, 0, 0, 0,
|
||||
87, 0, 0, 0, 85, 0, 0, 0, 86, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0,
|
||||
89, 0, 0, 0, 87, 0, 0, 0, 0, 0, 0, 0, 62, 0, 3, 0, 75, 0, 0, 0,
|
||||
89, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 91, 0, 0, 0, 87, 0, 0, 0,
|
||||
1, 0, 0, 0, 62, 0, 3, 0, 77, 0, 0, 0, 91, 0, 0, 0, 81, 0, 5, 0,
|
||||
6, 0, 0, 0, 93, 0, 0, 0, 87, 0, 0, 0, 2, 0, 0, 0, 62, 0, 3, 0,
|
||||
79, 0, 0, 0, 93, 0, 0, 0, 253, 0, 1, 0, 56, 0, 1, 0};
|
||||
// Shader generated from shader.st2084.frag.spv
|
||||
const uint8_t shader_shader_st2084_frag[] = {
|
||||
3, 2, 35, 7, 0, 3, 1, 0, 10, 0, 13, 0, 118, 0, 0, 0, 0, 0, 0, 0,
|
||||
17, 0, 2, 0, 1, 0, 0, 0, 11, 0, 6, 0, 1, 0, 0, 0, 71, 76, 83, 76,
|
||||
46, 115, 116, 100, 46, 52, 53, 48, 0, 0, 0, 0, 14, 0, 3, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 15, 0, 7, 0, 4, 0, 0, 0, 4, 0, 0, 0, 109, 97, 105, 110,
|
||||
0, 0, 0, 0, 9, 0, 0, 0, 17, 0, 0, 0, 16, 0, 3, 0, 4, 0, 0, 0,
|
||||
7, 0, 0, 0, 71, 0, 4, 0, 9, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0,
|
||||
71, 0, 4, 0, 13, 0, 0, 0, 34, 0, 0, 0, 0, 0, 0, 0, 71, 0, 4, 0,
|
||||
13, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 71, 0, 4, 0, 17, 0, 0, 0,
|
||||
30, 0, 0, 0, 0, 0, 0, 0, 19, 0, 2, 0, 2, 0, 0, 0, 33, 0, 3, 0,
|
||||
3, 0, 0, 0, 2, 0, 0, 0, 22, 0, 3, 0, 6, 0, 0, 0, 32, 0, 0, 0,
|
||||
23, 0, 4, 0, 7, 0, 0, 0, 6, 0, 0, 0, 4, 0, 0, 0, 32, 0, 4, 0,
|
||||
8, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0, 59, 0, 4, 0, 8, 0, 0, 0,
|
||||
9, 0, 0, 0, 3, 0, 0, 0, 25, 0, 9, 0, 10, 0, 0, 0, 6, 0, 0, 0,
|
||||
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 27, 0, 3, 0, 11, 0, 0, 0, 10, 0, 0, 0, 32, 0, 4, 0,
|
||||
12, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 59, 0, 4, 0, 12, 0, 0, 0,
|
||||
13, 0, 0, 0, 0, 0, 0, 0, 23, 0, 4, 0, 15, 0, 0, 0, 6, 0, 0, 0,
|
||||
2, 0, 0, 0, 32, 0, 4, 0, 16, 0, 0, 0, 1, 0, 0, 0, 15, 0, 0, 0,
|
||||
59, 0, 4, 0, 16, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 23, 0, 4, 0,
|
||||
20, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 43, 0, 4, 0, 6, 0, 0, 0,
|
||||
23, 0, 0, 0, 172, 205, 79, 60, 44, 0, 6, 0, 20, 0, 0, 0, 24, 0, 0, 0,
|
||||
23, 0, 0, 0, 23, 0, 0, 0, 23, 0, 0, 0, 21, 0, 4, 0, 26, 0, 0, 0,
|
||||
32, 0, 0, 0, 0, 0, 0, 0, 43, 0, 4, 0, 26, 0, 0, 0, 27, 0, 0, 0,
|
||||
0, 0, 0, 0, 32, 0, 4, 0, 28, 0, 0, 0, 3, 0, 0, 0, 6, 0, 0, 0,
|
||||
43, 0, 4, 0, 26, 0, 0, 0, 31, 0, 0, 0, 1, 0, 0, 0, 43, 0, 4, 0,
|
||||
26, 0, 0, 0, 34, 0, 0, 0, 2, 0, 0, 0, 43, 0, 4, 0, 6, 0, 0, 0,
|
||||
39, 0, 0, 0, 0, 0, 86, 63, 44, 0, 6, 0, 20, 0, 0, 0, 40, 0, 0, 0,
|
||||
39, 0, 0, 0, 39, 0, 0, 0, 39, 0, 0, 0, 43, 0, 4, 0, 6, 0, 0, 0,
|
||||
42, 0, 0, 0, 0, 0, 0, 0, 44, 0, 6, 0, 20, 0, 0, 0, 43, 0, 0, 0,
|
||||
42, 0, 0, 0, 42, 0, 0, 0, 42, 0, 0, 0, 43, 0, 4, 0, 6, 0, 0, 0,
|
||||
45, 0, 0, 0, 0, 208, 150, 65, 44, 0, 6, 0, 20, 0, 0, 0, 46, 0, 0, 0,
|
||||
45, 0, 0, 0, 45, 0, 0, 0, 45, 0, 0, 0, 43, 0, 4, 0, 6, 0, 0, 0,
|
||||
47, 0, 0, 0, 0, 128, 149, 65, 44, 0, 6, 0, 20, 0, 0, 0, 48, 0, 0, 0,
|
||||
47, 0, 0, 0, 47, 0, 0, 0, 47, 0, 0, 0, 43, 0, 4, 0, 6, 0, 0, 0,
|
||||
62, 0, 0, 0, 107, 224, 200, 64, 44, 0, 6, 0, 20, 0, 0, 0, 63, 0, 0, 0,
|
||||
62, 0, 0, 0, 62, 0, 0, 0, 62, 0, 0, 0, 20, 0, 2, 0, 82, 0, 0, 0,
|
||||
43, 0, 4, 0, 6, 0, 0, 0, 89, 0, 0, 0, 166, 155, 68, 59, 43, 0, 4, 0,
|
||||
6, 0, 0, 0, 106, 0, 0, 0, 129, 128, 128, 61, 43, 0, 4, 0, 6, 0, 0, 0,
|
||||
109, 0, 0, 0, 133, 10, 149, 63, 44, 0, 6, 0, 20, 0, 0, 0, 117, 0, 0, 0,
|
||||
106, 0, 0, 0, 106, 0, 0, 0, 106, 0, 0, 0, 54, 0, 5, 0, 2, 0, 0, 0,
|
||||
4, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 248, 0, 2, 0, 5, 0, 0, 0,
|
||||
61, 0, 4, 0, 11, 0, 0, 0, 14, 0, 0, 0, 13, 0, 0, 0, 61, 0, 4, 0,
|
||||
15, 0, 0, 0, 18, 0, 0, 0, 17, 0, 0, 0, 87, 0, 5, 0, 7, 0, 0, 0,
|
||||
19, 0, 0, 0, 14, 0, 0, 0, 18, 0, 0, 0, 62, 0, 3, 0, 9, 0, 0, 0,
|
||||
19, 0, 0, 0, 61, 0, 4, 0, 7, 0, 0, 0, 21, 0, 0, 0, 9, 0, 0, 0,
|
||||
79, 0, 8, 0, 20, 0, 0, 0, 22, 0, 0, 0, 21, 0, 0, 0, 21, 0, 0, 0,
|
||||
0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 12, 0, 7, 0, 20, 0, 0, 0,
|
||||
25, 0, 0, 0, 1, 0, 0, 0, 26, 0, 0, 0, 22, 0, 0, 0, 24, 0, 0, 0,
|
||||
65, 0, 5, 0, 28, 0, 0, 0, 29, 0, 0, 0, 9, 0, 0, 0, 27, 0, 0, 0,
|
||||
81, 0, 5, 0, 6, 0, 0, 0, 30, 0, 0, 0, 25, 0, 0, 0, 0, 0, 0, 0,
|
||||
62, 0, 3, 0, 29, 0, 0, 0, 30, 0, 0, 0, 65, 0, 5, 0, 28, 0, 0, 0,
|
||||
32, 0, 0, 0, 9, 0, 0, 0, 31, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0,
|
||||
33, 0, 0, 0, 25, 0, 0, 0, 1, 0, 0, 0, 62, 0, 3, 0, 32, 0, 0, 0,
|
||||
33, 0, 0, 0, 65, 0, 5, 0, 28, 0, 0, 0, 35, 0, 0, 0, 9, 0, 0, 0,
|
||||
34, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 36, 0, 0, 0, 25, 0, 0, 0,
|
||||
2, 0, 0, 0, 62, 0, 3, 0, 35, 0, 0, 0, 36, 0, 0, 0, 61, 0, 4, 0,
|
||||
7, 0, 0, 0, 37, 0, 0, 0, 9, 0, 0, 0, 79, 0, 8, 0, 20, 0, 0, 0,
|
||||
38, 0, 0, 0, 37, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
|
||||
2, 0, 0, 0, 131, 0, 5, 0, 20, 0, 0, 0, 41, 0, 0, 0, 38, 0, 0, 0,
|
||||
40, 0, 0, 0, 12, 0, 7, 0, 20, 0, 0, 0, 44, 0, 0, 0, 1, 0, 0, 0,
|
||||
40, 0, 0, 0, 41, 0, 0, 0, 43, 0, 0, 0, 61, 0, 4, 0, 7, 0, 0, 0,
|
||||
49, 0, 0, 0, 9, 0, 0, 0, 79, 0, 8, 0, 20, 0, 0, 0, 50, 0, 0, 0,
|
||||
49, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0,
|
||||
133, 0, 5, 0, 20, 0, 0, 0, 51, 0, 0, 0, 48, 0, 0, 0, 50, 0, 0, 0,
|
||||
131, 0, 5, 0, 20, 0, 0, 0, 52, 0, 0, 0, 46, 0, 0, 0, 51, 0, 0, 0,
|
||||
136, 0, 5, 0, 20, 0, 0, 0, 53, 0, 0, 0, 44, 0, 0, 0, 52, 0, 0, 0,
|
||||
81, 0, 5, 0, 6, 0, 0, 0, 55, 0, 0, 0, 53, 0, 0, 0, 0, 0, 0, 0,
|
||||
62, 0, 3, 0, 29, 0, 0, 0, 55, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0,
|
||||
57, 0, 0, 0, 53, 0, 0, 0, 1, 0, 0, 0, 62, 0, 3, 0, 32, 0, 0, 0,
|
||||
57, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 59, 0, 0, 0, 53, 0, 0, 0,
|
||||
2, 0, 0, 0, 62, 0, 3, 0, 35, 0, 0, 0, 59, 0, 0, 0, 61, 0, 4, 0,
|
||||
7, 0, 0, 0, 60, 0, 0, 0, 9, 0, 0, 0, 79, 0, 8, 0, 20, 0, 0, 0,
|
||||
61, 0, 0, 0, 60, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
|
||||
2, 0, 0, 0, 12, 0, 7, 0, 20, 0, 0, 0, 64, 0, 0, 0, 1, 0, 0, 0,
|
||||
26, 0, 0, 0, 61, 0, 0, 0, 63, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0,
|
||||
66, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 62, 0, 3, 0, 29, 0, 0, 0,
|
||||
66, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 68, 0, 0, 0, 64, 0, 0, 0,
|
||||
1, 0, 0, 0, 62, 0, 3, 0, 32, 0, 0, 0, 68, 0, 0, 0, 81, 0, 5, 0,
|
||||
6, 0, 0, 0, 70, 0, 0, 0, 64, 0, 0, 0, 2, 0, 0, 0, 62, 0, 3, 0,
|
||||
35, 0, 0, 0, 70, 0, 0, 0, 61, 0, 4, 0, 6, 0, 0, 0, 74, 0, 0, 0,
|
||||
29, 0, 0, 0, 61, 0, 4, 0, 6, 0, 0, 0, 76, 0, 0, 0, 32, 0, 0, 0,
|
||||
61, 0, 4, 0, 6, 0, 0, 0, 78, 0, 0, 0, 35, 0, 0, 0, 12, 0, 7, 0,
|
||||
6, 0, 0, 0, 79, 0, 0, 0, 1, 0, 0, 0, 40, 0, 0, 0, 76, 0, 0, 0,
|
||||
78, 0, 0, 0, 12, 0, 7, 0, 6, 0, 0, 0, 80, 0, 0, 0, 1, 0, 0, 0,
|
||||
40, 0, 0, 0, 74, 0, 0, 0, 79, 0, 0, 0, 186, 0, 5, 0, 82, 0, 0, 0,
|
||||
83, 0, 0, 0, 80, 0, 0, 0, 42, 0, 0, 0, 247, 0, 3, 0, 85, 0, 0, 0,
|
||||
0, 0, 0, 0, 250, 0, 4, 0, 83, 0, 0, 0, 84, 0, 0, 0, 85, 0, 0, 0,
|
||||
248, 0, 2, 0, 84, 0, 0, 0, 129, 0, 5, 0, 6, 0, 0, 0, 90, 0, 0, 0,
|
||||
80, 0, 0, 0, 89, 0, 0, 0, 136, 0, 5, 0, 6, 0, 0, 0, 91, 0, 0, 0,
|
||||
80, 0, 0, 0, 90, 0, 0, 0, 136, 0, 5, 0, 6, 0, 0, 0, 94, 0, 0, 0,
|
||||
91, 0, 0, 0, 80, 0, 0, 0, 61, 0, 4, 0, 7, 0, 0, 0, 95, 0, 0, 0,
|
||||
9, 0, 0, 0, 79, 0, 8, 0, 20, 0, 0, 0, 96, 0, 0, 0, 95, 0, 0, 0,
|
||||
95, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 142, 0, 5, 0,
|
||||
20, 0, 0, 0, 97, 0, 0, 0, 96, 0, 0, 0, 94, 0, 0, 0, 81, 0, 5, 0,
|
||||
6, 0, 0, 0, 99, 0, 0, 0, 97, 0, 0, 0, 0, 0, 0, 0, 62, 0, 3, 0,
|
||||
29, 0, 0, 0, 99, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 101, 0, 0, 0,
|
||||
97, 0, 0, 0, 1, 0, 0, 0, 62, 0, 3, 0, 32, 0, 0, 0, 101, 0, 0, 0,
|
||||
81, 0, 5, 0, 6, 0, 0, 0, 103, 0, 0, 0, 97, 0, 0, 0, 2, 0, 0, 0,
|
||||
62, 0, 3, 0, 35, 0, 0, 0, 103, 0, 0, 0, 249, 0, 2, 0, 85, 0, 0, 0,
|
||||
248, 0, 2, 0, 85, 0, 0, 0, 61, 0, 4, 0, 7, 0, 0, 0, 104, 0, 0, 0,
|
||||
9, 0, 0, 0, 79, 0, 8, 0, 20, 0, 0, 0, 105, 0, 0, 0, 104, 0, 0, 0,
|
||||
104, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 131, 0, 5, 0,
|
||||
20, 0, 0, 0, 108, 0, 0, 0, 105, 0, 0, 0, 117, 0, 0, 0, 142, 0, 5, 0,
|
||||
20, 0, 0, 0, 110, 0, 0, 0, 108, 0, 0, 0, 109, 0, 0, 0, 81, 0, 5, 0,
|
||||
6, 0, 0, 0, 112, 0, 0, 0, 110, 0, 0, 0, 0, 0, 0, 0, 62, 0, 3, 0,
|
||||
29, 0, 0, 0, 112, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 114, 0, 0, 0,
|
||||
110, 0, 0, 0, 1, 0, 0, 0, 62, 0, 3, 0, 32, 0, 0, 0, 114, 0, 0, 0,
|
||||
81, 0, 5, 0, 6, 0, 0, 0, 116, 0, 0, 0, 110, 0, 0, 0, 2, 0, 0, 0,
|
||||
62, 0, 3, 0, 35, 0, 0, 0, 116, 0, 0, 0, 253, 0, 1, 0, 56, 0, 1, 0};
|
||||
// Shader generated from shader.test.frag.spv
|
||||
const uint8_t shader_shader_test_frag[] = {
|
||||
3, 2, 35, 7, 0, 3, 1, 0, 10, 0, 13, 0, 31, 0, 0, 0, 0, 0, 0, 0, 17, 0,
|
||||
2, 0, 1, 0, 0, 0, 11, 0, 6, 0, 1, 0, 0, 0, 71, 76, 83, 76, 46, 115, 116, 100,
|
||||
46, 52, 53, 48, 0, 0, 0, 0, 14, 0, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 15, 0,
|
||||
7, 0, 4, 0, 0, 0, 4, 0, 0, 0, 109, 97, 105, 110, 0, 0, 0, 0, 9, 0, 0, 0,
|
||||
12, 0, 0, 0, 16, 0, 3, 0, 4, 0, 0, 0, 7, 0, 0, 0, 71, 0, 4, 0, 9, 0,
|
||||
0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 71, 0, 4, 0, 12, 0, 0, 0, 30, 0, 0, 0,
|
||||
0, 0, 0, 0, 19, 0, 2, 0, 2, 0, 0, 0, 33, 0, 3, 0, 3, 0, 0, 0, 2, 0,
|
||||
0, 0, 22, 0, 3, 0, 6, 0, 0, 0, 32, 0, 0, 0, 23, 0, 4, 0, 7, 0, 0, 0,
|
||||
6, 0, 0, 0, 4, 0, 0, 0, 32, 0, 4, 0, 8, 0, 0, 0, 3, 0, 0, 0, 7, 0,
|
||||
0, 0, 59, 0, 4, 0, 8, 0, 0, 0, 9, 0, 0, 0, 3, 0, 0, 0, 23, 0, 4, 0,
|
||||
10, 0, 0, 0, 6, 0, 0, 0, 2, 0, 0, 0, 32, 0, 4, 0, 11, 0, 0, 0, 1, 0,
|
||||
0, 0, 10, 0, 0, 0, 59, 0, 4, 0, 11, 0, 0, 0, 12, 0, 0, 0, 1, 0, 0, 0,
|
||||
21, 0, 4, 0, 13, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 43, 0, 4, 0, 13, 0,
|
||||
0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 32, 0, 4, 0, 15, 0, 0, 0, 1, 0, 0, 0,
|
||||
6, 0, 0, 0, 43, 0, 4, 0, 13, 0, 0, 0, 18, 0, 0, 0, 1, 0, 0, 0, 43, 0,
|
||||
4, 0, 6, 0, 0, 0, 21, 0, 0, 0, 0, 0, 128, 63, 32, 0, 4, 0, 24, 0, 0, 0,
|
||||
3, 0, 0, 0, 6, 0, 0, 0, 43, 0, 4, 0, 13, 0, 0, 0, 29, 0, 0, 0, 2, 0,
|
||||
0, 0, 54, 0, 5, 0, 2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0,
|
||||
248, 0, 2, 0, 5, 0, 0, 0, 65, 0, 5, 0, 15, 0, 0, 0, 16, 0, 0, 0, 12, 0,
|
||||
0, 0, 14, 0, 0, 0, 61, 0, 4, 0, 6, 0, 0, 0, 17, 0, 0, 0, 16, 0, 0, 0,
|
||||
65, 0, 5, 0, 15, 0, 0, 0, 19, 0, 0, 0, 12, 0, 0, 0, 18, 0, 0, 0, 61, 0,
|
||||
4, 0, 6, 0, 0, 0, 20, 0, 0, 0, 19, 0, 0, 0, 65, 0, 5, 0, 24, 0, 0, 0,
|
||||
25, 0, 0, 0, 9, 0, 0, 0, 14, 0, 0, 0, 62, 0, 3, 0, 25, 0, 0, 0, 17, 0,
|
||||
0, 0, 65, 0, 5, 0, 24, 0, 0, 0, 27, 0, 0, 0, 9, 0, 0, 0, 18, 0, 0, 0,
|
||||
62, 0, 3, 0, 27, 0, 0, 0, 20, 0, 0, 0, 65, 0, 5, 0, 24, 0, 0, 0, 30, 0,
|
||||
0, 0, 9, 0, 0, 0, 29, 0, 0, 0, 62, 0, 3, 0, 30, 0, 0, 0, 21, 0, 0, 0,
|
||||
253, 0, 1, 0, 56, 0, 1, 0};
|
||||
// Shader generated from shader.vert.spv
|
||||
const uint8_t shader_shader_vert[] = {
|
||||
3, 2, 35, 7, 0, 3, 1, 0, 10, 0, 13, 0, 73, 0, 0, 0, 0, 0, 0, 0,
|
||||
17, 0, 2, 0, 1, 0, 0, 0, 11, 0, 6, 0, 1, 0, 0, 0, 71, 76, 83, 76,
|
||||
46, 115, 116, 100, 46, 52, 53, 48, 0, 0, 0, 0, 14, 0, 3, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 15, 0, 8, 0, 0, 0, 0, 0, 4, 0, 0, 0, 109, 97, 105, 110,
|
||||
0, 0, 0, 0, 10, 0, 0, 0, 54, 0, 0, 0, 65, 0, 0, 0, 71, 0, 4, 0,
|
||||
10, 0, 0, 0, 11, 0, 0, 0, 42, 0, 0, 0, 72, 0, 5, 0, 52, 0, 0, 0,
|
||||
0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 72, 0, 5, 0, 52, 0, 0, 0,
|
||||
1, 0, 0, 0, 11, 0, 0, 0, 1, 0, 0, 0, 72, 0, 5, 0, 52, 0, 0, 0,
|
||||
2, 0, 0, 0, 11, 0, 0, 0, 3, 0, 0, 0, 72, 0, 5, 0, 52, 0, 0, 0,
|
||||
3, 0, 0, 0, 11, 0, 0, 0, 4, 0, 0, 0, 71, 0, 3, 0, 52, 0, 0, 0,
|
||||
2, 0, 0, 0, 71, 0, 4, 0, 65, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0,
|
||||
19, 0, 2, 0, 2, 0, 0, 0, 33, 0, 3, 0, 3, 0, 0, 0, 2, 0, 0, 0,
|
||||
21, 0, 4, 0, 6, 0, 0, 0, 32, 0, 0, 0, 1, 0, 0, 0, 32, 0, 4, 0,
|
||||
9, 0, 0, 0, 1, 0, 0, 0, 6, 0, 0, 0, 59, 0, 4, 0, 9, 0, 0, 0,
|
||||
10, 0, 0, 0, 1, 0, 0, 0, 43, 0, 4, 0, 6, 0, 0, 0, 12, 0, 0, 0,
|
||||
1, 0, 0, 0, 43, 0, 4, 0, 6, 0, 0, 0, 17, 0, 0, 0, 15, 0, 0, 0,
|
||||
43, 0, 4, 0, 6, 0, 0, 0, 21, 0, 0, 0, 5, 0, 0, 0, 43, 0, 4, 0,
|
||||
6, 0, 0, 0, 24, 0, 0, 0, 2, 0, 0, 0, 22, 0, 3, 0, 29, 0, 0, 0,
|
||||
32, 0, 0, 0, 43, 0, 4, 0, 29, 0, 0, 0, 32, 0, 0, 0, 0, 0, 128, 191,
|
||||
43, 0, 4, 0, 29, 0, 0, 0, 33, 0, 0, 0, 134, 136, 8, 62, 23, 0, 4, 0,
|
||||
48, 0, 0, 0, 29, 0, 0, 0, 4, 0, 0, 0, 21, 0, 4, 0, 49, 0, 0, 0,
|
||||
32, 0, 0, 0, 0, 0, 0, 0, 43, 0, 4, 0, 49, 0, 0, 0, 50, 0, 0, 0,
|
||||
1, 0, 0, 0, 28, 0, 4, 0, 51, 0, 0, 0, 29, 0, 0, 0, 50, 0, 0, 0,
|
||||
30, 0, 6, 0, 52, 0, 0, 0, 48, 0, 0, 0, 29, 0, 0, 0, 51, 0, 0, 0,
|
||||
51, 0, 0, 0, 32, 0, 4, 0, 53, 0, 0, 0, 3, 0, 0, 0, 52, 0, 0, 0,
|
||||
59, 0, 4, 0, 53, 0, 0, 0, 54, 0, 0, 0, 3, 0, 0, 0, 43, 0, 4, 0,
|
||||
6, 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, 0, 43, 0, 4, 0, 29, 0, 0, 0,
|
||||
58, 0, 0, 0, 0, 0, 0, 0, 43, 0, 4, 0, 29, 0, 0, 0, 59, 0, 0, 0,
|
||||
0, 0, 128, 63, 32, 0, 4, 0, 61, 0, 0, 0, 3, 0, 0, 0, 48, 0, 0, 0,
|
||||
23, 0, 4, 0, 63, 0, 0, 0, 29, 0, 0, 0, 2, 0, 0, 0, 32, 0, 4, 0,
|
||||
64, 0, 0, 0, 3, 0, 0, 0, 63, 0, 0, 0, 59, 0, 4, 0, 64, 0, 0, 0,
|
||||
65, 0, 0, 0, 3, 0, 0, 0, 43, 0, 4, 0, 29, 0, 0, 0, 66, 0, 0, 0,
|
||||
0, 0, 0, 63, 44, 0, 5, 0, 63, 0, 0, 0, 70, 0, 0, 0, 59, 0, 0, 0,
|
||||
59, 0, 0, 0, 54, 0, 5, 0, 2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0,
|
||||
3, 0, 0, 0, 248, 0, 2, 0, 5, 0, 0, 0, 61, 0, 4, 0, 6, 0, 0, 0,
|
||||
11, 0, 0, 0, 10, 0, 0, 0, 199, 0, 5, 0, 6, 0, 0, 0, 13, 0, 0, 0,
|
||||
11, 0, 0, 0, 12, 0, 0, 0, 195, 0, 5, 0, 6, 0, 0, 0, 16, 0, 0, 0,
|
||||
11, 0, 0, 0, 12, 0, 0, 0, 199, 0, 5, 0, 6, 0, 0, 0, 18, 0, 0, 0,
|
||||
16, 0, 0, 0, 17, 0, 0, 0, 195, 0, 5, 0, 6, 0, 0, 0, 22, 0, 0, 0,
|
||||
11, 0, 0, 0, 21, 0, 0, 0, 199, 0, 5, 0, 6, 0, 0, 0, 26, 0, 0, 0,
|
||||
22, 0, 0, 0, 12, 0, 0, 0, 132, 0, 5, 0, 6, 0, 0, 0, 27, 0, 0, 0,
|
||||
24, 0, 0, 0, 26, 0, 0, 0, 130, 0, 5, 0, 6, 0, 0, 0, 28, 0, 0, 0,
|
||||
12, 0, 0, 0, 27, 0, 0, 0, 111, 0, 4, 0, 29, 0, 0, 0, 35, 0, 0, 0,
|
||||
18, 0, 0, 0, 133, 0, 5, 0, 29, 0, 0, 0, 36, 0, 0, 0, 33, 0, 0, 0,
|
||||
35, 0, 0, 0, 129, 0, 5, 0, 29, 0, 0, 0, 37, 0, 0, 0, 32, 0, 0, 0,
|
||||
36, 0, 0, 0, 111, 0, 4, 0, 29, 0, 0, 0, 39, 0, 0, 0, 28, 0, 0, 0,
|
||||
133, 0, 5, 0, 29, 0, 0, 0, 40, 0, 0, 0, 37, 0, 0, 0, 39, 0, 0, 0,
|
||||
128, 0, 5, 0, 6, 0, 0, 0, 44, 0, 0, 0, 22, 0, 0, 0, 13, 0, 0, 0,
|
||||
111, 0, 4, 0, 29, 0, 0, 0, 45, 0, 0, 0, 44, 0, 0, 0, 133, 0, 5, 0,
|
||||
29, 0, 0, 0, 46, 0, 0, 0, 33, 0, 0, 0, 45, 0, 0, 0, 129, 0, 5, 0,
|
||||
29, 0, 0, 0, 47, 0, 0, 0, 32, 0, 0, 0, 46, 0, 0, 0, 80, 0, 7, 0,
|
||||
48, 0, 0, 0, 60, 0, 0, 0, 40, 0, 0, 0, 47, 0, 0, 0, 58, 0, 0, 0,
|
||||
59, 0, 0, 0, 65, 0, 5, 0, 61, 0, 0, 0, 62, 0, 0, 0, 54, 0, 0, 0,
|
||||
55, 0, 0, 0, 62, 0, 3, 0, 62, 0, 0, 0, 60, 0, 0, 0, 61, 0, 4, 0,
|
||||
48, 0, 0, 0, 68, 0, 0, 0, 62, 0, 0, 0, 79, 0, 7, 0, 63, 0, 0, 0,
|
||||
69, 0, 0, 0, 68, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
|
||||
129, 0, 5, 0, 63, 0, 0, 0, 71, 0, 0, 0, 69, 0, 0, 0, 70, 0, 0, 0,
|
||||
142, 0, 5, 0, 63, 0, 0, 0, 72, 0, 0, 0, 71, 0, 0, 0, 66, 0, 0, 0,
|
||||
62, 0, 3, 0, 65, 0, 0, 0, 72, 0, 0, 0, 253, 0, 1, 0, 56, 0, 1, 0};
|
||||
// Shader generated from shader_mesh.vert.spv
|
||||
const uint8_t shader_shader_mesh_vert[] = {
|
||||
3, 2, 35, 7, 0, 3, 1, 0, 10, 0, 13, 0, 69, 0, 0, 0, 0, 0, 0, 0, 17, 0,
|
||||
2, 0, 1, 0, 0, 0, 11, 0, 6, 0, 1, 0, 0, 0, 71, 76, 83, 76, 46, 115, 116, 100,
|
||||
46, 52, 53, 48, 0, 0, 0, 0, 14, 0, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 15, 0,
|
||||
9, 0, 0, 0, 0, 0, 4, 0, 0, 0, 109, 97, 105, 110, 0, 0, 0, 0, 38, 0, 0, 0,
|
||||
57, 0, 0, 0, 65, 0, 0, 0, 67, 0, 0, 0, 72, 0, 4, 0, 11, 0, 0, 0, 0, 0,
|
||||
0, 0, 5, 0, 0, 0, 72, 0, 5, 0, 11, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0,
|
||||
0, 0, 0, 0, 72, 0, 5, 0, 11, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 16, 0,
|
||||
0, 0, 71, 0, 3, 0, 11, 0, 0, 0, 2, 0, 0, 0, 71, 0, 4, 0, 13, 0, 0, 0,
|
||||
34, 0, 0, 0, 2, 0, 0, 0, 71, 0, 4, 0, 13, 0, 0, 0, 33, 0, 0, 0, 0, 0,
|
||||
0, 0, 71, 0, 4, 0, 22, 0, 0, 0, 6, 0, 0, 0, 64, 0, 0, 0, 71, 0, 4, 0,
|
||||
23, 0, 0, 0, 6, 0, 0, 0, 16, 0, 0, 0, 72, 0, 4, 0, 24, 0, 0, 0, 0, 0,
|
||||
0, 0, 5, 0, 0, 0, 72, 0, 5, 0, 24, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0,
|
||||
0, 0, 0, 0, 72, 0, 5, 0, 24, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 16, 0,
|
||||
0, 0, 72, 0, 5, 0, 24, 0, 0, 0, 1, 0, 0, 0, 35, 0, 0, 0, 128, 0, 0, 0,
|
||||
71, 0, 3, 0, 24, 0, 0, 0, 2, 0, 0, 0, 71, 0, 4, 0, 26, 0, 0, 0, 34, 0,
|
||||
0, 0, 1, 0, 0, 0, 71, 0, 4, 0, 26, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0,
|
||||
71, 0, 4, 0, 38, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 72, 0, 5, 0, 55, 0,
|
||||
0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 72, 0, 5, 0, 55, 0, 0, 0,
|
||||
1, 0, 0, 0, 11, 0, 0, 0, 1, 0, 0, 0, 72, 0, 5, 0, 55, 0, 0, 0, 2, 0,
|
||||
0, 0, 11, 0, 0, 0, 3, 0, 0, 0, 72, 0, 5, 0, 55, 0, 0, 0, 3, 0, 0, 0,
|
||||
11, 0, 0, 0, 4, 0, 0, 0, 71, 0, 3, 0, 55, 0, 0, 0, 2, 0, 0, 0, 71, 0,
|
||||
4, 0, 65, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 71, 0, 4, 0, 67, 0, 0, 0,
|
||||
30, 0, 0, 0, 1, 0, 0, 0, 19, 0, 2, 0, 2, 0, 0, 0, 33, 0, 3, 0, 3, 0,
|
||||
0, 0, 2, 0, 0, 0, 22, 0, 3, 0, 6, 0, 0, 0, 32, 0, 0, 0, 23, 0, 4, 0,
|
||||
7, 0, 0, 0, 6, 0, 0, 0, 4, 0, 0, 0, 24, 0, 4, 0, 8, 0, 0, 0, 7, 0,
|
||||
0, 0, 4, 0, 0, 0, 30, 0, 3, 0, 11, 0, 0, 0, 8, 0, 0, 0, 32, 0, 4, 0,
|
||||
12, 0, 0, 0, 2, 0, 0, 0, 11, 0, 0, 0, 59, 0, 4, 0, 12, 0, 0, 0, 13, 0,
|
||||
0, 0, 2, 0, 0, 0, 21, 0, 4, 0, 14, 0, 0, 0, 32, 0, 0, 0, 1, 0, 0, 0,
|
||||
43, 0, 4, 0, 14, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 32, 0, 4, 0, 16, 0,
|
||||
0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 21, 0, 4, 0, 20, 0, 0, 0, 32, 0, 0, 0,
|
||||
0, 0, 0, 0, 43, 0, 4, 0, 20, 0, 0, 0, 21, 0, 0, 0, 2, 0, 0, 0, 28, 0,
|
||||
4, 0, 22, 0, 0, 0, 8, 0, 0, 0, 21, 0, 0, 0, 28, 0, 4, 0, 23, 0, 0, 0,
|
||||
7, 0, 0, 0, 21, 0, 0, 0, 30, 0, 4, 0, 24, 0, 0, 0, 22, 0, 0, 0, 23, 0,
|
||||
0, 0, 32, 0, 4, 0, 25, 0, 0, 0, 2, 0, 0, 0, 24, 0, 0, 0, 59, 0, 4, 0,
|
||||
25, 0, 0, 0, 26, 0, 0, 0, 2, 0, 0, 0, 43, 0, 4, 0, 14, 0, 0, 0, 31, 0,
|
||||
0, 0, 1, 0, 0, 0, 32, 0, 4, 0, 32, 0, 0, 0, 2, 0, 0, 0, 7, 0, 0, 0,
|
||||
23, 0, 4, 0, 36, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 32, 0, 4, 0, 37, 0,
|
||||
0, 0, 1, 0, 0, 0, 36, 0, 0, 0, 59, 0, 4, 0, 37, 0, 0, 0, 38, 0, 0, 0,
|
||||
1, 0, 0, 0, 43, 0, 4, 0, 6, 0, 0, 0, 40, 0, 0, 0, 0, 0, 128, 63, 43, 0,
|
||||
4, 0, 20, 0, 0, 0, 53, 0, 0, 0, 1, 0, 0, 0, 28, 0, 4, 0, 54, 0, 0, 0,
|
||||
6, 0, 0, 0, 53, 0, 0, 0, 30, 0, 6, 0, 55, 0, 0, 0, 7, 0, 0, 0, 6, 0,
|
||||
0, 0, 54, 0, 0, 0, 54, 0, 0, 0, 32, 0, 4, 0, 56, 0, 0, 0, 3, 0, 0, 0,
|
||||
55, 0, 0, 0, 59, 0, 4, 0, 56, 0, 0, 0, 57, 0, 0, 0, 3, 0, 0, 0, 32, 0,
|
||||
4, 0, 61, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0, 23, 0, 4, 0, 63, 0, 0, 0,
|
||||
6, 0, 0, 0, 2, 0, 0, 0, 32, 0, 4, 0, 64, 0, 0, 0, 3, 0, 0, 0, 63, 0,
|
||||
0, 0, 59, 0, 4, 0, 64, 0, 0, 0, 65, 0, 0, 0, 3, 0, 0, 0, 32, 0, 4, 0,
|
||||
66, 0, 0, 0, 1, 0, 0, 0, 63, 0, 0, 0, 59, 0, 4, 0, 66, 0, 0, 0, 67, 0,
|
||||
0, 0, 1, 0, 0, 0, 54, 0, 5, 0, 2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0,
|
||||
3, 0, 0, 0, 248, 0, 2, 0, 5, 0, 0, 0, 65, 0, 5, 0, 16, 0, 0, 0, 17, 0,
|
||||
0, 0, 13, 0, 0, 0, 15, 0, 0, 0, 61, 0, 4, 0, 8, 0, 0, 0, 18, 0, 0, 0,
|
||||
17, 0, 0, 0, 65, 0, 6, 0, 16, 0, 0, 0, 27, 0, 0, 0, 26, 0, 0, 0, 15, 0,
|
||||
0, 0, 15, 0, 0, 0, 61, 0, 4, 0, 8, 0, 0, 0, 28, 0, 0, 0, 27, 0, 0, 0,
|
||||
65, 0, 6, 0, 32, 0, 0, 0, 33, 0, 0, 0, 26, 0, 0, 0, 31, 0, 0, 0, 15, 0,
|
||||
0, 0, 61, 0, 4, 0, 7, 0, 0, 0, 34, 0, 0, 0, 33, 0, 0, 0, 61, 0, 4, 0,
|
||||
36, 0, 0, 0, 39, 0, 0, 0, 38, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 41, 0,
|
||||
0, 0, 39, 0, 0, 0, 0, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 42, 0, 0, 0,
|
||||
39, 0, 0, 0, 1, 0, 0, 0, 81, 0, 5, 0, 6, 0, 0, 0, 43, 0, 0, 0, 39, 0,
|
||||
0, 0, 2, 0, 0, 0, 80, 0, 7, 0, 7, 0, 0, 0, 44, 0, 0, 0, 41, 0, 0, 0,
|
||||
42, 0, 0, 0, 43, 0, 0, 0, 40, 0, 0, 0, 145, 0, 5, 0, 7, 0, 0, 0, 48, 0,
|
||||
0, 0, 18, 0, 0, 0, 44, 0, 0, 0, 129, 0, 5, 0, 7, 0, 0, 0, 52, 0, 0, 0,
|
||||
48, 0, 0, 0, 34, 0, 0, 0, 145, 0, 5, 0, 7, 0, 0, 0, 60, 0, 0, 0, 28, 0,
|
||||
0, 0, 52, 0, 0, 0, 65, 0, 5, 0, 61, 0, 0, 0, 62, 0, 0, 0, 57, 0, 0, 0,
|
||||
15, 0, 0, 0, 62, 0, 3, 0, 62, 0, 0, 0, 60, 0, 0, 0, 61, 0, 4, 0, 63, 0,
|
||||
0, 0, 68, 0, 0, 0, 67, 0, 0, 0, 62, 0, 3, 0, 65, 0, 0, 0, 68, 0, 0, 0,
|
||||
253, 0, 1, 0, 56, 0, 1, 0};
|
||||
// Shader generated from shader_mesh_multiview.vert.spv
|
||||
const uint8_t shader_shader_mesh_multiview_vert[] = {
|
||||
3, 2, 35, 7, 0, 3, 1, 0, 10, 0, 13, 0, 73, 0, 0, 0, 0, 0, 0, 0, 17, 0,
|
||||
2, 0, 1, 0, 0, 0, 17, 0, 2, 0, 87, 17, 0, 0, 11, 0, 6, 0, 1, 0, 0, 0,
|
||||
71, 76, 83, 76, 46, 115, 116, 100, 46, 52, 53, 48, 0, 0, 0, 0, 14, 0, 3, 0, 0, 0,
|
||||
0, 0, 1, 0, 0, 0, 15, 0, 10, 0, 0, 0, 0, 0, 4, 0, 0, 0, 109, 97, 105, 110,
|
||||
0, 0, 0, 0, 28, 0, 0, 0, 42, 0, 0, 0, 61, 0, 0, 0, 69, 0, 0, 0, 71, 0,
|
||||
0, 0, 72, 0, 4, 0, 11, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 72, 0, 5, 0,
|
||||
11, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 72, 0, 5, 0, 11, 0,
|
||||
0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 16, 0, 0, 0, 71, 0, 3, 0, 11, 0, 0, 0,
|
||||
2, 0, 0, 0, 71, 0, 4, 0, 13, 0, 0, 0, 34, 0, 0, 0, 2, 0, 0, 0, 71, 0,
|
||||
4, 0, 13, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 71, 0, 4, 0, 22, 0, 0, 0,
|
||||
6, 0, 0, 0, 64, 0, 0, 0, 71, 0, 4, 0, 23, 0, 0, 0, 6, 0, 0, 0, 16, 0,
|
||||
0, 0, 72, 0, 4, 0, 24, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 72, 0, 5, 0,
|
||||
24, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 72, 0, 5, 0, 24, 0,
|
||||
0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 16, 0, 0, 0, 72, 0, 5, 0, 24, 0, 0, 0,
|
||||
1, 0, 0, 0, 35, 0, 0, 0, 128, 0, 0, 0, 71, 0, 3, 0, 24, 0, 0, 0, 2, 0,
|
||||
0, 0, 71, 0, 4, 0, 26, 0, 0, 0, 34, 0, 0, 0, 1, 0, 0, 0, 71, 0, 4, 0,
|
||||
26, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 71, 0, 4, 0, 28, 0, 0, 0, 11, 0,
|
||||
0, 0, 88, 17, 0, 0, 71, 0, 4, 0, 42, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0,
|
||||
72, 0, 5, 0, 59, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 72, 0,
|
||||
5, 0, 59, 0, 0, 0, 1, 0, 0, 0, 11, 0, 0, 0, 1, 0, 0, 0, 72, 0, 5, 0,
|
||||
59, 0, 0, 0, 2, 0, 0, 0, 11, 0, 0, 0, 3, 0, 0, 0, 72, 0, 5, 0, 59, 0,
|
||||
0, 0, 3, 0, 0, 0, 11, 0, 0, 0, 4, 0, 0, 0, 71, 0, 3, 0, 59, 0, 0, 0,
|
||||
2, 0, 0, 0, 71, 0, 4, 0, 69, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 71, 0,
|
||||
4, 0, 71, 0, 0, 0, 30, 0, 0, 0, 1, 0, 0, 0, 19, 0, 2, 0, 2, 0, 0, 0,
|
||||
33, 0, 3, 0, 3, 0, 0, 0, 2, 0, 0, 0, 22, 0, 3, 0, 6, 0, 0, 0, 32, 0,
|
||||
0, 0, 23, 0, 4, 0, 7, 0, 0, 0, 6, 0, 0, 0, 4, 0, 0, 0, 24, 0, 4, 0,
|
||||
8, 0, 0, 0, 7, 0, 0, 0, 4, 0, 0, 0, 30, 0, 3, 0, 11, 0, 0, 0, 8, 0,
|
||||
0, 0, 32, 0, 4, 0, 12, 0, 0, 0, 2, 0, 0, 0, 11, 0, 0, 0, 59, 0, 4, 0,
|
||||
12, 0, 0, 0, 13, 0, 0, 0, 2, 0, 0, 0, 21, 0, 4, 0, 14, 0, 0, 0, 32, 0,
|
||||
0, 0, 1, 0, 0, 0, 43, 0, 4, 0, 14, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0,
|
||||
32, 0, 4, 0, 16, 0, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 21, 0, 4, 0, 20, 0,
|
||||
0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 43, 0, 4, 0, 20, 0, 0, 0, 21, 0, 0, 0,
|
||||
2, 0, 0, 0, 28, 0, 4, 0, 22, 0, 0, 0, 8, 0, 0, 0, 21, 0, 0, 0, 28, 0,
|
||||
4, 0, 23, 0, 0, 0, 7, 0, 0, 0, 21, 0, 0, 0, 30, 0, 4, 0, 24, 0, 0, 0,
|
||||
22, 0, 0, 0, 23, 0, 0, 0, 32, 0, 4, 0, 25, 0, 0, 0, 2, 0, 0, 0, 24, 0,
|
||||
0, 0, 59, 0, 4, 0, 25, 0, 0, 0, 26, 0, 0, 0, 2, 0, 0, 0, 32, 0, 4, 0,
|
||||
27, 0, 0, 0, 1, 0, 0, 0, 14, 0, 0, 0, 59, 0, 4, 0, 27, 0, 0, 0, 28, 0,
|
||||
0, 0, 1, 0, 0, 0, 43, 0, 4, 0, 14, 0, 0, 0, 34, 0, 0, 0, 1, 0, 0, 0,
|
||||
32, 0, 4, 0, 36, 0, 0, 0, 2, 0, 0, 0, 7, 0, 0, 0, 23, 0, 4, 0, 40, 0,
|
||||
0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 32, 0, 4, 0, 41, 0, 0, 0, 1, 0, 0, 0,
|
||||
40, 0, 0, 0, 59, 0, 4, 0, 41, 0, 0, 0, 42, 0, 0, 0, 1, 0, 0, 0, 43, 0,
|
||||
4, 0, 6, 0, 0, 0, 44, 0, 0, 0, 0, 0, 128, 63, 43, 0, 4, 0, 20, 0, 0, 0,
|
||||
57, 0, 0, 0, 1, 0, 0, 0, 28, 0, 4, 0, 58, 0, 0, 0, 6, 0, 0, 0, 57, 0,
|
||||
0, 0, 30, 0, 6, 0, 59, 0, 0, 0, 7, 0, 0, 0, 6, 0, 0, 0, 58, 0, 0, 0,
|
||||
58, 0, 0, 0, 32, 0, 4, 0, 60, 0, 0, 0, 3, 0, 0, 0, 59, 0, 0, 0, 59, 0,
|
||||
4, 0, 60, 0, 0, 0, 61, 0, 0, 0, 3, 0, 0, 0, 32, 0, 4, 0, 65, 0, 0, 0,
|
||||
3, 0, 0, 0, 7, 0, 0, 0, 23, 0, 4, 0, 67, 0, 0, 0, 6, 0, 0, 0, 2, 0,
|
||||
0, 0, 32, 0, 4, 0, 68, 0, 0, 0, 3, 0, 0, 0, 67, 0, 0, 0, 59, 0, 4, 0,
|
||||
68, 0, 0, 0, 69, 0, 0, 0, 3, 0, 0, 0, 32, 0, 4, 0, 70, 0, 0, 0, 1, 0,
|
||||
0, 0, 67, 0, 0, 0, 59, 0, 4, 0, 70, 0, 0, 0, 71, 0, 0, 0, 1, 0, 0, 0,
|
||||
54, 0, 5, 0, 2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 248, 0,
|
||||
2, 0, 5, 0, 0, 0, 65, 0, 5, 0, 16, 0, 0, 0, 17, 0, 0, 0, 13, 0, 0, 0,
|
||||
15, 0, 0, 0, 61, 0, 4, 0, 8, 0, 0, 0, 18, 0, 0, 0, 17, 0, 0, 0, 61, 0,
|
||||
4, 0, 14, 0, 0, 0, 29, 0, 0, 0, 28, 0, 0, 0, 65, 0, 6, 0, 16, 0, 0, 0,
|
||||
30, 0, 0, 0, 26, 0, 0, 0, 15, 0, 0, 0, 29, 0, 0, 0, 61, 0, 4, 0, 8, 0,
|
||||
0, 0, 31, 0, 0, 0, 30, 0, 0, 0, 65, 0, 6, 0, 36, 0, 0, 0, 37, 0, 0, 0,
|
||||
26, 0, 0, 0, 34, 0, 0, 0, 29, 0, 0, 0, 61, 0, 4, 0, 7, 0, 0, 0, 38, 0,
|
||||
0, 0, 37, 0, 0, 0, 61, 0, 4, 0, 40, 0, 0, 0, 43, 0, 0, 0, 42, 0, 0, 0,
|
||||
81, 0, 5, 0, 6, 0, 0, 0, 45, 0, 0, 0, 43, 0, 0, 0, 0, 0, 0, 0, 81, 0,
|
||||
5, 0, 6, 0, 0, 0, 46, 0, 0, 0, 43, 0, 0, 0, 1, 0, 0, 0, 81, 0, 5, 0,
|
||||
6, 0, 0, 0, 47, 0, 0, 0, 43, 0, 0, 0, 2, 0, 0, 0, 80, 0, 7, 0, 7, 0,
|
||||
0, 0, 48, 0, 0, 0, 45, 0, 0, 0, 46, 0, 0, 0, 47, 0, 0, 0, 44, 0, 0, 0,
|
||||
145, 0, 5, 0, 7, 0, 0, 0, 52, 0, 0, 0, 18, 0, 0, 0, 48, 0, 0, 0, 129, 0,
|
||||
5, 0, 7, 0, 0, 0, 56, 0, 0, 0, 52, 0, 0, 0, 38, 0, 0, 0, 145, 0, 5, 0,
|
||||
7, 0, 0, 0, 64, 0, 0, 0, 31, 0, 0, 0, 56, 0, 0, 0, 65, 0, 5, 0, 65, 0,
|
||||
0, 0, 66, 0, 0, 0, 61, 0, 0, 0, 15, 0, 0, 0, 62, 0, 3, 0, 66, 0, 0, 0,
|
||||
64, 0, 0, 0, 61, 0, 4, 0, 67, 0, 0, 0, 72, 0, 0, 0, 71, 0, 0, 0, 62, 0,
|
||||
3, 0, 69, 0, 0, 0, 72, 0, 0, 0, 253, 0, 1, 0, 56, 0, 1, 0};
|
@ -0,0 +1,151 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// The main IMediaPlayer implementation. This is a
|
||||
// shim which provides Unreal interfaces, and then
|
||||
// defers everything vulkan related to the main
|
||||
// AndroidVulkanVideoImpl class.
|
||||
// ------------------------------------------------
|
||||
#pragma once
|
||||
|
||||
#include "IMediaCache.h"
|
||||
#include "IMediaControls.h"
|
||||
#include "IMediaEventSink.h"
|
||||
#include "IMediaPlayer.h"
|
||||
#include "IMediaTracks.h"
|
||||
#include "IMediaView.h"
|
||||
#include "MediaSamples.h"
|
||||
|
||||
// for vulkan types in the callback fn
|
||||
#include "IVulkanDynamicRHI.h"
|
||||
|
||||
#include "IAndroidVulkanVideoAVCallback.h"
|
||||
|
||||
#include "AndroidVulkanTextureSample.h"
|
||||
#include "VideoMediaSampleHolder.h"
|
||||
|
||||
class IAudioOut;
|
||||
class IVulkanImpl;
|
||||
|
||||
class FAndroidVulkanMediaPlayer : public IMediaPlayer,
|
||||
protected IMediaCache,
|
||||
protected IMediaControls,
|
||||
protected IMediaTracks,
|
||||
protected IMediaView,
|
||||
protected IAndroidVulkanVideoAVCallback
|
||||
{
|
||||
public:
|
||||
FAndroidVulkanMediaPlayer(IMediaEventSink &InEventSink);
|
||||
virtual ~FAndroidVulkanMediaPlayer();
|
||||
|
||||
// IMediaPlayer
|
||||
virtual void Close() override;
|
||||
virtual IMediaCache &GetCache() override
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
virtual IMediaControls &GetControls() override
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual bool FlushOnSeekCompleted() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual FString GetInfo() const override;
|
||||
virtual FGuid GetPlayerPluginGUID() const override;
|
||||
virtual IMediaSamples &GetSamples() override;
|
||||
virtual FString GetStats() const override;
|
||||
virtual IMediaTracks &GetTracks() override
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
virtual FString GetUrl() const override;
|
||||
virtual IMediaView &GetView() override
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
virtual bool Open(const FString &Url, const IMediaOptions *Options) override;
|
||||
virtual bool Open(const TSharedRef<FArchive, ESPMode::ThreadSafe> &Archive,
|
||||
const FString &OriginalUrl, const IMediaOptions *Options) override;
|
||||
virtual void SetGuid(const FGuid &Guid) override;
|
||||
virtual void TickFetch(FTimespan DeltaTime, FTimespan Timecode) override;
|
||||
virtual void TickInput(FTimespan DeltaTime, FTimespan Timecode) override;
|
||||
virtual bool GetPlayerFeatureFlag(EFeatureFlag flag) const override;
|
||||
|
||||
// IMediaCache
|
||||
// don't override these - we don't provide caching (yet?)
|
||||
// virtual bool QueryCacheState(EMediaCacheState State, TRangeSet<FTimespan>& OutTimeRanges)
|
||||
// const virtual int32 GetSampleCount(EMediaCacheState State) const
|
||||
|
||||
// IMediaControl
|
||||
virtual bool CanControl(EMediaControl Control) const override;
|
||||
virtual FTimespan GetDuration() const override;
|
||||
virtual float GetRate() const override;
|
||||
virtual EMediaState GetState() const override;
|
||||
virtual EMediaStatus GetStatus() const override;
|
||||
virtual TRangeSet<float> GetSupportedRates(EMediaRateThinning Thinning) const override;
|
||||
virtual FTimespan GetTime() const override;
|
||||
virtual bool IsLooping() const override;
|
||||
virtual bool Seek(const FTimespan &Time) override;
|
||||
virtual bool SetLooping(bool Looping) override;
|
||||
virtual bool SetRate(float Rate) override;
|
||||
virtual bool SetNativeVolume(float Volume) override;
|
||||
|
||||
// IMediaTracks
|
||||
virtual bool GetAudioTrackFormat(int32 TrackIndex, int32 FormatIndex,
|
||||
FMediaAudioTrackFormat &OutFormat) const override;
|
||||
virtual int32 GetNumTracks(EMediaTrackType TrackType) const override;
|
||||
virtual int32 GetNumTrackFormats(EMediaTrackType TrackType, int32 TrackIndex) const override;
|
||||
virtual int32 GetSelectedTrack(EMediaTrackType TrackType) const override;
|
||||
virtual FText GetTrackDisplayName(EMediaTrackType TrackType, int32 TrackIndex) const override;
|
||||
virtual int32 GetTrackFormat(EMediaTrackType TrackType, int32 TrackIndex) const override;
|
||||
virtual FString GetTrackLanguage(EMediaTrackType TrackType, int32 TrackIndex) const override;
|
||||
virtual FString GetTrackName(EMediaTrackType TrackType, int32 TrackIndex) const override;
|
||||
virtual bool GetVideoTrackFormat(int32 TrackIndex, int32 FormatIndex,
|
||||
FMediaVideoTrackFormat &OutFormat) const override;
|
||||
virtual bool SelectTrack(EMediaTrackType TrackType, int32 TrackIndex) override;
|
||||
virtual bool SetTrackFormat(EMediaTrackType TrackType, int32 TrackIndex,
|
||||
int32 FormatIndex) override;
|
||||
|
||||
// IMediaView
|
||||
// don't provide these features, so we don't override any of it
|
||||
|
||||
// IAndroidVulkanVideoAVCallback (impl callback)
|
||||
virtual void onVideoFrame(void *frameHwBuffer, int w, int h, int64_t presTimeNs) override;
|
||||
virtual void *getVkDeviceProcAddr(const char *name) override;
|
||||
virtual VkDevice getVkDevice() override;
|
||||
virtual const VkAllocationCallbacks *getVkAllocationCallbacks() override;
|
||||
virtual VkPhysicalDevice getNativePhysicalDevice() override;
|
||||
virtual void onPlaybackEnd(bool looping) override;
|
||||
|
||||
virtual void ProcessVideoSamples() override;
|
||||
|
||||
private:
|
||||
// delegates for foreground -> background transitions which should pause
|
||||
// and resume playback
|
||||
void OnEnterBackground();
|
||||
void OnEnterForeground();
|
||||
|
||||
FDelegateHandle DelegateEnterBackground;
|
||||
FDelegateHandle DelegateEnterForeground;
|
||||
|
||||
bool Seeking;
|
||||
int32 SeekIndex;
|
||||
|
||||
// our output samples
|
||||
TSharedPtr<FVideoMediaSampleHolder, ESPMode::ThreadSafe> SampleQueue;
|
||||
IMediaEventSink &EventSink;
|
||||
IVulkanImpl *impl;
|
||||
IAudioOut *AudioOut;
|
||||
FString CurInfo;
|
||||
FString VideoURL;
|
||||
FGuid PlayerGUID;
|
||||
EMediaState PlayState;
|
||||
bool Looping;
|
||||
FAndroidVulkanTextureSamplePool VideoSamplePool;
|
||||
bool HasVideoThisFrame;
|
||||
bool SentBlankFrame;
|
||||
};
|
@ -0,0 +1,190 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// Vulkan texture sample - holds an opaque pointer
|
||||
// to an Android AHardwareBuffer, and provides the
|
||||
// IMediaTextureSample and
|
||||
// IMediaTextureSampleConverter interfaces to allow
|
||||
// Unreal's media player to show the frame.
|
||||
// ------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IMediaTextureSample.h"
|
||||
#include "IMediaTextureSampleConverter.h"
|
||||
#include "MediaObjectPool.h"
|
||||
|
||||
#include "IVulkanVertexData.h"
|
||||
|
||||
#define TMP_REPLACE 5
|
||||
|
||||
class IVulkanImpl;
|
||||
|
||||
class AndroidVulkanTextureSample : public IMediaTextureSample,
|
||||
public IMediaTextureSampleConverter,
|
||||
public IMediaPoolable
|
||||
{
|
||||
friend class FAndroidVulkanTextureSamplePool;
|
||||
|
||||
public:
|
||||
AndroidVulkanTextureSample();
|
||||
|
||||
void Init(IVulkanImpl *impl, void *hwImage, int w, int h, FMediaTimeStamp sampleTime);
|
||||
void InitNoVideo();
|
||||
|
||||
~AndroidVulkanTextureSample();
|
||||
|
||||
virtual const void *GetBuffer() override
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
virtual FIntPoint GetDim() const override;
|
||||
virtual FTimespan GetDuration() const override
|
||||
{
|
||||
return FTimespan::Zero();
|
||||
}
|
||||
virtual EMediaTextureSampleFormat GetFormat() const override
|
||||
{
|
||||
return VideoTextureFormat;
|
||||
}
|
||||
|
||||
virtual FIntPoint GetOutputDim() const override;
|
||||
virtual uint32 GetStride() const override;
|
||||
virtual FRHITexture *GetTexture() const override;
|
||||
virtual IMediaTextureSampleConverter *GetMediaTextureSampleConverter() override;
|
||||
|
||||
virtual FMediaTimeStamp GetTime() const;
|
||||
|
||||
virtual bool IsCacheable() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
virtual bool IsOutputSrgb() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// IMediaTextureSampleConverter interface (changed in 5.5 to add command list)
|
||||
|
||||
virtual bool Convert(FTexture2DRHIRef &InDstTexture, const FConversionHints &Hints);
|
||||
|
||||
bool ConvertInternal(FRHICommandListImmediate &RHICmdList, FTexture2DRHIRef &InDstTexture,
|
||||
const FConversionHints &Hints);
|
||||
|
||||
// called inside render pass
|
||||
bool RenderToMesh(FTexture2DRHIRef InDstTexture, FRHICommandList &RHICmdList, void *MeshID);
|
||||
|
||||
// called outside a render pass
|
||||
// n.b. don't make matrices etc. const references
|
||||
// because then we get dangling references and
|
||||
// BAD THINGS happen
|
||||
bool UpdateViewMatrices(FTexture2DRHIRef InDstTexture, FRHICommandList &RHICmdList,
|
||||
ShaderViewMatrices Matrices, void *MeshID);
|
||||
bool InitFrameForMeshRendering(FTexture2DRHIRef InDstTexture, FRHICommandList &RHICmdList,
|
||||
void *MeshID);
|
||||
bool UpdateMesh(FTexture2DRHIRef InDstTexture, FRHICommandList &RHICmdList,
|
||||
TArray<VertexData> PositionAndUV, TArray<uint32> Indices,
|
||||
ShaderModelMatrix Matrix, void *MeshID);
|
||||
|
||||
void InitializePoolable() override;
|
||||
virtual bool IsReadyForReuse() override;
|
||||
virtual void ShutdownPoolable() override;
|
||||
|
||||
void Clear();
|
||||
void ClearIfShown();
|
||||
|
||||
static void SetVideoFormat(EMediaTextureSampleFormat Format)
|
||||
{
|
||||
VideoTextureFormat = Format;
|
||||
}
|
||||
|
||||
void ImplDeleted();
|
||||
|
||||
private:
|
||||
void FRenderOnRHIThreadCommand(const FTexture2DRHIRef &DstTexture);
|
||||
void FRenderMeshOnRHIThreadCommand(const FTexture2DRHIRef &DstTexture, void *MeshID,
|
||||
int Samples);
|
||||
void FUpdateMeshOnRHIThreadCommand(const FTexture2DRHIRef &DstTexture,
|
||||
const TArray<VertexData> &PositionAndUV,
|
||||
const TArray<uint32> &Indices,
|
||||
const ShaderModelMatrix &Matrix, void *MeshID);
|
||||
void FUpdateMatricesOnRHIThreadCommand(const FTexture2DRHIRef &DstTexture,
|
||||
const ShaderViewMatrices &Matrices, void *MeshID);
|
||||
void FInitMeshRenderingCommand(const FTexture2DRHIRef &DstTexture, void *MeshID, int Samples);
|
||||
bool IsEmptyTexture;
|
||||
FGPUFenceRHIRef Fence;
|
||||
|
||||
FIntPoint Dimension;
|
||||
void *FrameHWImage;
|
||||
|
||||
/** Duration for which the sample is valid. */
|
||||
// FTimespan TimeDuration;
|
||||
|
||||
/** Sample time. */
|
||||
FMediaTimeStamp SampleTime;
|
||||
|
||||
int NumSamples; // number of samples used for MSAA on rendering meshes directly
|
||||
|
||||
IVulkanImpl *Impl;
|
||||
bool Shown;
|
||||
|
||||
FCriticalSection AccessLock;
|
||||
|
||||
static EMediaTextureSampleFormat VideoTextureFormat;
|
||||
};
|
||||
|
||||
class FAndroidVulkanTextureSamplePool : public TMediaObjectPool<AndroidVulkanTextureSample>
|
||||
{
|
||||
public:
|
||||
void Tick()
|
||||
{
|
||||
TMediaObjectPool<AndroidVulkanTextureSample>::Tick();
|
||||
for (auto wp : liveObjects)
|
||||
{
|
||||
auto pin = wp.Pin();
|
||||
if (pin.IsValid())
|
||||
{
|
||||
pin->ClearIfShown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TSharedRef<AndroidVulkanTextureSample, ESPMode::ThreadSafe> AcquireShared()
|
||||
{
|
||||
auto ret = TMediaObjectPool<AndroidVulkanTextureSample>::AcquireShared();
|
||||
TWeakPtr<AndroidVulkanTextureSample, ESPMode::ThreadSafe> wp = ret;
|
||||
if (!liveObjects.Find(wp))
|
||||
{
|
||||
liveObjects.Add(wp);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ReleaseEverything()
|
||||
{
|
||||
for (auto wp : liveObjects)
|
||||
{
|
||||
auto pin = wp.Pin();
|
||||
if (pin.IsValid())
|
||||
{
|
||||
pin->ImplDeleted();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto wp : liveObjects)
|
||||
{
|
||||
auto pin = wp.Pin();
|
||||
if (pin.IsValid())
|
||||
{
|
||||
pin->ShutdownPoolable();
|
||||
}
|
||||
}
|
||||
Reset();
|
||||
}
|
||||
int bob[TMP_REPLACE];
|
||||
// for anything that has already been drawn, we want to release textures ASAP,
|
||||
// rather than whenever Unreal gets round to it.
|
||||
TArray<TWeakPtr<AndroidVulkanTextureSample, ESPMode::ThreadSafe>> liveObjects;
|
||||
};
|
@ -0,0 +1,22 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// AndroidVulkanVideo module implementation
|
||||
// ------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
#include "IMediaEventSink.h"
|
||||
#include "IMediaPlayer.h"
|
||||
|
||||
class IAndroidVulkanVideoModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
virtual TSharedPtr<IMediaPlayer, ESPMode::ThreadSafe> CreatePlayer(
|
||||
IMediaEventSink &EventSink) = 0;
|
||||
|
||||
private:
|
||||
};
|
@ -0,0 +1,21 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// Callback used by implementation class to send
|
||||
// buffers to media player.
|
||||
// ------------------------------------------------
|
||||
#pragma once
|
||||
class IAndroidVulkanVideoAVCallback
|
||||
{
|
||||
public:
|
||||
// called with video frame hardware buffers
|
||||
virtual void onVideoFrame(void *frameHwBuffer, int w, int h, int64_t presTimeNs) = 0;
|
||||
// we need to get vulkan functions and device etc. via Unreal Engine for consistency
|
||||
virtual void *getVkDeviceProcAddr(const char *name) = 0;
|
||||
virtual VkDevice getVkDevice() = 0;
|
||||
virtual const VkAllocationCallbacks *getVkAllocationCallbacks() = 0;
|
||||
virtual VkPhysicalDevice getNativePhysicalDevice() = 0;
|
||||
|
||||
virtual void onPlaybackEnd(bool looping) = 0;
|
||||
};
|
@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
struct VertexData
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float _ignored1;
|
||||
float u;
|
||||
float v;
|
||||
float _ignored2;
|
||||
float _ignored3;
|
||||
};
|
||||
|
||||
struct ShaderModelMatrix
|
||||
{
|
||||
ShaderModelMatrix()
|
||||
{
|
||||
memset(this, 0, sizeof(ShaderModelMatrix));
|
||||
for (int c = 0; c < 4; c++)
|
||||
{
|
||||
m44[c + c * 4] = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
float m44[16];
|
||||
};
|
||||
|
||||
struct ShaderViewMatrices
|
||||
{
|
||||
ShaderViewMatrices()
|
||||
{
|
||||
memset(this, 0, sizeof(ShaderViewMatrices));
|
||||
for (int c = 0; c < 4; c++)
|
||||
{
|
||||
vp44[c + c * 4] = 1;
|
||||
vp44_2[c + c * 4] = 1;
|
||||
}
|
||||
}
|
||||
float vp44[16];
|
||||
float vp44_2[16];
|
||||
float pre_view_translation[4];
|
||||
float pre_view_translation_2[4];
|
||||
};
|
@ -0,0 +1,12 @@
|
||||
<!-- Copyright Joe Marshall 2024 - All Rights Reserved -->
|
||||
<!-- Copies our custom vulkan layer into the build. -->
|
||||
|
||||
<root xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- init section is always evaluated once per architecture -->
|
||||
<trace enable="true"/>
|
||||
|
||||
<resourceCopies>
|
||||
<copyFile src="$S(PluginDir)/$S(Architecture)/libVkLayer_OverrideLib.so" dst="$S(BuildDir)/libs/$S(Architecture)/libVkLayer_OverrideLib.so" />
|
||||
</resourceCopies>
|
||||
|
||||
</root>
|
@ -0,0 +1,79 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// Build AndroidVulkanVideo factory module (to make
|
||||
// it possible to select this as a video source in
|
||||
// editor.)
|
||||
// ------------------------------------------------
|
||||
|
||||
using UnrealBuildTool;
|
||||
using System.IO;
|
||||
using System;
|
||||
|
||||
public class AndroidVulkanVideoFactory : ModuleRules
|
||||
{
|
||||
public AndroidVulkanVideoFactory(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
CppStandard = CppStandardVersion.Cpp17;
|
||||
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add public include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add other private include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
"Projects"
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"CoreUObject", "Engine","RHI","VulkanRHI","AudioExtensions"
|
||||
// ... add private dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
PrivateIncludePathModuleNames.AddRange(
|
||||
new string[] {
|
||||
"AndroidVulkanVideo",
|
||||
"Media",
|
||||
"MediaUtils"
|
||||
});
|
||||
|
||||
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Media"
|
||||
}
|
||||
);
|
||||
|
||||
if (Target.Platform == UnrealTargetPlatform.Android)
|
||||
{
|
||||
DynamicallyLoadedModuleNames.Add("AndroidVulkanVideo");
|
||||
}
|
||||
// don't precompile video
|
||||
// factory so any editor can use it
|
||||
bPrecompile = false;
|
||||
bUsePrecompiled = false;
|
||||
}
|
||||
}
|
@ -0,0 +1,168 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// Factory class to tell Unreal that we can play
|
||||
// different types of media.
|
||||
// ------------------------------------------------
|
||||
|
||||
#include "AndroidVulkanVideo.h"
|
||||
#include "IMediaModule.h"
|
||||
#include "IMediaPlayerFactory.h"
|
||||
#include "Misc/Guid.h"
|
||||
#include "Misc/Paths.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FAndroidVulkanVideoFactoryModule"
|
||||
|
||||
class FAndroidVulkanVideoFactoryModule : public IMediaPlayerFactory, public IModuleInterface
|
||||
{
|
||||
public:
|
||||
virtual bool CanPlayUrl(const FString &Url, const IMediaOptions * /*Options*/,
|
||||
TArray<FText> * /*OutWarnings*/,
|
||||
TArray<FText> *OutErrors) const override
|
||||
{
|
||||
FString Scheme;
|
||||
FString Location;
|
||||
|
||||
// check scheme
|
||||
if (!Url.Split(TEXT("://"), &Scheme, &Location, ESearchCase::CaseSensitive))
|
||||
{
|
||||
if (OutErrors != nullptr)
|
||||
{
|
||||
OutErrors->Add(LOCTEXT("NoSchemeFound", "No URI scheme found"));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SupportedUriSchemes.Contains(Scheme))
|
||||
{
|
||||
if (OutErrors != nullptr)
|
||||
{
|
||||
OutErrors->Add(FText::Format(
|
||||
LOCTEXT("SchemeNotSupported", "The URI scheme '{0}' is not supported"),
|
||||
FText::FromString(Scheme)));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// check file extension
|
||||
if (Scheme == TEXT("file"))
|
||||
{
|
||||
const FString Extension = FPaths::GetExtension(Location, false);
|
||||
|
||||
if (!SupportedFileExtensions.Contains(Extension))
|
||||
{
|
||||
if (OutErrors != nullptr)
|
||||
{
|
||||
OutErrors->Add(
|
||||
FText::Format(LOCTEXT("ExtensionNotSupported",
|
||||
"The file extension '{0}' is not supported"),
|
||||
FText::FromString(Extension)));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual TSharedPtr<IMediaPlayer, ESPMode::ThreadSafe> CreatePlayer(
|
||||
IMediaEventSink &EventSink) override
|
||||
{
|
||||
auto AndroidVulkanVideoModule =
|
||||
FModuleManager::LoadModulePtr<IAndroidVulkanVideoModule>("AndroidVulkanVideo");
|
||||
return (AndroidVulkanVideoModule != nullptr)
|
||||
? AndroidVulkanVideoModule->CreatePlayer(EventSink)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
virtual FText GetDisplayName() const override
|
||||
{
|
||||
return LOCTEXT("MediaPlayerDisplayName", "Android Vulkan Video");
|
||||
}
|
||||
|
||||
virtual FName GetPlayerName() const override
|
||||
{
|
||||
static FName PlayerName(TEXT("DirectVideo Android"));
|
||||
return PlayerName;
|
||||
}
|
||||
|
||||
virtual FGuid GetPlayerPluginGUID() const override
|
||||
{
|
||||
static FGuid OurGUID(0x9bf2d7c6, 0xb2b84d26, 0xb6ae5a3a, 0xc9883569);
|
||||
return OurGUID;
|
||||
}
|
||||
|
||||
virtual const TArray<FString> &GetSupportedPlatforms() const override
|
||||
{
|
||||
return SupportedPlatforms;
|
||||
}
|
||||
|
||||
virtual bool SupportsFeature(EMediaFeature Feature) const override
|
||||
{
|
||||
return ((Feature == EMediaFeature::AudioTracks) ||
|
||||
(Feature == EMediaFeature::VideoSamples) ||
|
||||
(Feature == EMediaFeature::VideoTracks));
|
||||
}
|
||||
|
||||
virtual void ShutdownModule() override
|
||||
{
|
||||
// unregister player factory
|
||||
auto MediaModule = FModuleManager::GetModulePtr<IMediaModule>("Media");
|
||||
|
||||
if (MediaModule != nullptr)
|
||||
{
|
||||
MediaModule->UnregisterPlayerFactory(*this);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void StartupModule() override
|
||||
{
|
||||
|
||||
// supported file extensions
|
||||
SupportedFileExtensions.Add(TEXT("3gpp"));
|
||||
SupportedFileExtensions.Add(TEXT("aac"));
|
||||
SupportedFileExtensions.Add(TEXT("mp4"));
|
||||
SupportedFileExtensions.Add(TEXT("m3u8"));
|
||||
SupportedFileExtensions.Add(TEXT("webm"));
|
||||
|
||||
// supported platforms
|
||||
SupportedPlatforms.Add(TEXT("Android"));
|
||||
|
||||
// supported schemes
|
||||
SupportedUriSchemes.Add(TEXT("file"));
|
||||
/* SupportedUriSchemes.Add(TEXT("http"));
|
||||
SupportedUriSchemes.Add(TEXT("httpd"));
|
||||
SupportedUriSchemes.Add(TEXT("https"));
|
||||
SupportedUriSchemes.Add(TEXT("mms"));
|
||||
SupportedUriSchemes.Add(TEXT("rtsp"));
|
||||
SupportedUriSchemes.Add(TEXT("rtspt"));
|
||||
SupportedUriSchemes.Add(TEXT("rtspu"));*/
|
||||
|
||||
// register media player info
|
||||
auto MediaModule = FModuleManager::LoadModulePtr<IMediaModule>("Media");
|
||||
|
||||
if (MediaModule != nullptr)
|
||||
{
|
||||
MediaModule->RegisterPlayerFactory(*this);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/** List of supported media file types. */
|
||||
TArray<FString> SupportedFileExtensions;
|
||||
|
||||
/** List of platforms that the media player support. */
|
||||
TArray<FString> SupportedPlatforms;
|
||||
|
||||
/** List of supported URI schemes. */
|
||||
TArray<FString> SupportedUriSchemes;
|
||||
};
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FAndroidVulkanVideoFactoryModule, AndroidVulkanVideoFactory);
|
@ -0,0 +1,518 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// Renders mesh directly to framebuffer from video
|
||||
// stream (without texture between)
|
||||
// ------------------------------------------------
|
||||
#include "DirectVideoMeshRenderer.h"
|
||||
|
||||
#include "AndroidVulkanTextureSample.h"
|
||||
|
||||
#include "Components/StaticMeshComponent.h"
|
||||
#include "Engine/StaticMesh.h"
|
||||
#include "Engine/StaticMeshActor.h"
|
||||
#include "EngineModule.h"
|
||||
#include "IMediaPlayer.h"
|
||||
#include "RHIResources.h"
|
||||
#include "RawIndexBuffer.h"
|
||||
#include "RenderGraphBuilder.h"
|
||||
#include "Rendering/ColorVertexBuffer.h"
|
||||
#include "Rendering/PositionVertexBuffer.h"
|
||||
#include "Rendering/StaticMeshVertexBuffer.h"
|
||||
#include "SceneView.h"
|
||||
#include "SceneViewExtension.h"
|
||||
#include "StaticMeshResources.h"
|
||||
#include "StereoRendering.h"
|
||||
|
||||
#include "Math/Matrix.h"
|
||||
|
||||
#include "MeshDescription.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogDirectVideoMeshRenderer);
|
||||
|
||||
class FRendererSceneViewExt : public FSceneViewExtensionBase
|
||||
{
|
||||
public:
|
||||
FRendererSceneViewExt(const FAutoRegister &AutoRegister,
|
||||
UDirectVideoMeshRendererComponent &InOwner)
|
||||
: FSceneViewExtensionBase(AutoRegister), Owner(InOwner)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void PostRenderView_RenderThread(FRDGBuilder &GraphBuilder, FSceneView &InView) override
|
||||
{
|
||||
}
|
||||
|
||||
virtual void PostRenderBasePassMobile_RenderThread(FRHICommandList &RHICmdList,
|
||||
FSceneView &InView) override
|
||||
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, VeryVerbose, "PostRenderBasePassMobile");
|
||||
|
||||
#if PLATFORM_ANDROID
|
||||
Owner.DrawToCommandList(RHICmdList, InView);
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual void SetupViewFamily(FSceneViewFamily &InViewFamily) override
|
||||
{
|
||||
}
|
||||
|
||||
virtual void SetupView(FSceneViewFamily &InViewFamily, FSceneView &InView) override
|
||||
{
|
||||
}
|
||||
|
||||
virtual void PreRenderViewFamily_RenderThread(FRDGBuilder &GraphBuilder,
|
||||
FSceneViewFamily &InViewFamily)
|
||||
{
|
||||
#if PLATFORM_ANDROID
|
||||
|
||||
// we are outside of a command list, so we can update the mesh if needed
|
||||
Owner.DoWorkOutsideRenderPass(GraphBuilder.RHICmdList, InViewFamily);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual void PreRenderView_RenderThread(FRDGBuilder &GraphBuilder, FSceneView &InView)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void BeginRenderViewFamily(FSceneViewFamily &InViewFamily) override
|
||||
{
|
||||
Owner.UpdateVisibility();
|
||||
}
|
||||
|
||||
UDirectVideoMeshRendererComponent &Owner;
|
||||
};
|
||||
|
||||
UDirectVideoMeshRendererComponent::UDirectVideoMeshRendererComponent()
|
||||
{
|
||||
HideMeshWhileWeHaveVideo = true;
|
||||
PrimaryComponentTick.bCanEverTick = true;
|
||||
Initialized = false;
|
||||
}
|
||||
|
||||
UDirectVideoMeshRendererComponent::~UDirectVideoMeshRendererComponent()
|
||||
{
|
||||
DeInit();
|
||||
}
|
||||
|
||||
void UDirectVideoMeshRendererComponent::DeInit()
|
||||
{
|
||||
Initialized = false;
|
||||
#if PLATFORM_ANDROID
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, VeryVerbose, "Deinit");
|
||||
// Extensions clean up on destroy
|
||||
ExtensionHolder.Reset();
|
||||
|
||||
if (PreparedSample != NULL)
|
||||
{
|
||||
AndroidVulkanTextureSample *VulkanSample =
|
||||
static_cast<AndroidVulkanTextureSample *>(PreparedSample.Get());
|
||||
VulkanSample->ImplDeleted();
|
||||
}
|
||||
if (CurrentSample != NULL)
|
||||
{
|
||||
AndroidVulkanTextureSample *VulkanSample =
|
||||
static_cast<AndroidVulkanTextureSample *>(CurrentSample.Get());
|
||||
VulkanSample->ImplDeleted();
|
||||
}
|
||||
CurrentSample = NULL;
|
||||
PreparedSample = NULL;
|
||||
if (Facade != NULL)
|
||||
{
|
||||
auto PFacade = Facade.Pin();
|
||||
}
|
||||
#endif
|
||||
|
||||
Facade.Reset();
|
||||
// the media facade sample sink keeps a weak pointer to
|
||||
// the texture sink and then clears it if destroyed,
|
||||
// so we don't need to remove the texture sink, we can just
|
||||
// kill it here.
|
||||
TextureSink.Reset();
|
||||
}
|
||||
|
||||
void UDirectVideoMeshRendererComponent::BeginDestroy()
|
||||
{
|
||||
DeInit();
|
||||
UActorComponent::BeginDestroy();
|
||||
}
|
||||
|
||||
void UDirectVideoMeshRendererComponent::Initialize()
|
||||
{
|
||||
#if PLATFORM_ANDROID
|
||||
TextureSink = MakeShared<_TextureSink>(this);
|
||||
|
||||
if (Facade == NULL)
|
||||
{
|
||||
if (LinkedPlayer != NULL)
|
||||
{
|
||||
Facade = LinkedPlayer->GetPlayerFacade();
|
||||
}
|
||||
}
|
||||
|
||||
if (Facade != NULL)
|
||||
{
|
||||
if (!HasAnyFlags(RF_ClassDefaultObject))
|
||||
{
|
||||
auto PFacade = Facade.Pin();
|
||||
TSharedRef<FMediaTextureSampleSink> sinkPtr = TextureSink.ToSharedRef();
|
||||
PFacade.Get()->AddVideoSampleSink(sinkPtr);
|
||||
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, VeryVerbose, "Registered for frames");
|
||||
Initialized = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, VeryVerbose, "No media player");
|
||||
}
|
||||
|
||||
GetMeshObject();
|
||||
|
||||
if (ExtensionHolder == NULL)
|
||||
{
|
||||
ExtensionHolder = FSceneViewExtensions::NewExtension<FRendererSceneViewExt>(*this);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void UDirectVideoMeshRendererComponent::HandlePlayerMediaEvent(EMediaSampleSinkEvent Event)
|
||||
{
|
||||
int IEvent = (int)Event;
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, VeryVerbose, "Handle event {0}", IEvent);
|
||||
|
||||
switch (Event)
|
||||
{
|
||||
case EMediaSampleSinkEvent::Detached:
|
||||
case EMediaSampleSinkEvent::MediaClosed:
|
||||
CurrentSample = NULL;
|
||||
PreparedSample = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void UDirectVideoMeshRendererComponent::TickComponent(float DeltaTime, ELevelTick TickType,
|
||||
FActorComponentTickFunction *ThisTickFunction)
|
||||
{
|
||||
if (!Initialized)
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
void UDirectVideoMeshRendererComponent::UpdateVisibility()
|
||||
{
|
||||
if (HideMeshWhileWeHaveVideo)
|
||||
{
|
||||
MeshActor = GetOwner<AStaticMeshActor>();
|
||||
if (MeshActor == NULL)
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, Warning,
|
||||
"Mesh renderer has to be on StaticMeshActor");
|
||||
return;
|
||||
}
|
||||
// need to unhide or hide the underlying staticmesh here
|
||||
// depending on whether we have video
|
||||
UStaticMeshComponent *MeshComponent = MeshActor->GetStaticMeshComponent();
|
||||
if (MeshComponent == NULL)
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, Warning,
|
||||
"Mesh renderer needs a static mesh component");
|
||||
return;
|
||||
}
|
||||
|
||||
MeshComponent->bRenderInDepthPass = 0;
|
||||
MeshComponent->SetRenderInMainPass(CurrentSample == NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void UDirectVideoMeshRendererComponent::GetMeshObject()
|
||||
{
|
||||
MeshActor = GetOwner<AStaticMeshActor>();
|
||||
if (MeshActor == NULL)
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, Warning,
|
||||
"Mesh renderer has to be on StaticMeshActor");
|
||||
return;
|
||||
}
|
||||
UStaticMeshComponent *MeshComponent = MeshActor->GetStaticMeshComponent();
|
||||
TObjectPtr<UStaticMesh> Mesh = MeshComponent->GetStaticMesh();
|
||||
#if WITH_EDITOR
|
||||
if (Mesh != NULL)
|
||||
{
|
||||
FStaticMeshRenderData *RenderData = Mesh->GetRenderData();
|
||||
if (RenderData != NULL)
|
||||
{
|
||||
const FStaticMeshLODResources &LODRenderData = RenderData->LODResources[0];
|
||||
const auto &VertexBuffers = LODRenderData.VertexBuffers;
|
||||
const auto &IndexBuffer = LODRenderData.IndexBuffer; // index buffer for rendering this
|
||||
|
||||
if (ConstructMeshFromRenderData(VertexBuffers, IndexBuffer))
|
||||
{
|
||||
HasMesh = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, Warning, "Mesh making failed");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, Warning, "No render data");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, Warning, "Mesh is null");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void DumpMatrix(const char *Name, FMatrix44f Matrix)
|
||||
{
|
||||
char MatrixDesc[256];
|
||||
int ofs = snprintf(MatrixDesc, 250, "%s:\n", Name);
|
||||
for (int c = 0; c < 4; c++)
|
||||
{
|
||||
for (int d = 0; d < 4; d++)
|
||||
{
|
||||
int len = snprintf(&MatrixDesc[ofs], 250 - ofs, "%4.4f,", Matrix.M[c][d]);
|
||||
ofs += len;
|
||||
}
|
||||
MatrixDesc[ofs] = '\n';
|
||||
ofs++;
|
||||
}
|
||||
MatrixDesc[ofs] = 0;
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, Verbose, "{0}", MatrixDesc);
|
||||
}
|
||||
|
||||
void UDirectVideoMeshRendererComponent::DrawToCommandList(FRHICommandList &RHICmdList,
|
||||
FSceneView &InView)
|
||||
{
|
||||
bool hasSample = (PreparedSample != NULL);
|
||||
#if PLATFORM_ANDROID
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, Verbose, "DrawToCOmmandList_Init:{0} HasSample:{1}",
|
||||
Initialized, hasSample);
|
||||
|
||||
if (!Initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (PreparedSample != NULL)
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, Verbose, "DrawToCOmmandList:{0}",
|
||||
RHICmdList.IsOutsideRenderPass());
|
||||
AndroidVulkanTextureSample *VulkanSample =
|
||||
static_cast<AndroidVulkanTextureSample *>(PreparedSample.Get());
|
||||
const FTextureRHIRef &ColourTex = InView.Family->RenderTarget->GetRenderTargetTexture();
|
||||
VulkanSample->RenderToMesh(ColourTex, RHICmdList, (void *)(MeshActor.Get()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void UDirectVideoMeshRendererComponent::GetShaderInfo(const FSceneView *InView, float *m44,
|
||||
float *vp44, float *pre_view_translation)
|
||||
{
|
||||
FTransform Transform = MeshActor->ActorToWorld();
|
||||
FMatrix44f LocalToRelativeWorld = FMatrix44f(Transform.ToMatrixWithScale());
|
||||
// DumpMatrix("Model", LocalToRelativeWorld);
|
||||
|
||||
auto TranslatedWorldToClip =
|
||||
FMatrix44f(InView->ViewMatrices.GetTranslatedViewProjectionMatrix());
|
||||
// DumpMatrix("VP", TranslatedWorldToClip);
|
||||
auto MVPMatrix = LocalToRelativeWorld * TranslatedWorldToClip;
|
||||
// DumpMatrix("MVP", MVPMatrix);
|
||||
|
||||
auto Translation = InView->ViewMatrices.GetPreViewTranslation();
|
||||
FVector4f Translation4 = FVector4f(Translation.X, Translation.Y, Translation.Z, 0);
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, VeryVerbose, "Pre view translation 2:{0},{1},{2},{3}",
|
||||
Translation4.X, Translation4.Y, Translation4.Z, Translation4.W);
|
||||
memcpy(vp44, TranslatedWorldToClip.M, 16 * sizeof(float));
|
||||
// auto TransposedVP=TranslatedWorldToClip.GetTransposed();
|
||||
// memcpy(vp44,TransposedVP.M,16*sizeof(float));
|
||||
// auto TransposedM = LocalToRelativeWorld.GetTransposed();
|
||||
// memcpy(m44,TransposedM.M,16*sizeof(float));
|
||||
memcpy(m44, LocalToRelativeWorld.M, 16 * sizeof(float));
|
||||
pre_view_translation[0] = Translation4.X;
|
||||
pre_view_translation[1] = Translation4.Y;
|
||||
pre_view_translation[2] = Translation4.Z;
|
||||
pre_view_translation[3] = Translation4.W;
|
||||
}
|
||||
|
||||
void UDirectVideoMeshRendererComponent::DoWorkOutsideRenderPass(FRHICommandList &RHICmdList,
|
||||
FSceneViewFamily &InViewFamily)
|
||||
{
|
||||
|
||||
// render both views if we are stereo multiview (with single pass rendering)
|
||||
#if PLATFORM_ANDROID
|
||||
if (!Initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (CurrentSample != NULL)
|
||||
{
|
||||
ShaderViewMatrices ViewMatrices;
|
||||
ShaderModelMatrix ModelMatrix;
|
||||
PreparedSample = CurrentSample;
|
||||
AndroidVulkanTextureSample *VulkanSample =
|
||||
static_cast<AndroidVulkanTextureSample *>(PreparedSample.Get());
|
||||
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, VeryVerbose, "Work outside render pass:{0}",
|
||||
RHICmdList.IsOutsideRenderPass());
|
||||
for (auto &InView : InViewFamily.Views)
|
||||
{
|
||||
|
||||
if (IStereoRendering::IsASecondaryView(*InView))
|
||||
{
|
||||
GetShaderInfo(InView, ModelMatrix.m44, ViewMatrices.vp44_2,
|
||||
ViewMatrices.pre_view_translation_2);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetShaderInfo(InView, ModelMatrix.m44, ViewMatrices.vp44,
|
||||
ViewMatrices.pre_view_translation);
|
||||
}
|
||||
}
|
||||
const FTextureRHIRef &ColourTex = InViewFamily.RenderTarget->GetRenderTargetTexture();
|
||||
VulkanSample->UpdateViewMatrices(ColourTex, RHICmdList, ViewMatrices,
|
||||
(void *)(MeshActor.Get()));
|
||||
VulkanSample->UpdateMesh(ColourTex, RHICmdList, Positions, Indices, ModelMatrix,
|
||||
(void *)(MeshActor.Get()));
|
||||
VulkanSample->InitFrameForMeshRendering(ColourTex, RHICmdList, (void *)(MeshActor.Get()));
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, VeryVerbose, "Emptying prepared sample");
|
||||
PreparedSample = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool UDirectVideoMeshRendererComponent::Enqueue(
|
||||
const TSharedRef<IMediaTextureSample, ESPMode::ThreadSafe> &Sample)
|
||||
{
|
||||
if (!Initialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
static FGuid OurGUID(0x9bf2d7c6, 0xb2b84d26, 0xb6ae5a3a, 0xc9883569);
|
||||
auto PFacade = Facade.Pin();
|
||||
if (PFacade->GetPlayer()->GetPlayerPluginGUID() == OurGUID)
|
||||
{
|
||||
CurrentSample = Sample;
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, VeryVerbose, "Mesh component has sample");
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, Warning, "Mesh component has wrong player {0}",
|
||||
PFacade.Get()->GetGuid());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void UDirectVideoMeshRendererComponent::Serialize(FArchive &Ar)
|
||||
{
|
||||
/* if(!Ar.IsSaving()){
|
||||
char bob[119];
|
||||
Ar.Serialize(bob,119);
|
||||
return;
|
||||
}*/
|
||||
#if WITH_EDITOR
|
||||
if (Ar.IsSaving())
|
||||
{
|
||||
if (!HasMesh)
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, Warning, "Needs mesh object");
|
||||
|
||||
GetMeshObject();
|
||||
}
|
||||
if (!HasMesh)
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, Warning,
|
||||
"Couldn't get mesh for DirectVideoMeshRenderComponent");
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, VeryVerbose, "Saving mesh details");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
UActorComponent::Serialize(Ar);
|
||||
|
||||
if (Ar.IsLoading())
|
||||
{
|
||||
if (HasMesh)
|
||||
{
|
||||
int NumVertices = PositionsAsFloats.Num() / 5;
|
||||
Positions.Init({}, NumVertices);
|
||||
for (int c = 0; c < NumVertices; c++)
|
||||
{
|
||||
Positions[c].x = PositionsAsFloats[c * 5];
|
||||
Positions[c].y = PositionsAsFloats[c * 5 + 1];
|
||||
Positions[c].z = PositionsAsFloats[c * 5 + 2];
|
||||
Positions[c].u = PositionsAsFloats[c * 5 + 3];
|
||||
Positions[c].v = PositionsAsFloats[c * 5 + 4];
|
||||
}
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, VeryVerbose,
|
||||
"DirectVideoMeshRenderComponent - Vertices {0} Indices {1}", Indices.Num(),
|
||||
Positions.Num());
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, Warning,
|
||||
"No saved mesh for DirectVideoMeshRenderComponent");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool UDirectVideoMeshRendererComponent::ConstructMeshFromRenderData(
|
||||
const FStaticMeshVertexBuffers &VertexBuffers, const FRawStaticIndexBuffer &IndexBuffer)
|
||||
{
|
||||
const FPositionVertexBuffer &Pos =
|
||||
VertexBuffers.PositionVertexBuffer; // contains vertex positions
|
||||
const FStaticMeshVertexBuffer &TangentsAndTextureCoords =
|
||||
VertexBuffers
|
||||
.StaticMeshVertexBuffer; // contains tangents, texture coords and lightmap coords
|
||||
const FColorVertexBuffer &Color = VertexBuffers.ColorVertexBuffer; // vertex colours
|
||||
if (IndexBuffer.GetIndexDataSize() == 0)
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, Warning, "No indices for mesh");
|
||||
return false;
|
||||
}
|
||||
|
||||
// make 1 buffer with:
|
||||
// position (float * 3, packed as 4 byte)
|
||||
// texture coordinates (float*2), packed as 4 byte
|
||||
int NumVertices = Pos.GetNumVertices();
|
||||
Positions.Init({}, NumVertices);
|
||||
int NumIndices = IndexBuffer.GetNumIndices();
|
||||
Indices.Init({}, NumIndices);
|
||||
|
||||
for (int c = 0; c < NumVertices; c++)
|
||||
{
|
||||
const FVector3f &VPos = Pos.VertexPosition(c);
|
||||
Positions[c].x = VPos.X;
|
||||
Positions[c].y = VPos.Y;
|
||||
Positions[c].z = VPos.Z;
|
||||
FVector2f TexPos = TangentsAndTextureCoords.GetVertexUV(c, 0);
|
||||
Positions[c].u = TexPos.X;
|
||||
Positions[c].v = TexPos.Y;
|
||||
}
|
||||
|
||||
// and 1 index buffer (int32)
|
||||
IndexBuffer.GetCopy(Indices);
|
||||
PositionsAsFloats.Init({}, NumVertices * 5);
|
||||
for (int c = 0; c < NumVertices; c++)
|
||||
{
|
||||
PositionsAsFloats[c * 5] = Positions[c].x;
|
||||
PositionsAsFloats[c * 5 + 1] = Positions[c].y;
|
||||
PositionsAsFloats[c * 5 + 2] = Positions[c].z;
|
||||
PositionsAsFloats[c * 5 + 3] = Positions[c].u;
|
||||
PositionsAsFloats[c * 5 + 4] = Positions[c].v;
|
||||
}
|
||||
return true;
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FVulkanVideoMeshComponentModule"
|
||||
|
||||
class FVulkanVideoMeshComponentModule : public IModuleInterface
|
||||
{
|
||||
|
||||
void StartupModule()
|
||||
{
|
||||
// This code will execute after your module is loaded into memory; the exact timing is
|
||||
// specified in the .uplugin file per-module
|
||||
|
||||
// Get the base directory of this plugin
|
||||
// FString BaseDir = IPluginManager::Get().FindPlugin("AndroidVulkanVideo")->GetBaseDir();
|
||||
//
|
||||
}
|
||||
|
||||
void ShutdownModule()
|
||||
{
|
||||
// This function may be called during shutdown to clean up your module. For modules that
|
||||
// support dynamic reloading, we call this function before unloading the module.
|
||||
}
|
||||
};
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FVulkanVideoMeshComponentModule, VulkanVideoMeshComponent)
|
@ -0,0 +1,188 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// Attaches to a static mesh, and allows the vertex data
|
||||
// to be passed into the vulkanimpl for rendering
|
||||
// ------------------------------------------------
|
||||
#pragma once
|
||||
|
||||
#include "Engine/StaticMesh.h"
|
||||
#include "Engine/StaticMeshActor.h"
|
||||
#include "IMediaEventSink.h"
|
||||
#include "MediaObjectPool.h"
|
||||
#include "MediaPlayer.h"
|
||||
#include "MediaPlayerFacade.h"
|
||||
#include "MediaSampleSink.h"
|
||||
|
||||
#include "Logging/StructuredLog.h"
|
||||
|
||||
#include "IVulkanVertexData.h"
|
||||
|
||||
#include "RendererInterface.h"
|
||||
|
||||
#include "DirectVideoMeshRenderer.generated.h"
|
||||
|
||||
class AndroidVulkanTextureSample;
|
||||
|
||||
class FRendererSceneViewExt;
|
||||
|
||||
class FRawStaticIndexBuffer;
|
||||
struct FStaticMeshVertexBuffers;
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogDirectVideoMeshRenderer, Log, All);
|
||||
|
||||
UCLASS(Blueprintable, ClassGroup = (Rendering, Common),
|
||||
hidecategories = (Object, Activation, "Components|Activation"), ShowCategories = (Mobility),
|
||||
editinlinenew, meta = (BlueprintSpawnableComponent))
|
||||
class VULKANVIDEOMESHCOMPONENT_API UDirectVideoMeshRendererComponent : public UActorComponent
|
||||
{
|
||||
friend class FRendererSceneViewExt;
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UDirectVideoMeshRendererComponent();
|
||||
~UDirectVideoMeshRendererComponent();
|
||||
|
||||
void HandlePlayerMediaEvent(EMediaSampleSinkEvent Event);
|
||||
|
||||
void DeInit();
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Components|DirectVideo")
|
||||
UMediaPlayer *LinkedPlayer;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Components|DirectVideo")
|
||||
bool HideMeshWhileWeHaveVideo;
|
||||
|
||||
// FMediaTextureSampleSink (called indirectly from _TextureSink)
|
||||
virtual bool Enqueue(const TSharedRef<IMediaTextureSample, ESPMode::ThreadSafe> &Sample);
|
||||
virtual int32 Num() const
|
||||
{
|
||||
if (CurrentSample.IsValid())
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
virtual void RequestFlush()
|
||||
{
|
||||
CurrentSample.Reset();
|
||||
PreparedSample.Reset();
|
||||
}
|
||||
|
||||
virtual void BeginDestroy() override;
|
||||
|
||||
protected:
|
||||
// draw to a command list (which is already in a render pass with outputs set up)
|
||||
void DrawToCommandList(FRHICommandList &RHICmdList, FSceneView &InView);
|
||||
// load mesh, update matrices, create vulkan images (must be outside render pass)
|
||||
void DoWorkOutsideRenderPass(FRHICommandList &RHICmdList, FSceneViewFamily &InViewFamily);
|
||||
|
||||
void UpdateVisibility();
|
||||
|
||||
class _TextureSink : public FMediaTextureSampleSink
|
||||
{
|
||||
public:
|
||||
_TextureSink(UDirectVideoMeshRendererComponent *InOwner)
|
||||
{
|
||||
Owner = InOwner;
|
||||
FlushCount = 0;
|
||||
EventDelegate =
|
||||
OnMediaSampleSinkEvent().AddRaw(this, &_TextureSink::ReceiveEventHandler);
|
||||
}
|
||||
|
||||
virtual ~_TextureSink()
|
||||
{
|
||||
OnMediaSampleSinkEvent().Remove(EventDelegate);
|
||||
}
|
||||
virtual bool Enqueue(
|
||||
const TSharedRef<IMediaTextureSample, ESPMode::ThreadSafe> &Sample) override
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, Verbose, "Enqueue");
|
||||
auto POwner = Owner.Get();
|
||||
if (POwner != NULL)
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, Verbose, "Enqueue2");
|
||||
return POwner->Enqueue(Sample);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
virtual int32 Num() const override
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, Verbose, "Num");
|
||||
auto POwner = Owner.Get();
|
||||
if (POwner != NULL)
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, Verbose, "Num2");
|
||||
return POwner->Num();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
virtual void RequestFlush() override
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, Verbose, "Flush");
|
||||
|
||||
FlushCount += 1;
|
||||
auto POwner = Owner.Get();
|
||||
if (POwner != NULL)
|
||||
{
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, Verbose, "Flush2");
|
||||
POwner->RequestFlush();
|
||||
}
|
||||
}
|
||||
|
||||
void ReceiveEventHandler(EMediaSampleSinkEvent Event, const FMediaSampleSinkEventData &Data)
|
||||
{
|
||||
int iEvent = (int)Event;
|
||||
UE_LOGFMT(LogDirectVideoMeshRenderer, Verbose, "On event:{1}", iEvent);
|
||||
auto POwner = Owner.Get();
|
||||
if (POwner != NULL)
|
||||
{
|
||||
POwner->HandlePlayerMediaEvent(Event);
|
||||
}
|
||||
}
|
||||
|
||||
TWeakObjectPtr<UDirectVideoMeshRendererComponent> Owner;
|
||||
int FlushCount;
|
||||
|
||||
FDelegateHandle EventDelegate;
|
||||
};
|
||||
|
||||
void Initialize();
|
||||
|
||||
void GetMeshObject();
|
||||
bool ConstructMeshFromRenderData(const FStaticMeshVertexBuffers &VertexBuffers,
|
||||
const FRawStaticIndexBuffer &IndexBuffer);
|
||||
void GetShaderInfo(const FSceneView *InView, float *m44, float *vp44,
|
||||
float *pre_view_translation);
|
||||
virtual void TickComponent(float DeltaTime, ELevelTick TickType,
|
||||
FActorComponentTickFunction *ThisTickFunction) override;
|
||||
|
||||
virtual void Serialize(FArchive &Ar) override;
|
||||
|
||||
UPROPERTY()
|
||||
bool HasMesh;
|
||||
// raw mesh data as passed to RenderToMesh
|
||||
// we save this in UObject::Serialize so that
|
||||
// we don't need to load it from the mesh at
|
||||
// runtime
|
||||
UPROPERTY()
|
||||
TArray<float> PositionsAsFloats; // x,y,z,u,v (floats)
|
||||
|
||||
// vertexData is not a UStruct because we pass it through to
|
||||
// native android code
|
||||
TArray<VertexData> Positions; // x,y,z,u,v (floats)
|
||||
|
||||
UPROPERTY()
|
||||
TArray<uint32> Indices; // index buffer for mesh (uint32)
|
||||
|
||||
TSharedPtr<_TextureSink> TextureSink;
|
||||
|
||||
TSharedPtr<IMediaTextureSample, ESPMode::ThreadSafe> CurrentSample;
|
||||
TSharedPtr<IMediaTextureSample, ESPMode::ThreadSafe> PreparedSample;
|
||||
|
||||
TWeakPtr<FMediaPlayerFacade, ESPMode::ThreadSafe> Facade;
|
||||
|
||||
TObjectPtr<AStaticMeshActor> MeshActor;
|
||||
|
||||
TSharedPtr<FRendererSceneViewExt, ESPMode::ThreadSafe> ExtensionHolder;
|
||||
|
||||
bool Initialized;
|
||||
};
|
@ -0,0 +1,73 @@
|
||||
// ------------------------------------------------
|
||||
// Copyright Joe Marshall 2024- All Rights Reserved
|
||||
// ------------------------------------------------
|
||||
//
|
||||
// Build AndroidVulkanVideo factory module (to make
|
||||
// it possible to select this as a video source in
|
||||
// editor.)
|
||||
// ------------------------------------------------
|
||||
|
||||
using UnrealBuildTool;
|
||||
using System.IO;
|
||||
using System;
|
||||
|
||||
public class VulkanVideoMeshComponent : ModuleRules
|
||||
{
|
||||
public VulkanVideoMeshComponent(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
CppStandard = CppStandardVersion.Cpp17;
|
||||
|
||||
|
||||
PublicIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add public include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// ... add other private include paths required here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
"Projects",
|
||||
"CoreUObject", "Engine","RHI","VulkanRHI","MediaUtils"
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core","CoreUObject", "Engine","RHI","RenderCore","VulkanRHI","MediaUtils","Media","MediaAssets"
|
||||
// ... add private dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
PrivateIncludePathModuleNames.AddRange(
|
||||
new string[] {
|
||||
"AndroidVulkanVideo",
|
||||
});
|
||||
|
||||
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
}
|
||||
);
|
||||
|
||||
if (Target.Platform == UnrealTargetPlatform.Android)
|
||||
{
|
||||
DynamicallyLoadedModuleNames.Add("AndroidVulkanVideo");
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user