Keywords: UE4, RHI (Rendering Hardware Interface) Notes

Usages

How to convert Texture2D asset to pixel of image in raw data format

Example:
https://github.com/EpicGames/UnrealEngine/blob/release/Engine/Source/Runtime/ImageWriteQueue/Private/ImageWriteBlueprintLibrary.cpp#L59

FTextureResource* TextureResource = InTexture->Resource;
ENQUEUE_RENDER_COMMAND(ResolvePixelData)(
    [TextureResource, OnPixelsReady](FRHICommandListImmediate& RHICmdList)
    {
        FTexture2DRHIRef Texture2D = TextureResource->TextureRHI ? TextureResource->TextureRHI->GetTexture2D() : nullptr;
        if (!Texture2D)
        {
            OnPixelsReady(nullptr);
            return;
        }

        FIntRect SourceRect(0, 0, Texture2D->GetSizeX(), Texture2D->GetSizeY());
        switch (Texture2D->GetFormat())
        {
            case PF_FloatRGBA:
            {
                TArray<FFloat16Color> RawPixels;
                RawPixels.SetNum(SourceRect.Width() * SourceRect.Height());
                RHICmdList.ReadSurfaceFloatData(Texture2D, SourceRect, RawPixels, (ECubeFace)0, 0, 0);

                TUniquePtr<TImagePixelData<FFloat16Color>> PixelData = MakeUnique<TImagePixelData<FFloat16Color>>(SourceRect.Size(), TArray64<FFloat16Color>(MoveTemp(RawPixels)));

                if (PixelData->IsDataWellFormed())
                {
                    OnPixelsReady(MoveTemp(PixelData));
                    return;
                }

                break;
            }

            case PF_A32B32G32R32F:
            {
                FReadSurfaceDataFlags ReadDataFlags(RCM_MinMax);
                ReadDataFlags.SetLinearToGamma(false);


                TArray<FLinearColor> RawPixels;
                RawPixels.SetNum(SourceRect.Width() * SourceRect.Height());
                RHICmdList.ReadSurfaceData(Texture2D, SourceRect, RawPixels, ReadDataFlags);
                TUniquePtr<TImagePixelData<FLinearColor>> PixelData = MakeUnique<TImagePixelData<FLinearColor>>(SourceRect.Size(), TArray64<FLinearColor>(MoveTemp(RawPixels)));
                if (PixelData->IsDataWellFormed())
                {
                    OnPixelsReady(MoveTemp(PixelData));
                    return;
                }

                break;
            }

            case PF_R8G8B8A8:
            case PF_B8G8R8A8:
            {
                FReadSurfaceDataFlags ReadDataFlags;
                ReadDataFlags.SetLinearToGamma(false);

                TArray<FColor> RawPixels;
                RawPixels.SetNum(SourceRect.Width() * SourceRect.Height());
                RHICmdList.ReadSurfaceData(Texture2D, SourceRect, RawPixels, ReadDataFlags);

                TUniquePtr<TImagePixelData<FColor>> PixelData = MakeUnique<TImagePixelData<FColor>>(SourceRect.Size(), TArray64<FColor>(MoveTemp(RawPixels)));
                if (PixelData->IsDataWellFormed())
                {
                    OnPixelsReady(MoveTemp(PixelData));
                    return;
                }

                break;
            }

            default:
                break;
        }

        OnPixelsReady(nullptr);
    }
);
How to convert image raw data to Texture2D asset

Example:
https://github.com/emilioC33/ITUFast/blob/529ee2e13857e95d3f1e2808dcafc84cd1097978/UnrealRacing/Plugins/socketio-client-ue4/Source/CoreUtility/Private/CoreUtilityBPLibrary.cpp#L71

//testing image path
FString ImagePath = { "C:\\Sven_icon.png" };

//image raw data
TArray<uint8> ImageRawData;
FFileHelper::LoadFileToArray(ImageRawData, *ImagePath);

//Convert the UTexture2D back to an image
UTexture2D* Texture = nullptr;

static IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
EImageFormat DetectedFormat = ImageWrapperModule.DetectImageFormat(ImageRawData.GetData(), ImageRawData.Num());

