// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. #include "RenderCore.h" #include "RenderUtils.h" #include "RHI.h" #include "RenderResource.h" const uint16 GCubeIndices[12*3] = { 0, 2, 3, 0, 3, 1, 4, 5, 7, 4, 7, 6, 0, 1, 5, 0, 5, 4, 2, 6, 7, 2, 7, 3, 0, 4, 6, 0, 6, 2, 1, 3, 7, 1, 7, 5, }; /** X=127.5, Y=127.5, Z=1/127.5f, W=-1.0 */ const VectorRegister GVectorPackingConstants = MakeVectorRegister( 127.5f, 127.5f, 1.0f/127.5f, -1.0f ); /** Zero Normal **/ FPackedNormal FPackedNormal::ZeroNormal(127, 127, 127, 127); // // FPackedNormal serializer // FArchive& operator<<(FArchive& Ar,FPackedNormal& N) { Ar << N.Vector.Packed; return Ar; } // // Pixel format information. // FPixelFormatInfo GPixelFormats[PF_MAX] = { // Name BlockSizeX BlockSizeY BlockSizeZ BlockBytes NumComponents PlatformFormat Supported UnrealFormat { TEXT("unknown"), 0, 0, 0, 0, 0, 0, 0, PF_Unknown }, { TEXT("A32B32G32R32F"), 1, 1, 1, 16, 4, 0, 1, PF_A32B32G32R32F }, { TEXT("B8G8R8A8"), 1, 1, 1, 4, 4, 0, 1, PF_B8G8R8A8 }, { TEXT("G8"), 1, 1, 1, 1, 1, 0, 1, PF_G8 }, { TEXT("G16"), 1, 1, 1, 2, 1, 0, 1, PF_G16 }, { TEXT("DXT1"), 4, 4, 1, 8, 3, 0, 1, PF_DXT1 }, { TEXT("DXT3"), 4, 4, 1, 16, 4, 0, 1, PF_DXT3 }, { TEXT("DXT5"), 4, 4, 1, 16, 4, 0, 1, PF_DXT5 }, { TEXT("UYVY"), 2, 1, 1, 4, 4, 0, 0, PF_UYVY }, { TEXT("FloatRGB"), 1, 1, 1, 0, 3, 0, 0, PF_FloatRGB }, { TEXT("FloatRGBA"), 1, 1, 1, 8, 4, 0, 1, PF_FloatRGBA }, { TEXT("DepthStencil"), 1, 1, 1, 0, 1, 0, 0, PF_DepthStencil }, { TEXT("ShadowDepth"), 1, 1, 1, 4, 1, 0, 0, PF_ShadowDepth }, { TEXT("R32_FLOAT"), 1, 1, 1, 4, 1, 0, 1, PF_R32_FLOAT }, { TEXT("G16R16"), 1, 1, 1, 4, 2, 0, 1, PF_G16R16 }, { TEXT("G16R16F"), 1, 1, 1, 4, 2, 0, 1, PF_G16R16F }, { TEXT("G16R16F_FILTER"), 1, 1, 1, 4, 2, 0, 1, PF_G16R16F_FILTER }, { TEXT("G32R32F"), 1, 1, 1, 8, 2, 0, 1, PF_G32R32F }, { TEXT("A2B10G10R10"), 1, 1, 1, 4, 4, 0, 1, PF_A2B10G10R10 }, { TEXT("A16B16G16R16"), 1, 1, 1, 8, 4, 0, 1, PF_A16B16G16R16 }, { TEXT("D24"), 1, 1, 1, 4, 1, 0, 1, PF_D24 }, { TEXT("PF_R16F"), 1, 1, 1, 2, 1, 0, 1, PF_R16F }, { TEXT("PF_R16F_FILTER"), 1, 1, 1, 2, 1, 0, 1, PF_R16F_FILTER }, { TEXT("BC5"), 4, 4, 1, 16, 2, 0, 1, PF_BC5 }, { TEXT("V8U8"), 1, 1, 1, 2, 2, 0, 1, PF_V8U8 }, { TEXT("A1"), 1, 1, 1, 1, 1, 0, 0, PF_A1 }, { TEXT("FloatR11G11B10"), 1, 1, 1, 0, 3, 0, 0, PF_FloatR11G11B10 }, { TEXT("A8"), 1, 1, 1, 1, 1, 0, 1, PF_A8 }, { TEXT("R32_UINT"), 1, 1, 1, 4, 1, 0, 1, PF_R32_UINT }, { TEXT("R32_SINT"), 1, 1, 1, 4, 1, 0, 1, PF_R32_SINT }, // IOS Support { TEXT("PVRTC2"), 8, 4, 1, 8, 4, 0, 0, PF_PVRTC2 }, { TEXT("PVRTC4"), 4, 4, 1, 8, 4, 0, 0, PF_PVRTC4 }, { TEXT("R16_UINT"), 1, 1, 1, 2, 1, 0, 1, PF_R16_UINT }, { TEXT("R16_SINT"), 1, 1, 1, 2, 1, 0, 1, PF_R16_SINT }, { TEXT("R16G16B16A16_UINT"),1, 1, 1, 8, 4, 0, 1, PF_R16G16B16A16_UINT}, { TEXT("R16G16B16A16_SINT"),1, 1, 1, 8, 4, 0, 1, PF_R16G16B16A16_SINT}, { TEXT("R5G6B5_UNORM"), 1, 1, 1, 2, 3, 0, 1, PF_R5G6B5_UNORM }, { TEXT("R8G8B8A8"), 1, 1, 1, 4, 4, 0, 1, PF_R8G8B8A8 }, { TEXT("A8R8G8B8"), 1, 1, 1, 4, 4, 0, 1, PF_A8R8G8B8 }, { TEXT("BC4"), 4, 4, 1, 8, 1, 0, 1, PF_BC4 }, { TEXT("R8G8"), 1, 1, 1, 2, 2, 0, 1, PF_R8G8 }, { TEXT("ATC_RGB"), 4, 4, 1, 8, 3, 0, 0, PF_ATC_RGB }, { TEXT("ATC_RGBA_E"), 4, 4, 1, 16, 4, 0, 0, PF_ATC_RGBA_E }, { TEXT("ATC_RGBA_I"), 4, 4, 1, 16, 4, 0, 0, PF_ATC_RGBA_I }, { TEXT("X24_G8"), 1, 1, 1, 1, 1, 0, 0, PF_X24_G8 }, { TEXT("ETC1"), 4, 4, 1, 8, 3, 0, 0, PF_ETC1 }, { TEXT("ETC2_RGB"), 4, 4, 1, 8, 3, 0, 0, PF_ETC2_RGB }, { TEXT("ETC2_RGBA"), 4, 4, 1, 16, 4, 0, 0, PF_ETC2_RGBA }, { TEXT("PF_R32G32B32A32_UINT"),1, 1, 1, 16, 4, 0, 1, PF_R32G32B32A32_UINT}, { TEXT("PF_R16G16_UINT"), 1, 1, 1, 4, 4, 0, 1, PF_R16G16_UINT}, // ASTC support { TEXT("ASTC_4x4"), 4, 4, 1, 16, 4, 0, 0, PF_ASTC_4x4 }, { TEXT("ASTC_6x6"), 6, 6, 1, 16, 4, 0, 0, PF_ASTC_6x6 }, { TEXT("ASTC_8x8"), 8, 8, 1, 16, 4, 0, 0, PF_ASTC_8x8 }, { TEXT("ASTC_10x10"), 10, 10, 1, 16, 4, 0, 0, PF_ASTC_10x10 }, { TEXT("ASTC_12x12"), 12, 12, 1, 16, 4, 0, 0, PF_ASTC_12x12 }, { TEXT("BC6H"), 4, 4, 1, 16, 3, 0, 1, PF_BC6H }, { TEXT("BC7"), 4, 4, 1, 16, 4, 0, 1, PF_BC7 }, }; static struct FValidatePixelFormats { FValidatePixelFormats() { for (int32 X = 0; X < ARRAY_COUNT(GPixelFormats); ++X) { // Make sure GPixelFormats has an entry for every unreal format check(X == GPixelFormats[X].UnrealFormat); } } } ValidatePixelFormats; // // CalculateImageBytes // SIZE_T CalculateImageBytes(uint32 SizeX,uint32 SizeY,uint32 SizeZ,uint8 Format) { if ( Format == PF_A1 ) { // The number of bytes needed to store all 1 bit pixels in a line is the width of the image divided by the number of bits in a byte uint32 BytesPerLine = SizeX / 8; // The number of actual bytes in a 1 bit image is the bytes per line of pixels times the number of lines return sizeof(uint8) * BytesPerLine * SizeY; } else if( SizeZ > 0 ) { return (SizeX / GPixelFormats[Format].BlockSizeX) * (SizeY / GPixelFormats[Format].BlockSizeY) * (SizeZ / GPixelFormats[Format].BlockSizeZ) * GPixelFormats[Format].BlockBytes; } else { return (SizeX / GPixelFormats[Format].BlockSizeX) * (SizeY / GPixelFormats[Format].BlockSizeY) * GPixelFormats[Format].BlockBytes; } } // // FWhiteTexture implementation // /** * A solid-colored 1x1 texture. */ template class FColoredTexture : public FTexture { public: // FResource interface. virtual void InitRHI() override { // Create the texture RHI. FRHIResourceCreateInfo CreateInfo; FTexture2DRHIRef Texture2D = RHICreateTexture2D(1, 1, PF_B8G8R8A8, 1, 1, TexCreate_ShaderResource, CreateInfo); TextureRHI = Texture2D; // Write the contents of the texture. uint32 DestStride; FColor* DestBuffer = (FColor*)RHILockTexture2D(Texture2D, 0, RLM_WriteOnly, DestStride, false); *DestBuffer = FColor(R, G, B, A); RHIUnlockTexture2D(Texture2D, 0, false); // Create the sampler state RHI resource. FSamplerStateInitializerRHI SamplerStateInitializer(SF_Point,AM_Wrap,AM_Wrap,AM_Wrap); SamplerStateRHI = RHICreateSamplerState(SamplerStateInitializer); } /** Returns the width of the texture in pixels. */ virtual uint32 GetSizeX() const override { return 1; } /** Returns the height of the texture in pixels. */ virtual uint32 GetSizeY() const override { return 1; } }; FTexture* GWhiteTexture = new TGlobalResource >; FTexture* GBlackTexture = new TGlobalResource >; /** * Bulk data interface for providing a single black color used to initialize a * volume texture. */ class FBlackVolumeTextureResourceBulkDataInterface : public FResourceBulkDataInterface { public: /** Default constructor. */ FBlackVolumeTextureResourceBulkDataInterface() : Color(0) { } /** * Returns a pointer to the bulk data. */ virtual const void* GetResourceBulkData() const override { return &Color; } /** * @return size of resource memory */ virtual uint32 GetResourceBulkDataSize() const override { return sizeof(Color); } /** * Free memory after it has been used to initialize RHI resource */ virtual void Discard() override { } private: /** Storage for the color. */ FColor Color; }; /** * A class representing a 1x1x1 black volume texture. */ class FBlackVolumeTexture : public FTexture { public: /** * Initialize RHI resources. */ virtual void InitRHI() override { if (GetFeatureLevel() >= ERHIFeatureLevel::ES3_1) { // Create the texture. FBlackVolumeTextureResourceBulkDataInterface BlackTextureBulkData; FRHIResourceCreateInfo CreateInfo(&BlackTextureBulkData); FTexture3DRHIRef Texture3D = RHICreateTexture3D(1,1,1,PF_B8G8R8A8,1,TexCreate_ShaderResource,CreateInfo); TextureRHI = Texture3D; // Create the sampler state. FSamplerStateInitializerRHI SamplerStateInitializer(SF_Point,AM_Wrap,AM_Wrap,AM_Wrap); SamplerStateRHI = RHICreateSamplerState(SamplerStateInitializer); } } /** * Return the size of the texture in the X dimension. */ virtual uint32 GetSizeX() const override { return 1; } /** * Return the size of the texture in the Y dimension. */ virtual uint32 GetSizeY() const override { return 1; } }; /** Global black volume texture resource. */ FTexture* GBlackVolumeTexture = new TGlobalResource(); class FBlackArrayTexture : public FTexture { public: // FResource interface. virtual void InitRHI() override { if (GetFeatureLevel() >= ERHIFeatureLevel::SM4) { // Create the texture RHI. FRHIResourceCreateInfo CreateInfo; FTexture2DArrayRHIRef TextureArray = RHICreateTexture2DArray(1,1,1,PF_B8G8R8A8,1,TexCreate_ShaderResource,CreateInfo); TextureRHI = TextureArray; uint32 DestStride; FColor* DestBuffer = (FColor*)RHILockTexture2DArray(TextureArray, 0, 0, RLM_WriteOnly, DestStride, false); *DestBuffer = FColor(0, 0, 0, 0); RHIUnlockTexture2DArray(TextureArray, 0, 0, false); // Create the sampler state RHI resource. FSamplerStateInitializerRHI SamplerStateInitializer(SF_Point,AM_Wrap,AM_Wrap,AM_Wrap); SamplerStateRHI = RHICreateSamplerState(SamplerStateInitializer); } } /** Returns the width of the texture in pixels. */ virtual uint32 GetSizeX() const override { return 1; } /** Returns the height of the texture in pixels. */ virtual uint32 GetSizeY() const override { return 1; } }; FTexture* GBlackArrayTexture = new TGlobalResource; // // FMipColorTexture implementation // /** * A texture that has a different solid color in each mip-level */ class FMipColorTexture : public FTexture { public: enum { NumMips = 12 }; static const FColor MipColors[NumMips]; // FResource interface. virtual void InitRHI() override { // Create the texture RHI. int32 TextureSize = 1 << (NumMips - 1); FRHIResourceCreateInfo CreateInfo; FTexture2DRHIRef Texture2D = RHICreateTexture2D(TextureSize,TextureSize,PF_B8G8R8A8,NumMips,1,TexCreate_ShaderResource,CreateInfo); TextureRHI = Texture2D; // Write the contents of the texture. uint32 DestStride; int32 Size = TextureSize; for ( int32 MipIndex=0; MipIndex < NumMips; ++MipIndex ) { FColor* DestBuffer = (FColor*)RHILockTexture2D(Texture2D, MipIndex, RLM_WriteOnly, DestStride, false); for ( int32 Y=0; Y < Size; ++Y ) { for ( int32 X=0; X < Size; ++X ) { DestBuffer[X] = MipColors[NumMips - 1 - MipIndex]; } DestBuffer += DestStride / sizeof(FColor); } RHIUnlockTexture2D(Texture2D, MipIndex, false); Size >>= 1; } // Create the sampler state RHI resource. FSamplerStateInitializerRHI SamplerStateInitializer(SF_Point,AM_Wrap,AM_Wrap,AM_Wrap); SamplerStateRHI = RHICreateSamplerState(SamplerStateInitializer); } /** Returns the width of the texture in pixels. */ virtual uint32 GetSizeX() const override { int32 TextureSize = 1 << (NumMips - 1); return TextureSize; } /** Returns the height of the texture in pixels. */ // PVS-Studio notices that the implementation of GetSizeX is identical to this one // and warns us. In this case, it is intentional, so we disable the warning: virtual uint32 GetSizeY() const override //-V524 { int32 TextureSize = 1 << (NumMips - 1); return TextureSize; } }; const FColor FMipColorTexture::MipColors[NumMips] = { FColor( 80, 80, 80, 0 ), // Mip 0: 1x1 (dark grey) FColor( 200, 200, 200, 0 ), // Mip 1: 2x2 (light grey) FColor( 200, 200, 0, 0 ), // Mip 2: 4x4 (medium yellow) FColor( 255, 255, 0, 0 ), // Mip 3: 8x8 (yellow) FColor( 160, 255, 40, 0 ), // Mip 4: 16x16 (light green) FColor( 0, 255, 0, 0 ), // Mip 5: 32x32 (green) FColor( 0, 255, 200, 0 ), // Mip 6: 64x64 (cyan) FColor( 0, 170, 170, 0 ), // Mip 7: 128x128 (light blue) FColor( 60, 60, 255, 0 ), // Mip 8: 256x256 (dark blue) FColor( 255, 0, 255, 0 ), // Mip 9: 512x512 (pink) FColor( 255, 0, 0, 0 ), // Mip 10: 1024x1024 (red) FColor( 255, 130, 0, 0 ), // Mip 11: 2048x2048 (orange) }; RENDERCORE_API FTexture* GMipColorTexture = new FMipColorTexture; RENDERCORE_API int32 GMipColorTextureMipLevels = FMipColorTexture::NumMips; // 4: 8x8 cubemap resolution, shader needs to use the same value as preprocessing RENDERCORE_API const uint32 GDiffuseConvolveMipLevel = 4; // // FWhiteTextureCube implementation // /** A solid color cube texture. */ class FSolidColorTextureCube : public FTexture { public: FSolidColorTextureCube(const FColor& InColor) : Color(InColor) {} // FRenderResource interface. virtual void InitRHI() override { // Create the texture RHI. FRHIResourceCreateInfo CreateInfo; FTextureCubeRHIRef TextureCube = RHICreateTextureCube(1,PF_B8G8R8A8,1,0,CreateInfo); TextureRHI = TextureCube; // Write the contents of the texture. for(uint32 FaceIndex = 0;FaceIndex < 6;FaceIndex++) { uint32 DestStride; FColor* DestBuffer = (FColor*)RHILockTextureCubeFace(TextureCube, FaceIndex, 0, 0, RLM_WriteOnly, DestStride, false); *DestBuffer = Color; RHIUnlockTextureCubeFace(TextureCube, FaceIndex, 0, 0, false); } // Create the sampler state RHI resource. FSamplerStateInitializerRHI SamplerStateInitializer(SF_Point,AM_Wrap,AM_Wrap,AM_Wrap); SamplerStateRHI = RHICreateSamplerState(SamplerStateInitializer); } /** Returns the width of the texture in pixels. */ virtual uint32 GetSizeX() const override { return 1; } /** Returns the height of the texture in pixels. */ virtual uint32 GetSizeY() const override { return 1; } private: FColor Color; }; /** A white cube texture. */ class FWhiteTextureCube : public FSolidColorTextureCube { public: FWhiteTextureCube() : FSolidColorTextureCube(FColor::White) {} }; FTexture* GWhiteTextureCube = new TGlobalResource; /** A black cube texture. */ class FBlackTextureCube : public FSolidColorTextureCube { public: FBlackTextureCube(): FSolidColorTextureCube(FColor::Black) {} }; FTexture* GBlackTextureCube = new TGlobalResource; class FBlackCubeArrayTexture : public FTexture { public: // FResource interface. virtual void InitRHI() override { if (GetFeatureLevel() >= ERHIFeatureLevel::SM5) { // Create the texture RHI. FRHIResourceCreateInfo CreateInfo; FTextureCubeRHIRef TextureCubeArray = RHICreateTextureCubeArray(1,1,PF_B8G8R8A8,1,TexCreate_ShaderResource,CreateInfo); TextureRHI = TextureCubeArray; for(uint32 FaceIndex = 0;FaceIndex < 6;FaceIndex++) { uint32 DestStride; FColor* DestBuffer = (FColor*)RHILockTextureCubeFace(TextureCubeArray, FaceIndex, 0, 0, RLM_WriteOnly, DestStride, false); // Note: alpha is used by reflection environment to say how much of the foreground texture is visible, so 0 says it is completely invisible *DestBuffer = FColor(0, 0, 0, 0); RHIUnlockTextureCubeFace(TextureCubeArray, FaceIndex, 0, 0, false); } // Create the sampler state RHI resource. FSamplerStateInitializerRHI SamplerStateInitializer(SF_Point,AM_Wrap,AM_Wrap,AM_Wrap); SamplerStateRHI = RHICreateSamplerState(SamplerStateInitializer); } } /** Returns the width of the texture in pixels. */ virtual uint32 GetSizeX() const override { return 1; } /** Returns the height of the texture in pixels. */ virtual uint32 GetSizeY() const override { return 1; } }; FTexture* GBlackCubeArrayTexture = new TGlobalResource; /* 3 XYZ packed in 4 bytes. (11:11:10 for X:Y:Z) */ /** * operator FVector - unpacked to -1 to 1 */ FPackedPosition::operator FVector() const { return FVector(Vector.X/1023.f, Vector.Y/1023.f, Vector.Z/511.f); } /** * operator VectorRegister */ VectorRegister FPackedPosition::GetVectorRegister() const { FVector UnpackedVect = *this; VectorRegister VectorToUnpack = VectorLoadFloat3_W0(&UnpackedVect); return VectorToUnpack; } /** * Pack this vector(-1 to 1 for XYZ) to 4 bytes XYZ(11:11:10) */ void FPackedPosition::Set( const FVector& InVector ) { check (FMath::Abs(InVector.X) <= 1.f && FMath::Abs(InVector.Y) <= 1.f && FMath::Abs(InVector.Z) <= 1.f); #if !WITH_EDITORONLY_DATA // This should not happen in Console - this should happen during Cooking in PC check (false); #else // Too confusing to use .5f - wanted to use the last bit! // Change to int for easier read Vector.X = FMath::Clamp(FMath::TruncToInt(InVector.X * 1023.0f),-1023,1023); Vector.Y = FMath::Clamp(FMath::TruncToInt(InVector.Y * 1023.0f),-1023,1023); Vector.Z = FMath::Clamp(FMath::TruncToInt(InVector.Z * 511.0f),-511,511); #endif } /** * operator << serialize */ FArchive& operator<<(FArchive& Ar,FPackedPosition& N) { // Save N.Packed return Ar << N.Packed; } void CalcMipMapExtent3D( uint32 TextureSizeX, uint32 TextureSizeY, uint32 TextureSizeZ, EPixelFormat Format, uint32 MipIndex, uint32& OutXExtent, uint32& OutYExtent, uint32& OutZExtent ) { OutXExtent = FMath::Max(TextureSizeX >> MipIndex, GPixelFormats[Format].BlockSizeX); OutYExtent = FMath::Max(TextureSizeY >> MipIndex, GPixelFormats[Format].BlockSizeY); OutZExtent = FMath::Max(TextureSizeZ >> MipIndex, GPixelFormats[Format].BlockSizeZ); } SIZE_T CalcTextureMipMapSize3D( uint32 TextureSizeX, uint32 TextureSizeY, uint32 TextureSizeZ, EPixelFormat Format, uint32 MipIndex ) { uint32 XExtent; uint32 YExtent; uint32 ZExtent; CalcMipMapExtent3D(TextureSizeX, TextureSizeY, TextureSizeZ, Format, MipIndex, XExtent, YExtent, ZExtent); const uint32 XPitch = (XExtent / GPixelFormats[Format].BlockSizeX) * GPixelFormats[Format].BlockBytes; const uint32 NumRows = YExtent / GPixelFormats[Format].BlockSizeY; const uint32 NumLayers = ZExtent / GPixelFormats[Format].BlockSizeZ; return NumLayers * NumRows * XPitch; } SIZE_T CalcTextureSize3D( uint32 SizeX, uint32 SizeY, uint32 SizeZ, EPixelFormat Format, uint32 MipCount ) { SIZE_T Size = 0; for ( uint32 MipIndex=0; MipIndex < MipCount; ++MipIndex ) { Size += CalcTextureMipMapSize3D(SizeX,SizeY,SizeZ,Format,MipIndex); } return Size; } FIntPoint CalcMipMapExtent( uint32 TextureSizeX, uint32 TextureSizeY, EPixelFormat Format, uint32 MipIndex ) { return FIntPoint(FMath::Max(TextureSizeX >> MipIndex, GPixelFormats[Format].BlockSizeX), FMath::Max(TextureSizeY >> MipIndex, GPixelFormats[Format].BlockSizeY)); } SIZE_T CalcTextureMipMapSize( uint32 TextureSizeX, uint32 TextureSizeY, EPixelFormat Format, uint32 MipIndex ) { FIntPoint MipExtent = CalcMipMapExtent(TextureSizeX, TextureSizeY, Format, MipIndex); const uint32 Pitch = (MipExtent.X / GPixelFormats[Format].BlockSizeX) * GPixelFormats[Format].BlockBytes; const uint32 NumRows = MipExtent.Y / GPixelFormats[Format].BlockSizeY; return NumRows * Pitch; } SIZE_T CalcTextureSize( uint32 SizeX, uint32 SizeY, EPixelFormat Format, uint32 MipCount ) { SIZE_T Size = 0; for ( uint32 MipIndex=0; MipIndex < MipCount; ++MipIndex ) { Size += CalcTextureMipMapSize(SizeX,SizeY,Format,MipIndex); } return Size; } void CopyTextureData2D(const void* Source,void* Dest,uint32 SizeY,EPixelFormat Format,uint32 SourceStride,uint32 DestStride) { const uint32 BlockSizeY = GPixelFormats[Format].BlockSizeY; const uint32 NumBlocksY = (SizeY + BlockSizeY - 1) / BlockSizeY; // a DestStride of 0 means to use the SourceStride if(SourceStride == DestStride || DestStride == 0) { // If the source and destination have the same stride, copy the data in one block. FMemory::Memcpy(Dest,Source,NumBlocksY * SourceStride); } else { // If the source and destination have different strides, copy each row of blocks separately. const uint32 NumBytesPerRow = FMath::Min(SourceStride, DestStride); for(uint32 BlockY = 0;BlockY < NumBlocksY;++BlockY) { FMemory::Memcpy( (uint8*)Dest + DestStride * BlockY, (uint8*)Source + SourceStride * BlockY, NumBytesPerRow ); } } } /** Helper functions for text output of texture properties... */ #ifndef CASE_ENUM_TO_TEXT #define CASE_ENUM_TO_TEXT(txt) case txt: return TEXT(#txt); #endif #ifndef TEXT_TO_ENUM #define TEXT_TO_ENUM(eVal, txt) if (FCString::Stricmp(TEXT(#eVal), txt) == 0) return eVal; #endif const TCHAR* GetPixelFormatString(EPixelFormat InPixelFormat) { switch (InPixelFormat) { FOREACH_ENUM_EPIXELFORMAT(CASE_ENUM_TO_TEXT) default: return TEXT("PF_Unknown"); } } EPixelFormat GetPixelFormatFromString(const TCHAR* InPixelFormatStr) { #define TEXT_TO_PIXELFORMAT(f) TEXT_TO_ENUM(f, InPixelFormatStr); FOREACH_ENUM_EPIXELFORMAT(TEXT_TO_PIXELFORMAT) #undef TEXT_TO_PIXELFORMAT return PF_Unknown; } const TCHAR* GetCubeFaceName(ECubeFace Face) { switch(Face) { case CubeFace_PosX: return TEXT("PosX"); case CubeFace_NegX: return TEXT("NegX"); case CubeFace_PosY: return TEXT("PosY"); case CubeFace_NegY: return TEXT("NegY"); case CubeFace_PosZ: return TEXT("PosZ"); case CubeFace_NegZ: return TEXT("NegZ"); default: return TEXT(""); } } ECubeFace GetCubeFaceFromName(const FString& Name) { // not fast but doesn't have to be if(Name.EndsWith(TEXT("PosX"))) { return CubeFace_PosX; } else if(Name.EndsWith(TEXT("NegX"))) { return CubeFace_NegX; } else if(Name.EndsWith(TEXT("PosY"))) { return CubeFace_PosY; } else if(Name.EndsWith(TEXT("NegY"))) { return CubeFace_NegY; } else if(Name.EndsWith(TEXT("PosZ"))) { return CubeFace_PosZ; } else if(Name.EndsWith(TEXT("NegZ"))) { return CubeFace_NegZ; } return CubeFace_MAX; } class FVector4VertexDeclaration : public FRenderResource { public: FVertexDeclarationRHIRef VertexDeclarationRHI; virtual void InitRHI() override { FVertexDeclarationElementList Elements; Elements.Add(FVertexElement(0, 0, VET_Float4, 0, sizeof(FVector4))); VertexDeclarationRHI = RHICreateVertexDeclaration(Elements); } virtual void ReleaseRHI() override { VertexDeclarationRHI.SafeRelease(); } }; TGlobalResource GVector4VertexDeclaration; RENDERCORE_API FVertexDeclarationRHIRef& GetVertexDeclarationFVector4() { return GVector4VertexDeclaration.VertexDeclarationRHI; } class FVector3VertexDeclaration : public FRenderResource { public: FVertexDeclarationRHIRef VertexDeclarationRHI; virtual void InitRHI() override { FVertexDeclarationElementList Elements; Elements.Add(FVertexElement(0, 0, VET_Float3, 0, sizeof(FVector))); VertexDeclarationRHI = RHICreateVertexDeclaration(Elements); } virtual void ReleaseRHI() override { VertexDeclarationRHI.SafeRelease(); } }; TGlobalResource GVector3VertexDeclaration; RENDERCORE_API FVertexDeclarationRHIRef& GetVertexDeclarationFVector3() { return GVector3VertexDeclaration.VertexDeclarationRHI; } RENDERCORE_API bool IsSimpleDynamicLightingEnabled() { static const auto CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.SimpleDynamicLighting")); return (CVar->GetValueOnAnyThread() != 0); }