Files
TerracottaWarriors/Plugins/AndroidVulkanVideo_5.3/Source/AndroidVulkanVideo/Private/AndroidVulkanTextureSample.cpp
2025-07-14 22:24:27 +08:00

400 lines
13 KiB
C++

// ------------------------------------------------
// 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;