UE4引擎的CopyTexture, CopyToResolveTarget

前言

在图形渲染各种算法上,特别是对2D纹理的处理上,经常出现一种情况,我们需要将一张纹理的一部分拷贝到另外一张纹理上。如下所示:

 

 UE4提供了CopyTextureCopyToResolveTarget来实现拷贝一张纹理一部分到另外一张纹理上的类似功能。

CopyTexture

CopyTexture的基础介绍

struct FRHICopyTextureInfo
{
	// Number of texels to copy. By default it will copy the whole resource if no size is specified.
	FIntVector Size = FIntVector::ZeroValue;

	// Position of the copy from the source texture/to destination texture
	FIntVector SourcePosition = FIntVector::ZeroValue;
	FIntVector DestPosition = FIntVector::ZeroValue;

	uint32 SourceSliceIndex = 0;
	uint32 DestSliceIndex = 0;
	uint32 NumSlices = 1;

	// Mips to copy and destination mips
	uint32 SourceMipIndex = 0;
	uint32 DestMipIndex = 0;
	uint32 NumMips = 1;
};

CopyTexture的SourceTextureRHI和DestTextureRHI适用于UE4的UTexture2D, UTextureRenderTarget2D, UTexture2DArray等几种纹理类型进行相关拷贝。

拷贝纹理参数FRHICopyTextureInfo

FRHICopyTextureInfo包含了关于CopySrcTextureCopyDestTexture各种设定参数

Size: 拷贝纹理部分的大小

SourcePosition:SrcTexture拷贝部分的起始位置

DestPosition:DestTexture拷贝部分的起始位置

SourceSliceIndex:SrcTexture拷贝的部分所在SrcTexture纹理数组的数组index(普通纹理就是数组大小为1的纹理数组,默认SliceIndex等于0)

DestSliceIndex:DestTexture拷贝的部分所在DestTexture纹理数组的数组index(普通纹理就是数组大小为1的纹理数组,默认SliceIndex 等于0)

SourceMipIndexSrcTexture拷贝的部分是当前SrcTexture纹理的哪个Mip级

DestMipIndex: DestMipIndex拷贝的部分是当前DestMipIndex纹理的哪个Mip级

NumMips: 这次进行进行N级Mip的拷贝(通常情况下,这个是1,一次一般只拷贝一级Mip)

总结:SrcTexture数组的第SourceSliceIndex个纹理的第SourceMipIndexMip级的[SourcePosition, SourcePosition+Size]范围拷贝到DestTexture数组的第DestSliceIndex个纹理的第DestMipIndex级的[DestPosition, DestPosition+Size]范围

示例

将一张Texture的每级Mip左上角四分之一拷贝到一张RenderTexture对应Mip级的左上角部分

	UFUNCTION(BlueprintCallable, Category = "Test Shader", meta = (WorldContext = "WorldContextObject"))
		static void CopyTextureToRenderTextureAllMips(
			const UObject* WorldContextObject, 
			const UTexture2D* InTexture,
			class UTextureRenderTarget2D* OutputRenderTarget);
void UTestCopyRenderFunctionLibrary::CopyTextureToRenderTextureAllMips(
	const UObject* WorldContextObject,
	const UTexture2D* InTexture,
	class UTextureRenderTarget2D* OutputRenderTargetA)
{
	UWorld* World = WorldContextObject->GetWorld();
	ERHIFeatureLevel::Type FeatureLevel = World->Scene->GetFeatureLevel();

	if (FeatureLevel < ERHIFeatureLevel::SM5)
	{
		UE_LOG(LogTemp, Warning, TEXT("FeatureLevel < ERHIFeatureLevel::SM5"));
		return;
	}

	if (nullptr == InTexture)
	{
		UE_LOG(LogTemp, Warning, TEXT("InTexture Is NULL"));
		return;
	}

	FTextureRenderTargetResource* TextureRenderTargetSource = OutputRenderTargetA->GameThread_GetRenderTargetResource();
	FTexture* TextureSource = InTexture->Resource;

	int32 CommonMip = FMath::Min(TextureRenderTargetSource->TextureRHI->GetNumMips(), TextureSource->TextureRHI->GetNumMips());

	ENQUEUE_RENDER_COMMAND(TestShaderCommand)(
		[TextureSource, TextureRenderTargetSource, FeatureLevel, CommonMip](FRHICommandListImmediate& RHICmdList)
		{
			if (CommonMip >= 2)
			{
				//Test Copy Subregion
				for (int32 MipIndex = 0; MipIndex < CommonMip - 1; ++MipIndex)
				{
					FRHICopyTextureInfo CopyInfo;
					CopyInfo.SourcePosition = FIntVector(0, 0, 0);
					CopyInfo.DestPosition = FIntVector(0, 0, 0);
					CopyInfo.SourceMipIndex = MipIndex;
					CopyInfo.DestMipIndex = MipIndex;
					CopyInfo.Size = FIntVector(TextureSource->GetSizeX() / FMath::Pow(2, MipIndex + 1), TextureSource->GetSizeY() / FMath::Pow(2, MipIndex + 1), TextureSource->GetSizeZ() / FMath::Pow(2, MipIndex + 1));
					RHICmdList.Transition(FRHITransitionInfo(TextureRenderTargetSource->TextureRHI, ERHIAccess::Unknown, ERHIAccess::CopyDest));
					RHICmdList.Transition(FRHITransitionInfo(TextureSource->TextureRHI, ERHIAccess::SRVMask, ERHIAccess::CopySrc));
					FRHITexture* Test = TextureSource->TextureRHI.GetReference();
					RHICmdList.CopyTexture(TextureSource->TextureRHI, TextureRenderTargetSource->TextureRHI, CopyInfo);
					RHICmdList.Transition(FRHITransitionInfo(TextureRenderTargetSource->TextureRHI, ERHIAccess::CopyDest, ERHIAccess::SRVMask));
					RHICmdList.Transition(FRHITransitionInfo(TextureRenderTargetSource->TextureRHI, ERHIAccess::CopySrc, ERHIAccess::SRVMask));
				}
			}
			else
			{
				FRHICopyTextureInfo CopyInfo;
				CopyInfo.SourcePosition = FIntVector(0, 0, 0);
				CopyInfo.DestPosition = FIntVector(0, 0, 0);
				CopyInfo.Size = FIntVector(TextureSource->GetSizeX() / 2, TextureSource->GetSizeY() /2, TextureSource->GetSizeZ() / 2);
				RHICmdList.Transition(FRHITransitionInfo(TextureRenderTargetSource->TextureRHI, ERHIAccess::Unknown, ERHIAccess::CopyDest));
				RHICmdList.Transition(FRHITransitionInfo(TextureSource->TextureRHI, ERHIAccess::SRVMask, ERHIAccess::CopySrc));
				RHICmdList.CopyTexture(TextureSource->TextureRHI, TextureRenderTargetSource->TextureRHI, CopyInfo);
				RHICmdList.Transition(FRHITransitionInfo(TextureRenderTargetSource->TextureRHI, ERHIAccess::CopyDest, ERHIAccess::SRVMask));
				RHICmdList.Transition(FRHITransitionInfo(TextureRenderTargetSource->TextureRHI, ERHIAccess::CopySrc, ERHIAccess::SRVMask));
			}

		}
	);

