// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= RHISurfaceDataConversion.h: RHI surface data conversions. =============================================================================*/ #pragma once #include "Math/Float16Color.h" #include "Math/PackedVector.h" #include "Math/Plane.h" #include "Math/UnrealMathUtility.h" #include "RHI.h" #include "RHITypes.h" namespace { /** Helper for accessing R10G10B10A2 colors. */ struct FRHIR10G10B10A2 { uint32 R : 10; uint32 G : 10; uint32 B : 10; uint32 A : 2; }; /** Helper for accessing R16G16B16A16 colors. */ struct FRHIRGBA16 { uint16 R; uint16 G; uint16 B; uint16 A; }; /** Helper for accessing R16G16 colors. */ struct FRHIRG16 { uint16 R; uint16 G; }; inline void ConvertRawR16DataToFColor(uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FColor* Out) { // e.g. shadow maps for (uint32 Y = 0; Y < Height; Y++) { uint16* SrcPtr = (uint16*)(In + Y * SrcPitch); FColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { uint16 Value16 = *SrcPtr; int Value = FColor::Requantize16to8(Value16); *DestPtr = FColor(Value, Value, Value); ++SrcPtr; ++DestPtr; } } } inline void ConvertRawR8G8B8A8DataToFColor(uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FColor* Out) { for (uint32 Y = 0; Y < Height; Y++) { FColor* SrcPtr = (FColor*)(In + Y * SrcPitch); FColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { *DestPtr = FColor(SrcPtr->B, SrcPtr->G, SrcPtr->R, SrcPtr->A); ++SrcPtr; ++DestPtr; } } } inline void ConvertRawB8G8R8A8DataToFColor(uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FColor* Out) { const uint32 DstPitch = Width * sizeof(FColor); // If source & dest pitch matches, perform a single memcpy. if (DstPitch == SrcPitch) { FPlatformMemory::Memcpy(Out, In, Width * Height * sizeof(FColor)); } else { check(SrcPitch > DstPitch); // Need to copy row wise since the Pitch does not match the Width. for (uint32 Y = 0; Y < Height; Y++) { FColor* SrcPtr = (FColor*)(In + Y * SrcPitch); FColor* DestPtr = Out + Y * Width; FMemory::Memcpy(DestPtr, SrcPtr, DstPitch); } } } inline void ConvertRawR16G16B16A16FDataToFFloat16Color(uint32 Width, uint32 Height, uint8* In, uint32 SrcPitch, FFloat16Color* Out) { const uint32 DstPitch = Width * sizeof(FFloat16Color); // If source & dest pitch matches, perform a single memcpy. if (DstPitch == SrcPitch) { FPlatformMemory::Memcpy(Out, In, Width * Height * sizeof(FFloat16Color)); } else { check(SrcPitch > DstPitch); // Need to copy row wise since the Pitch does not match the Width. for (uint32 Y = 0; Y < Height; Y++) { FFloat16Color* SrcPtr = (FFloat16Color*)(In + Y * SrcPitch); FFloat16Color* DestPtr = Out + Y * Width; FMemory::Memcpy(DestPtr, SrcPtr, DstPitch); } } } inline void ConvertRawR10G10B10A2DataToFColor(uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FColor* Out) { for (uint32 Y = 0; Y < Height; Y++) { FRHIR10G10B10A2* SrcPtr = (FRHIR10G10B10A2*)(In + Y * SrcPitch); FColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { *DestPtr = FColor::MakeRequantizeFrom1010102( SrcPtr->R, SrcPtr->G, SrcPtr->B, SrcPtr->A ); ++SrcPtr; ++DestPtr; } } } inline void ConvertRawB10G10R10A2DataToFColor(uint32 Width, uint32 Height, uint8* In, uint32 SrcPitch, FColor* Out) { for (uint32 Y = 0; Y < Height; Y++) { FRHIR10G10B10A2* SrcPtr = (FRHIR10G10B10A2*)(In + Y * SrcPitch); FColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { *DestPtr = FColor::MakeRequantizeFrom1010102( SrcPtr->B, SrcPtr->G, SrcPtr->R, SrcPtr->A ); ++SrcPtr; ++DestPtr; } } } inline void ConvertRawR16G16B16A16FDataToFColor(uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FColor* Out, bool LinearToGamma) { FPlane MinValue(0.0f, 0.0f, 0.0f, 0.0f), MaxValue(1.0f, 1.0f, 1.0f, 1.0f); check(sizeof(FFloat16) == sizeof(uint16)); for (uint32 Y = 0; Y < Height; Y++) { FFloat16* SrcPtr = (FFloat16*)(In + Y * SrcPitch); for (uint32 X = 0; X < Width; X++) { MinValue.X = FMath::Min(SrcPtr[0], MinValue.X); MinValue.Y = FMath::Min(SrcPtr[1], MinValue.Y); MinValue.Z = FMath::Min(SrcPtr[2], MinValue.Z); MinValue.W = FMath::Min(SrcPtr[3], MinValue.W); MaxValue.X = FMath::Max(SrcPtr[0], MaxValue.X); MaxValue.Y = FMath::Max(SrcPtr[1], MaxValue.Y); MaxValue.Z = FMath::Max(SrcPtr[2], MaxValue.Z); MaxValue.W = FMath::Max(SrcPtr[3], MaxValue.W); SrcPtr += 4; } } for (uint32 Y = 0; Y < Height; Y++) { FFloat16* SrcPtr = (FFloat16*)(In + Y * SrcPitch); FColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { *DestPtr = FLinearColor( (SrcPtr[0] - MinValue.X) / (MaxValue.X - MinValue.X), (SrcPtr[1] - MinValue.Y) / (MaxValue.Y - MinValue.Y), (SrcPtr[2] - MinValue.Z) / (MaxValue.Z - MinValue.Z), (SrcPtr[3] - MinValue.W) / (MaxValue.W - MinValue.W) ).ToFColor(LinearToGamma); SrcPtr += 4; ++DestPtr; } } } inline void ConvertRawR11G11B10DataToFColor(uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FColor* Out, bool LinearToGamma) { check(sizeof(FFloat3Packed) == sizeof(uint32)); for (uint32 Y = 0; Y < Height; Y++) { FFloat3Packed* SrcPtr = (FFloat3Packed*)(In + Y * SrcPitch); FColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { FLinearColor Value = (*SrcPtr).ToLinearColor(); *DestPtr = Value.ToFColor(LinearToGamma); ++SrcPtr; ++DestPtr; } } } inline void ConvertRawR9G9B9E5DataToFColor(uint32 Width, uint32 Height, uint8* In, uint32 SrcPitch, FColor* Out, bool LinearToGamma) { check(sizeof(FFloat3PackedSE) == sizeof(uint32)); for (uint32 Y = 0; Y < Height; Y++) { FFloat3PackedSE* SrcPtr = (FFloat3PackedSE*)(In + Y * SrcPitch); FColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { FLinearColor Value = (*SrcPtr).ToLinearColor(); *DestPtr = Value.ToFColor(LinearToGamma); ++SrcPtr; ++DestPtr; } } } inline void ConvertRawR32G32B32A32DataToFColor(uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FColor* Out, bool LinearToGamma) { FPlane MinValue(0.0f, 0.0f, 0.0f, 0.0f); FPlane MaxValue(1.0f, 1.0f, 1.0f, 1.0f); for (uint32 Y = 0; Y < Height; Y++) { float* SrcPtr = (float*)(In + Y * SrcPitch); for (uint32 X = 0; X < Width; X++) { MinValue.X = FMath::Min(SrcPtr[0], MinValue.X); MinValue.Y = FMath::Min(SrcPtr[1], MinValue.Y); MinValue.Z = FMath::Min(SrcPtr[2], MinValue.Z); MinValue.W = FMath::Min(SrcPtr[3], MinValue.W); MaxValue.X = FMath::Max(SrcPtr[0], MaxValue.X); MaxValue.Y = FMath::Max(SrcPtr[1], MaxValue.Y); MaxValue.Z = FMath::Max(SrcPtr[2], MaxValue.Z); MaxValue.W = FMath::Max(SrcPtr[3], MaxValue.W); SrcPtr += 4; } } for (uint32 Y = 0; Y < Height; Y++) { float* SrcPtr = (float*)(In + Y * SrcPitch); FColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { *DestPtr = FLinearColor( (SrcPtr[0] - MinValue.X) / (MaxValue.X - MinValue.X), (SrcPtr[1] - MinValue.Y) / (MaxValue.Y - MinValue.Y), (SrcPtr[2] - MinValue.Z) / (MaxValue.Z - MinValue.Z), (SrcPtr[3] - MinValue.W) / (MaxValue.W - MinValue.W) ).ToFColor(LinearToGamma); SrcPtr += 4; ++DestPtr; } } } inline void ConvertRawR24G8DataToFColor(uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FColor* Out, FReadSurfaceDataFlags InFlags) { bool bLinearToGamma = InFlags.GetLinearToGamma(); // Depth stencil for (uint32 Y = 0; Y < Height; Y++) { uint32* SrcPtr = (uint32 *)In; FColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { FColor NormalizedColor; if (InFlags.GetOutputStencil()) { uint8 DeviceStencil = (*SrcPtr & 0xFF000000) >> 24; NormalizedColor = FColor(DeviceStencil, DeviceStencil, DeviceStencil, 0xFF); } else { float DeviceZ = (*SrcPtr & 0xffffff) / (float)(1 << 24); float LinearValue = FMath::Min(InFlags.ComputeNormalizedDepth(DeviceZ), 1.0f); NormalizedColor = FLinearColor(LinearValue, LinearValue, LinearValue, 0).ToFColor(bLinearToGamma); } *DestPtr = NormalizedColor; ++SrcPtr; ++DestPtr; } } } inline void ConvertRawDepthStencil64DataToFColor(uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FColor* Out, FReadSurfaceDataFlags InFlags) { bool bLinearToGamma = InFlags.GetLinearToGamma(); for (uint32 Y = 0; Y < Height; Y++) { float* SrcPtr = (float *)(In + Y * SrcPitch); FColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { float DeviceZ = (*SrcPtr); float LinearValue = FMath::Min(InFlags.ComputeNormalizedDepth(DeviceZ), 1.0f); *DestPtr = FLinearColor(LinearValue, LinearValue, LinearValue, 0).ToFColor(bLinearToGamma); SrcPtr += 1; // todo: copies only depth, need to check how this format is read ++DestPtr; UE_LOG(LogRHI, Warning, TEXT("CPU read of R32G8X24 is not tested and may not function.")); } } } inline void ConvertRawR16G16B16A16DataToFColor(uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FColor* Out, bool bLinearToGamma = false) { for (uint32 Y = 0; Y < Height; Y++) { FRHIRGBA16* SrcPtr = (FRHIRGBA16*)(In + Y * SrcPitch); FColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { *DestPtr = FLinearColor( FColor::DequantizeUNorm16ToFloat(SrcPtr->R), FColor::DequantizeUNorm16ToFloat(SrcPtr->G), FColor::DequantizeUNorm16ToFloat(SrcPtr->B), FColor::DequantizeUNorm16ToFloat(SrcPtr->A) ).ToFColor(bLinearToGamma); ++SrcPtr; ++DestPtr; } } } inline void ConvertRawR16G16DataToFColor(uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FColor* Out) { for (uint32 Y = 0; Y < Height; Y++) { FRHIRG16* SrcPtr = (FRHIRG16*)(In + Y * SrcPitch); FColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { *DestPtr = FColor( FColor::Requantize16to8(SrcPtr->R), FColor::Requantize16to8(SrcPtr->G), 0); ++SrcPtr; ++DestPtr; } } } inline void ConvertRawR8DataToFColor(uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FColor* Out) { for (uint32 Y = 0; Y < Height; Y++) { uint8* SrcPtr = (uint8*)(In + Y * SrcPitch); FColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { *DestPtr = FColor(*SrcPtr, *SrcPtr, *SrcPtr, *SrcPtr); ++SrcPtr; ++DestPtr; } } } inline void ConvertRawR8G8DataToFColor(uint32 Width, uint32 Height, uint8* In, uint32 SrcPitch, FColor* Out) { for (uint32 Y = 0; Y < Height; Y++) { uint8* SrcPtr = (uint8*)(In + Y * SrcPitch); FColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { *DestPtr = FColor(*SrcPtr, *(SrcPtr + 1), 0); SrcPtr += 2; ++DestPtr; } } } inline void ConvertRawD32S8DataToFColor(uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FColor* Out, FReadSurfaceDataFlags InFlags) { bool bLinearToGamma = InFlags.GetLinearToGamma(); // Depth if (!InFlags.GetOutputStencil()) { for (uint32 Y = 0; Y < Height; Y++) { uint32* SrcPtr = (uint32*)(In + Y * SrcPitch); FColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { float DeviceZ = *SrcPtr; float LinearValue = FMath::Min(InFlags.ComputeNormalizedDepth(DeviceZ), 1.0f); *DestPtr = FLinearColor(LinearValue, LinearValue, LinearValue, 0).ToFColor(bLinearToGamma); ++DestPtr; ++SrcPtr; } } } // Stencil else { // Depth stencil for (uint32 Y = 0; Y < Height; Y++) { uint8* SrcPtr = (uint8*)(In + Y * SrcPitch); FColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { uint8 DeviceStencil = *SrcPtr; *DestPtr = FColor(DeviceStencil, DeviceStencil, DeviceStencil, 0xFF); ++SrcPtr; ++DestPtr; } } } } // Linear functions inline void ConvertRawR16UDataToFLinearColor(uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FLinearColor* Out) { // e.g. shadow maps for (uint32 Y = 0; Y < Height; Y++) { uint16* SrcPtr = (uint16*)(In + Y * SrcPitch); FLinearColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { uint16 Value16 = *SrcPtr; float Value = Value16 * (1.f/0xffff); *DestPtr = FLinearColor(Value, Value, Value); ++SrcPtr; ++DestPtr; } } } inline void ConvertRawR16FDataToFLinearColor(uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FLinearColor* Out) { // e.g. shadow maps for (uint32 Y = 0; Y < Height; Y++) { FFloat16 * SrcPtr = (FFloat16 *)(In + Y * SrcPitch); FLinearColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { float Value = SrcPtr->GetFloat(); *DestPtr = FLinearColor(Value, Value, Value); ++SrcPtr; ++DestPtr; } } } inline void ConvertRawR8G8B8A8DataToFLinearColor(uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FLinearColor* Out) { // Read the data out of the buffer, converting it from ABGR to ARGB. for (uint32 Y = 0; Y < Height; Y++) { FColor* SrcPtr = (FColor*)(In + Y * SrcPitch); FLinearColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { FColor sRGBColor = FColor(SrcPtr->B, SrcPtr->G, SrcPtr->R, SrcPtr->A); // swap RB // FLinearColor(FColor) does SRGB -> Linear *DestPtr = FLinearColor(sRGBColor); ++SrcPtr; ++DestPtr; } } } inline void ConvertRawB8G8R8A8DataToFLinearColor(uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FLinearColor* Out) { for (uint32 Y = 0; Y < Height; Y++) { FColor* SrcPtr = (FColor*)(In + Y * SrcPitch); FLinearColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { // FLinearColor(FColor) does SRGB -> Linear *DestPtr = FLinearColor(*SrcPtr); ++SrcPtr; ++DestPtr; } } } inline void ConvertRawA2B10G10R10DataToFLinearColor(uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FLinearColor* Out) { // Read the data out of the buffer, converting it from R10G10B10A2 to FLinearColor. for (uint32 Y = 0; Y < Height; Y++) { FRHIR10G10B10A2* SrcPtr = (FRHIR10G10B10A2*)(In + Y * SrcPitch); FLinearColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { *DestPtr = FLinearColor( (float)SrcPtr->R / 1023.0f, (float)SrcPtr->G / 1023.0f, (float)SrcPtr->B / 1023.0f, (float)SrcPtr->A / 3.0f ); ++SrcPtr; ++DestPtr; } } } inline void ConvertRawR16G16B16A16FDataToFLinearColor(uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FLinearColor* Out, FReadSurfaceDataFlags InFlags) { if (InFlags.GetCompressionMode() == RCM_MinMax) { for (uint32 Y = 0; Y < Height; Y++) { FFloat16* SrcPtr = (FFloat16*)(In + Y * SrcPitch); FLinearColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { *DestPtr = FLinearColor((float)SrcPtr[0], (float)SrcPtr[1], (float)SrcPtr[2], (float)SrcPtr[3]); ++DestPtr; SrcPtr += 4; } } } else { FPlane MinValue(0.0f, 0.0f, 0.0f, 0.0f); FPlane MaxValue(1.0f, 1.0f, 1.0f, 1.0f); check(sizeof(FFloat16) == sizeof(uint16)); for (uint32 Y = 0; Y < Height; Y++) { FFloat16* SrcPtr = (FFloat16*)(In + Y * SrcPitch); for (uint32 X = 0; X < Width; X++) { MinValue.X = FMath::Min(SrcPtr[0], MinValue.X); MinValue.Y = FMath::Min(SrcPtr[1], MinValue.Y); MinValue.Z = FMath::Min(SrcPtr[2], MinValue.Z); MinValue.W = FMath::Min(SrcPtr[3], MinValue.W); MaxValue.X = FMath::Max(SrcPtr[0], MaxValue.X); MaxValue.Y = FMath::Max(SrcPtr[1], MaxValue.Y); MaxValue.Z = FMath::Max(SrcPtr[2], MaxValue.Z); MaxValue.W = FMath::Max(SrcPtr[3], MaxValue.W); SrcPtr += 4; } } for (uint32 Y = 0; Y < Height; Y++) { FFloat16* SrcPtr = (FFloat16*)(In + Y * SrcPitch); FLinearColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { *DestPtr = FLinearColor( (SrcPtr[0] - MinValue.X) / (MaxValue.X - MinValue.X), (SrcPtr[1] - MinValue.Y) / (MaxValue.Y - MinValue.Y), (SrcPtr[2] - MinValue.Z) / (MaxValue.Z - MinValue.Z), (SrcPtr[3] - MinValue.W) / (MaxValue.W - MinValue.W) ); ++DestPtr; SrcPtr += 4; } } } } inline void ConvertRawR11G11B10FDataToFLinearColor(uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FLinearColor* Out) { check(sizeof(FFloat3Packed) == sizeof(uint32)); for (uint32 Y = 0; Y < Height; Y++) { FFloat3Packed* SrcPtr = (FFloat3Packed*)(In + Y * SrcPitch); FLinearColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { *DestPtr = (*SrcPtr).ToLinearColor(); ++DestPtr; ++SrcPtr; } } } inline void ConvertRawR32G32B32A32DataToFLinearColor(uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FLinearColor* Out, FReadSurfaceDataFlags InFlags) { if (InFlags.GetCompressionMode() == RCM_MinMax) { // Copy data directly, respecting existing min-max values FLinearColor* SrcPtr = (FLinearColor*)In; FLinearColor* DestPtr = (FLinearColor*)Out; const int32 ImageSize = sizeof(FLinearColor) * Height * Width; FMemory::Memcpy(DestPtr, SrcPtr, ImageSize); } else { // Normalize data FPlane MinValue(0.0f, 0.0f, 0.0f, 0.0f); FPlane MaxValue(1.0f, 1.0f, 1.0f, 1.0f); for (uint32 Y = 0; Y < Height; Y++) { float* SrcPtr = (float*)(In + Y * SrcPitch); for (uint32 X = 0; X < Width; X++) { MinValue.X = FMath::Min(SrcPtr[0], MinValue.X); MinValue.Y = FMath::Min(SrcPtr[1], MinValue.Y); MinValue.Z = FMath::Min(SrcPtr[2], MinValue.Z); MinValue.W = FMath::Min(SrcPtr[3], MinValue.W); MaxValue.X = FMath::Max(SrcPtr[0], MaxValue.X); MaxValue.Y = FMath::Max(SrcPtr[1], MaxValue.Y); MaxValue.Z = FMath::Max(SrcPtr[2], MaxValue.Z); MaxValue.W = FMath::Max(SrcPtr[3], MaxValue.W); SrcPtr += 4; } } float* SrcPtr = (float*)In; for (uint32 Y = 0; Y < Height; Y++) { FLinearColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { *DestPtr = FLinearColor( (SrcPtr[0] - MinValue.X) / (MaxValue.X - MinValue.X), (SrcPtr[1] - MinValue.Y) / (MaxValue.Y - MinValue.Y), (SrcPtr[2] - MinValue.Z) / (MaxValue.Z - MinValue.Z), (SrcPtr[3] - MinValue.W) / (MaxValue.W - MinValue.W) ); ++DestPtr; SrcPtr += 4; } } } } inline void ConvertRawR24G8DataToFLinearColor(uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FLinearColor* Out, FReadSurfaceDataFlags InFlags) { // Depth stencil for (uint32 Y = 0; Y < Height; Y++) { uint32* SrcPtr = (uint32 *)In; FLinearColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { float DeviceStencil = 0.0f; DeviceStencil = (float)((*SrcPtr & 0xFF000000) >> 24) / 255.0f; float DeviceZ = (*SrcPtr & 0xffffff) / (float)(1 << 24); float LinearValue = FMath::Min(InFlags.ComputeNormalizedDepth(DeviceZ), 1.0f); *DestPtr = FLinearColor(LinearValue, DeviceStencil, 0.0f, 0.0f); ++DestPtr; ++SrcPtr; } } } inline void ConvertRawDepthStencil64DataToFLinearColor(uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FLinearColor* Out, FReadSurfaceDataFlags InFlags) { // Depth stencil for (uint32 Y = 0; Y < Height; Y++) { uint8* SrcStart = (uint8 *)(In + Y * SrcPitch); FLinearColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { float DeviceZ = *((float *)(SrcStart)); float LinearValue = FMath::Min(InFlags.ComputeNormalizedDepth(DeviceZ), 1.0f); float DeviceStencil = (float)(*(SrcStart + 4)) / 255.0f; *DestPtr = FLinearColor(LinearValue, DeviceStencil, 0.0f, 0.0f); SrcStart += 8; //64 bit format with the last 24 bit ignore ++DestPtr; } } } inline void ConvertRawR16G16B16A16DataToFLinearColor(uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FLinearColor* Out) { // Read the data out of the buffer, converting it to FLinearColor. for (uint32 Y = 0; Y < Height; Y++) { FRHIRGBA16* SrcPtr = (FRHIRGBA16*)(In + Y * SrcPitch); FLinearColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { *DestPtr = FLinearColor( (float)SrcPtr->R / 65535.0f, (float)SrcPtr->G / 65535.0f, (float)SrcPtr->B / 65535.0f, (float)SrcPtr->A / 65535.0f ); ++SrcPtr; ++DestPtr; } } } inline void ConvertRawR16G16DataToFLinearColor(uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FLinearColor* Out) { // Read the data out of the buffer, converting it to FLinearColor. for (uint32 Y = 0; Y < Height; Y++) { FRHIRG16* SrcPtr = (FRHIRG16*)(In + Y * SrcPitch); FLinearColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { *DestPtr = FLinearColor( (float)SrcPtr->R / 65535.0f, (float)SrcPtr->G / 65535.0f, 0); ++SrcPtr; ++DestPtr; } } } bool ConvertRAWSurfaceDataToFLinearColor(EPixelFormat Format, uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FLinearColor* Out, FReadSurfaceDataFlags InFlags) { // InFlags.GetLinearToGamma() is ignored by the FLinearColor reader // Flags RCM_MinMax means pass the values out unchanged // default flags RCM_UNorm rescales them to [0,1] if they were outside that range if (Format == PF_G16 || Format == PF_R16_UINT ) { ConvertRawR16UDataToFLinearColor(Width, Height, In, SrcPitch, Out); return true; } else if (Format == PF_R16F || Format == PF_R16F_FILTER) { ConvertRawR16FDataToFLinearColor(Width, Height, In, SrcPitch, Out); return true; } else if (Format == PF_R8G8B8A8) { ConvertRawR8G8B8A8DataToFLinearColor(Width, Height, In, SrcPitch, Out); return true; } else if (Format == PF_B8G8R8A8) { ConvertRawB8G8R8A8DataToFLinearColor(Width, Height, In, SrcPitch, Out); return true; } else if (Format == PF_A2B10G10R10) { ConvertRawA2B10G10R10DataToFLinearColor(Width, Height, In, SrcPitch, Out); return true; } else if (Format == PF_FloatRGBA) { ConvertRawR16G16B16A16FDataToFLinearColor(Width, Height, In, SrcPitch, Out, InFlags); return true; } else if (Format == PF_FloatRGB || Format == PF_FloatR11G11B10) { // assume FloatRGB == PF_FloatR11G11B10 always here check( GPixelFormats[Format].BlockBytes == 4 ); ConvertRawR11G11B10FDataToFLinearColor(Width, Height, In, SrcPitch, Out); return true; } else if (Format == PF_A32B32G32R32F) { ConvertRawR32G32B32A32DataToFLinearColor(Width, Height, In, SrcPitch, Out, InFlags); return true; } else if ( Format == PF_D24 || ( (Format == PF_X24_G8 || Format == PF_DepthStencil ) && GPixelFormats[Format].BlockBytes == 4 ) ) { // see CVarD3D11UseD24/CVarD3D12UseD24 ConvertRawR24G8DataToFLinearColor(Width, Height, In, SrcPitch, Out, InFlags); return true; } else if ( (Format == PF_X24_G8 || Format == PF_DepthStencil ) && GPixelFormats[Format].BlockBytes > 4 ) { // @@ D3D 11/12 different? /** D3D11 and 12 formats are almost the same, one difference : DepthStencil formats are interleaved DepthStencil formats are planar. For example a format of 24 bits of depth, 8 bits of stencil would be stored in the format 24/8/24/8... etc in Direct3D 11, but as 24/24/24... followed by 8/8/8... in Direct3D 12. Note that each plane is its own subresource in D3D12 **/ ConvertRawDepthStencil64DataToFLinearColor(Width, Height, In, SrcPitch, Out, InFlags); return true; } else if (Format == PF_A16B16G16R16) { ConvertRawR16G16B16A16DataToFLinearColor(Width, Height, In, SrcPitch, Out); return true; } else if (Format == PF_G16R16) { ConvertRawR16G16DataToFLinearColor(Width, Height, In, SrcPitch, Out); return true; } else if (Format == PF_G16R16F) { // Read the data out of the buffer, converting it to FLinearColor. for (uint32 Y = 0; Y < Height; Y++) { FFloat16 * SrcPtr = (FFloat16*)(In + Y * SrcPitch); FLinearColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { *DestPtr = FLinearColor( SrcPtr[0].GetFloat(), SrcPtr[1].GetFloat(), 0.f,1.f); SrcPtr += 2; ++DestPtr; } } return true; } else if (Format == PF_G32R32F) { // not doing MinMax/Unorm remap here // Read the data out of the buffer, converting it to FLinearColor. for (uint32 Y = 0; Y < Height; Y++) { float * SrcPtr = (float *)(In + Y * SrcPitch); FLinearColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { *DestPtr = FLinearColor( SrcPtr[0], SrcPtr[1], 0.f, 1.f ); SrcPtr += 2; ++DestPtr; } } return true; } else if (Format == PF_R32_FLOAT) { // not doing MinMax/Unorm remap here // Read the data out of the buffer, converting it to FLinearColor. for (uint32 Y = 0; Y < Height; Y++) { float * SrcPtr = (float *)(In + Y * SrcPitch); FLinearColor* DestPtr = Out + Y * Width; for (uint32 X = 0; X < Width; X++) { *DestPtr = FLinearColor( SrcPtr[0], 0.f, 0.f, 1.f ); ++SrcPtr; ++DestPtr; } } return true; } else { // not supported yet check(0); return false; } } bool ConvertRAWSurfaceDataToFColor(EPixelFormat Format, uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FColor* Out, FReadSurfaceDataFlags InFlags) { bool bLinearToGamma = InFlags.GetLinearToGamma(); if (Format == PF_G16 || Format == PF_R16_UINT) { ConvertRawR16DataToFColor(Width, Height, In, SrcPitch, Out); return true; } else if (Format == PF_R8G8B8A8) { ConvertRawR8G8B8A8DataToFColor(Width, Height, In, SrcPitch, Out); return true; } else if (Format == PF_B8G8R8A8) { ConvertRawB8G8R8A8DataToFColor(Width, Height, In, SrcPitch, Out); return true; } else if (Format == PF_A2B10G10R10) { ConvertRawR10G10B10A2DataToFColor(Width, Height, In, SrcPitch, Out); return true; } else if (Format == PF_FloatRGBA) { ConvertRawR16G16B16A16FDataToFColor(Width, Height, In, SrcPitch, Out, bLinearToGamma); return true; } else if (Format == PF_FloatRGB || Format == PF_FloatR11G11B10) { ConvertRawR11G11B10DataToFColor(Width, Height, In, SrcPitch, Out, bLinearToGamma); return true; } else if (Format == PF_A32B32G32R32F) { ConvertRawR32G32B32A32DataToFColor(Width, Height, In, SrcPitch, Out, bLinearToGamma); return true; } else if (Format == PF_D24 || ((Format == PF_X24_G8 || Format == PF_DepthStencil) && GPixelFormats[Format].BlockBytes == 4)) { ConvertRawR24G8DataToFColor(Width, Height, In, SrcPitch, Out, InFlags); return true; } else if (Format == PF_A16B16G16R16) { ConvertRawR16G16B16A16DataToFColor(Width, Height, In, SrcPitch, Out); return true; } else if (Format == PF_G16R16) { ConvertRawR16G16DataToFColor(Width, Height, In, SrcPitch, Out); return true; } else { // not supported yet check(0); return false; } } #if defined(DXGI_FORMAT_DEFINED) && DXGI_FORMAT_DEFINED // switching on platform format // could switch on EPixelFormat instead and make this more generic // have to be a little careful with that as the mapping is not one to one /** Convert D3D format type to general pixel format type*/ bool ConvertDXGIToFColor(DXGI_FORMAT Format, uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FColor* Out, FReadSurfaceDataFlags InFlags) { // todo : use a helper to map all the typeless to unorm before switching here ; see DXGIUtilities bool bLinearToGamma = InFlags.GetLinearToGamma(); switch (Format) { case DXGI_FORMAT_R16_TYPELESS: case DXGI_FORMAT_R16_UNORM: ConvertRawR16DataToFColor(Width, Height, In, SrcPitch, Out); return true; case DXGI_FORMAT_R8G8B8A8_TYPELESS: case DXGI_FORMAT_R8G8B8A8_UNORM: case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: ConvertRawR8G8B8A8DataToFColor(Width, Height, In, SrcPitch, Out); return true; case DXGI_FORMAT_B8G8R8A8_TYPELESS: case DXGI_FORMAT_B8G8R8A8_UNORM: case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: ConvertRawB8G8R8A8DataToFColor(Width, Height, In, SrcPitch, Out); return true; case DXGI_FORMAT_R10G10B10A2_UNORM: ConvertRawR10G10B10A2DataToFColor(Width, Height, In, SrcPitch, Out); return true; case DXGI_FORMAT_R16G16B16A16_FLOAT: ConvertRawR16G16B16A16FDataToFColor(Width, Height, In, SrcPitch, Out, bLinearToGamma); return true; case DXGI_FORMAT_R11G11B10_FLOAT: ConvertRawR11G11B10DataToFColor(Width, Height, In, SrcPitch, Out, bLinearToGamma); return true; case DXGI_FORMAT_R9G9B9E5_SHAREDEXP: ConvertRawR9G9B9E5DataToFColor(Width, Height, In, SrcPitch, Out, bLinearToGamma); return true; case DXGI_FORMAT_R32G32B32A32_FLOAT: ConvertRawR32G32B32A32DataToFColor(Width, Height, In, SrcPitch, Out, bLinearToGamma); return true; case DXGI_FORMAT_R24G8_TYPELESS: ConvertRawR24G8DataToFColor(Width, Height, In, SrcPitch, Out, InFlags); return true; case DXGI_FORMAT_R32G8X24_TYPELESS: ConvertRawDepthStencil64DataToFColor(Width, Height, In, SrcPitch, Out, InFlags); return true; case DXGI_FORMAT_R16G16B16A16_UNORM: ConvertRawR16G16B16A16DataToFColor(Width, Height, In, SrcPitch, Out, bLinearToGamma); return true; case DXGI_FORMAT_R16G16_UNORM: ConvertRawR16G16DataToFColor(Width, Height, In, SrcPitch, Out); return true; case DXGI_FORMAT_R8_UNORM: ConvertRawR8DataToFColor(Width, Height, In, SrcPitch, Out); return true; case DXGI_FORMAT_R8G8_UNORM: ConvertRawR8G8DataToFColor(Width, Height, In, SrcPitch, Out); return true; default: return false; } } #endif // DXGI_FORMAT_DEFINED }; // namespace