TSharedPtr<IImageWrapper> ImageWrapper = ImageWrapperModule.CreateImageWrapper(DetectedFormat);

//Set the compressed bytes - we need this information on game thread to be able to determine texture size, otherwise we'll need a complete async callback
if (ImageWrapper.IsValid() && ImageWrapper->SetCompressed(ImageRawData.GetData(), ImageRawData.Num()))
{
    //Create image given sizes
    Texture = UTexture2D::CreateTransient(ImageWrapper->GetWidth(), ImageWrapper->GetHeight(), PF_B8G8R8A8);
    Texture->UpdateResource();

    //Uncompress on a background thread pool
    Async(EAsyncExecution::ThreadPool, [ImageWrapper, Texture] {
        TArray<uint8> UncompressedBGRA;
        if (ImageWrapper->GetRaw(ERGBFormat::BGRA, 8, UncompressedBGRA))
        {

            FUpdateTextureData* UpdateData = new FUpdateTextureData;
            UpdateData->Texture2D = Texture;
            UpdateData->Region = FUpdateTextureRegion2D(0, 0, 0, 0, Texture->GetSizeX(), Texture->GetSizeY());
            UpdateData->BufferArray = &UncompressedBGRA;
            UpdateData->Pitch = Texture->GetSizeX() * 4;
            UpdateData->Wrapper = ImageWrapper;

            ENQUEUE_RENDER_COMMAND(UpdateTextureDataCommand)([UpdateData](FRHICommandListImmediate& RHICmdList) {
                RHIUpdateTexture2D(
                    ((FTexture2DResource*)UpdateData->Texture2D->Resource)->GetTexture2DRHI(),
                    0,
                    UpdateData->Region,
                    UpdateData->Pitch,
                    UpdateData->BufferArray->GetData()
                );
                delete UpdateData; //now that we've updated the texture data, we can finally release any data we're holding on to
                });//End Enqueue
        }
        }
        );
}
else
{
    UE_LOG(LogTemp, Warning, TEXT("Invalid image format cannot decode %d"), (int32)DetectedFormat);
}
How to invoke functions of game source in render thread
namespace
{
    void DoTesting_OnRenderThread(FRHICommandListImmediate& RHICmdList, AMyActor* Actor)
    {
        if(AMyActor)
        {
            AMyActor->DoMyTask();
        }
    }
}

void AMyCharacter::TestRender()
{
    AMyActor* Actor = CreateTestActor();
    
    ENQUEUE_RENDER_COMMAND(MyRenderingCommand)(
        [Actor](FRHICommandListImmediate& RHICmdList)
        {
            DoTesting_OnRenderThread(RHICmdList, Actor);
        });
}

Issues

Please use ENQUEUE_RENDER_COMMAND instead Please update your code to the new API before upgrading to the next release

Warning on compiling:

warning: Please use ENQUEUE_RENDER_COMMAND instead Please update your code to the new API before upgrading to the next release, otherwise your project will no longer compile. [-W#pragma-messages]

Caused by:
ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER was obsolete, use ENQUEUE_RENDER_COMMAND instead.

Solution:

MyRenderingHandle* Handle = CreateMyRenderingHandle();

ENQUEUE_RENDER_COMMAND(MyRenderingCommand)(
[Handle](FRHICommandListImmediate& RHICmdList)
{
    Handle->ExecuteMyRenderTask();
});

Reference:
UE4.24源码分析 - ENQUEUE_RENDER_COMMAND
https://zhuanlan.zhihu.com/p/149917554

How to use ENQUEUE_RENDER_COMMAND instead of ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER
https://forums.unrealengine.com/development-discussion/engine-source-github/1608365-how-to-use-enqueue_render_command-instead-of-enqueue_unique_render_command_oneparameter


The leaders who work most effectively, it seems to me, never say "I." And that's not because they have trained themselves not to say "I." They don't think "I." They think "we"; they think "team." They understand their job to be to make the team function. They accept responsibility and don't sidestep it, but "we" gets the credit. This is what creates trust, what enables you to get the task done. ― Peter Drucker