	FlushRenderingCommands();
}

SrcTexture: 512 * 512的存在10级Mip的Texture

DestTexture: 512 * 512的存在10级Mip的RenderTexture

 

 

 CopyTexture的使用注意事项

 [1]SrcTexture和DestTexture数组格式匹配,比如都说RGBA8或者RGBA16,格式不匹配可能导致程序崩溃

[2]像MipIndex,SliceIndex, Szie等参数注意不要越界(比如纹理数组Size为5, 在程序指定来SliceIndex = 5), 也可能导致程序崩溃.

[3]当CopySize为0时,会自动拷贝各级Mip, 进行整体拷贝,参考下面

CopyTexture的底层原理

UE4 CopyTexture使用RHI层的东西,在不同的平台下有不同的实现,在DX11下的实现:

 CopySubresourceRegion(CopySize不为0时) 和 CopyResource(CopySize等于0时)

 CopyToResolveTarget

 CopyToResolveTarget在UE4渲染线程经常使用,作用也说用来拷贝FTextureRHI,作用和CopyTexture很类似。

使用案例代码

void UTestCopyRenderFunctionLibrary::CopyToResolveTarget(
	const UObject* WorldContextObject,
	class UTexture2D* InTexture,
	const UTexture2D* OutTexture,
	FIntVector CopySize)
{
	UWorld* World = WorldContextObject->GetWorld();
	ERHIFeatureLevel::Type FeatureLevel = World->Scene->GetFeatureLevel();

	if (FeatureLevel < ERHIFeatureLevel::SM5)
	{
		UE_LOG(LogTemp, Warning, TEXT("FeatureLevel < ERHIFeatureLevel::SM5"));
		return;
	}

	if (nullptr == OutTexture)
	{
		UE_LOG(LogTemp, Warning, TEXT("InTexture Is NULL"));
		return;
	}


	FTexture* SourceTexture = InTexture->Resource;
	FTexture* TargetTexture = OutTexture->Resource;

	ENQUEUE_RENDER_COMMAND(TestShaderCommand)(
		[SourceTexture, TargetTexture, FeatureLevel, CopySize](FRHICommandListImmediate& RHICmdList)
		{
			FResolveParams ResolveParams;
			ResolveParams.Rect = FResolveRect(0, 0, SourceTexture->GetSizeX(), SourceTexture->GetSizeY());
			ResolveParams.DestRect = FResolveRect(0, 0, SourceTexture->GetSizeX(), SourceTexture->GetSizeY());
			RHICmdList.Transition(FRHITransitionInfo(TargetTexture->TextureRHI, ERHIAccess::Unknown, ERHIAccess::CopyDest));
			RHICmdList.Transition(FRHITransitionInfo(SourceTexture->TextureRHI, ERHIAccess::SRVMask, ERHIAccess::CopySrc));
			RHICmdList.CopyToResolveTarget(SourceTexture->TextureRHI, TargetTexture->TextureRHI, ResolveParams);
			RHICmdList.Transition(FRHITransitionInfo(TargetTexture->TextureRHI, ERHIAccess::CopyDest, ERHIAccess::SRVMask));
			RHICmdList.Transition(FRHITransitionInfo(SourceTexture->TextureRHI, ERHIAccess::CopySrc, ERHIAccess::SRVMask));
		});
}

 连参数的作用也很类似,不过严格来说CopyToResolveTarget使用 范围比CopyTexture要广,比如说普通纹理(UTexture2D, UTexture2DArray, UTextureRenderTarget2D)使用CopyTexture不存在什么问题,但是SceneDepthTexture这种Texture使用CopyTexture直接野蛮拷贝效果是问题的。

看看CopyToResolveTargetDX11的实现

 可以看到CopyToResolveTarget在针对DepthStencil类纹理,说采用定制的FResolveDepthPS来进行Copy的,而针对普通纹理,则和CopyTexture一样。 总体上CopyTexture只说CopyToResolveTarget功能上的一个子集。

 案例代码下载

https://download.csdn.net/download/qq_29523119/86242859

参考资料

https://docs.microsoft.com/zh-cn/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-copysubresourceregion

猜你喜欢

转载自blog.csdn.net/qq_29523119/article/details/125712958