1001 lines
34 KiB
C++
1001 lines
34 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "GlobalRenderResources.h"
|
|
#include "RenderGraphUtils.h"
|
|
#include "Containers/ResourceArray.h"
|
|
#include "RenderCore.h"
|
|
#include "RenderUtils.h"
|
|
#include "RHIResourceUtils.h"
|
|
#include "Algo/Reverse.h"
|
|
#include "Async/Mutex.h"
|
|
|
|
// The maximum number of transient vertex buffer bytes to allocate before we start panic logging who is doing the allocations
|
|
int32 GMaxVertexBytesAllocatedPerFrame = 32 * 1024 * 1024;
|
|
|
|
FAutoConsoleVariableRef CVarMaxVertexBytesAllocatedPerFrame(
|
|
TEXT("r.MaxVertexBytesAllocatedPerFrame"),
|
|
GMaxVertexBytesAllocatedPerFrame,
|
|
TEXT("The maximum number of transient vertex buffer bytes to allocate before we start panic logging who is doing the allocations"));
|
|
|
|
int32 GGlobalBufferNumFramesUnusedThreshold = 30;
|
|
FAutoConsoleVariableRef CVarGlobalBufferNumFramesUnusedThreshold(
|
|
TEXT("r.NumFramesUnusedBeforeReleasingGlobalResourceBuffers"),
|
|
GGlobalBufferNumFramesUnusedThreshold,
|
|
TEXT("Number of frames after which unused global resource allocations will be discarded. Set 0 to ignore. (default=30)"));
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
// Bulk data interface for providing a single color used to initialize a volume texture.
|
|
struct FColorBulkData : public FResourceBulkDataInterface
|
|
{
|
|
FColorBulkData(uint8 Alpha) : Color(0, 0, 0, Alpha) { }
|
|
FColorBulkData(int32 R, int32 G, int32 B, int32 A) : Color(R, G, B, A) {}
|
|
FColorBulkData(FColor InColor) : Color(InColor) { }
|
|
|
|
virtual const void* GetResourceBulkData() const override { return &Color; }
|
|
virtual uint32 GetResourceBulkDataSize() const override { return sizeof(Color); }
|
|
virtual void Discard() override { }
|
|
|
|
FColor Color;
|
|
};
|
|
|
|
/**
|
|
* A solid-colored 1x1 texture.
|
|
*/
|
|
template <int32 R, int32 G, int32 B, int32 A>
|
|
class FColoredTexture : public FTextureWithSRV
|
|
{
|
|
public:
|
|
// FResource interface.
|
|
virtual void InitRHI(FRHICommandListBase& RHICmdList) override
|
|
{
|
|
// BGRA typed UAV is unsupported per D3D spec, use RGBA here.
|
|
const FRHITextureCreateDesc Desc =
|
|
FRHITextureCreateDesc::Create2D(TEXT("ColoredTexture"), 1, 1, PF_R8G8B8A8)
|
|
.SetFlags(ETextureCreateFlags::ShaderResource)
|
|
.SetClassName(TEXT("FColoredTexture"))
|
|
.SetInitActionInitializer();
|
|
|
|
FRHITextureInitializer Initializer = RHICmdList.CreateTextureInitializer(Desc);
|
|
|
|
const FColor ColorValue(R, G, B, A);
|
|
Initializer.GetTexture2DSubresource(0).WriteColor(ColorValue);
|
|
|
|
// Create the texture RHI.
|
|
TextureRHI = Initializer.Finalize();
|
|
|
|
// Create the sampler state RHI resource.
|
|
FSamplerStateInitializerRHI SamplerStateInitializer(SF_Point, AM_Wrap, AM_Wrap, AM_Wrap);
|
|
SamplerStateRHI = GetOrCreateSamplerState(SamplerStateInitializer);
|
|
|
|
// Create a view of the texture
|
|
ShaderResourceViewRHI = RHICmdList.CreateShaderResourceView(TextureRHI, FRHIViewDesc::CreateTextureSRV().SetDimensionFromTexture(TextureRHI));
|
|
}
|
|
|
|
virtual uint32 GetSizeX() const override { return 1; }
|
|
virtual uint32 GetSizeY() const override { return 1; }
|
|
};
|
|
|
|
class FEmptyVertexBuffer : public FVertexBufferWithSRV
|
|
{
|
|
public:
|
|
virtual void InitRHI(FRHICommandListBase& RHICmdList) override
|
|
{
|
|
// Create the texture RHI.
|
|
const FRHIBufferCreateDesc CreateDesc =
|
|
FRHIBufferCreateDesc::CreateVertex(TEXT("EmptyVertexBuffer"), 16)
|
|
.AddUsage(BUF_Static | BUF_ShaderResource | BUF_UnorderedAccess)
|
|
.DetermineInitialState();
|
|
|
|
VertexBufferRHI = RHICmdList.CreateBuffer(CreateDesc);
|
|
|
|
// Create a view of the buffer
|
|
ShaderResourceViewRHI = RHICmdList.CreateShaderResourceView(
|
|
VertexBufferRHI,
|
|
FRHIViewDesc::CreateBufferSRV()
|
|
.SetType(FRHIViewDesc::EBufferType::Typed)
|
|
.SetFormat(PF_R32_UINT));
|
|
UnorderedAccessViewRHI = RHICmdList.CreateUnorderedAccessView(
|
|
VertexBufferRHI,
|
|
FRHIViewDesc::CreateBufferUAV()
|
|
.SetType(FRHIViewDesc::EBufferType::Typed)
|
|
.SetFormat(PF_R32_UINT));
|
|
}
|
|
};
|
|
|
|
class FEmptyStructuredBuffer : public FVertexBufferWithSRV
|
|
{
|
|
public:
|
|
virtual void InitRHI(FRHICommandListBase& RHICmdList) override
|
|
{
|
|
const FRHIBufferCreateDesc CreateDesc =
|
|
FRHIBufferCreateDesc::CreateStructured(TEXT("EmptyStructuredBuffer"), sizeof(uint32) * 4, sizeof(uint32))
|
|
.AddUsage(BUF_Static | BUF_ShaderResource | BUF_UnorderedAccess)
|
|
.DetermineInitialState();
|
|
|
|
// Create the buffer RHI.
|
|
VertexBufferRHI = RHICmdList.CreateBuffer(CreateDesc);
|
|
|
|
// Create a view of the buffer
|
|
ShaderResourceViewRHI = RHICmdList.CreateShaderResourceView(VertexBufferRHI, FRHIViewDesc::CreateBufferSRV().SetTypeFromBuffer(VertexBufferRHI));
|
|
UnorderedAccessViewRHI = RHICmdList.CreateUnorderedAccessView(VertexBufferRHI, FRHIViewDesc::CreateBufferUAV().SetTypeFromBuffer(VertexBufferRHI));
|
|
}
|
|
};
|
|
|
|
class FBlackFloat4StructuredBufferWithSRV : public FVertexBufferWithSRV
|
|
{
|
|
public:
|
|
virtual void InitRHI(FRHICommandListBase& RHICmdList) override
|
|
{
|
|
const FRHIBufferCreateDesc CreateDesc =
|
|
FRHIBufferCreateDesc::CreateStructured(TEXT("BlackFloat4StructuredBuffer"), sizeof(FVector4f), sizeof(FVector4f))
|
|
.AddUsage(BUF_Static | BUF_ShaderResource | BUF_UnorderedAccess)
|
|
.SetInitialState(ERHIAccess::SRVMask);
|
|
|
|
const FVector4f InitialData(0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
// Create the buffer RHI.
|
|
VertexBufferRHI = UE::RHIResourceUtils::CreateBufferWithValue(RHICmdList, CreateDesc, InitialData);
|
|
|
|
// Create a view of the buffer
|
|
ShaderResourceViewRHI = RHICmdList.CreateShaderResourceView(VertexBufferRHI, FRHIViewDesc::CreateBufferSRV().SetTypeFromBuffer(VertexBufferRHI));
|
|
UnorderedAccessViewRHI = RHICmdList.CreateUnorderedAccessView(VertexBufferRHI, FRHIViewDesc::CreateBufferUAV().SetTypeFromBuffer(VertexBufferRHI));
|
|
}
|
|
};
|
|
|
|
class FBlackFloat4VertexBuffer : public FVertexBufferWithSRV
|
|
{
|
|
public:
|
|
virtual void InitRHI(FRHICommandListBase& RHICmdList) override
|
|
{
|
|
const FRHIBufferCreateDesc CreateDesc =
|
|
FRHIBufferCreateDesc::CreateVertex(TEXT("BlackFloat4VertexBuffer"), sizeof(FVector4f))
|
|
.AddUsage(BUF_Static | BUF_ShaderResource | BUF_UnorderedAccess)
|
|
.SetInitialState(ERHIAccess::SRVMask);
|
|
|
|
const FVector4f InitialData(0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
// Create the texture RHI.
|
|
VertexBufferRHI = UE::RHIResourceUtils::CreateBufferWithValue(RHICmdList, CreateDesc, InitialData);
|
|
|
|
// Create a view of the buffer
|
|
ShaderResourceViewRHI = RHICmdList.CreateShaderResourceView(
|
|
VertexBufferRHI,
|
|
FRHIViewDesc::CreateBufferSRV()
|
|
.SetType(FRHIViewDesc::EBufferType::Typed)
|
|
.SetFormat(PF_A32B32G32R32F));
|
|
UnorderedAccessViewRHI = RHICmdList.CreateUnorderedAccessView(
|
|
VertexBufferRHI, FRHIViewDesc::CreateBufferUAV()
|
|
.SetType(FRHIViewDesc::EBufferType::Typed)
|
|
.SetFormat(PF_A32B32G32R32F));
|
|
}
|
|
};
|
|
|
|
class FBlackTextureWithSRV : public FColoredTexture<0, 0, 0, 255>
|
|
{
|
|
public:
|
|
virtual void InitRHI(FRHICommandListBase& RHICmdList) override
|
|
{
|
|
FColoredTexture::InitRHI(RHICmdList);
|
|
FRHITextureReference::DefaultTexture = TextureRHI;
|
|
}
|
|
|
|
virtual void ReleaseRHI() override
|
|
{
|
|
FRHITextureReference::DefaultTexture.SafeRelease();
|
|
FColoredTexture::ReleaseRHI();
|
|
}
|
|
};
|
|
|
|
FTextureWithSRV* GWhiteTextureWithSRV = new TGlobalResource<FColoredTexture<255, 255, 255, 255>, FRenderResource::EInitPhase::Pre>;
|
|
FTextureWithSRV* GBlackTextureWithSRV = new TGlobalResource<FBlackTextureWithSRV, FRenderResource::EInitPhase::Pre>();
|
|
FTextureWithSRV* GTransparentBlackTextureWithSRV = new TGlobalResource<FColoredTexture<0, 0, 0, 0>, FRenderResource::EInitPhase::Pre>;
|
|
FTexture* GWhiteTexture = GWhiteTextureWithSRV;
|
|
FTexture* GBlackTexture = GBlackTextureWithSRV;
|
|
FTexture* GTransparentBlackTexture = GTransparentBlackTextureWithSRV;
|
|
|
|
FVertexBufferWithSRV* GEmptyVertexBufferWithUAV = new TGlobalResource<FEmptyVertexBuffer, FRenderResource::EInitPhase::Pre>;
|
|
FVertexBufferWithSRV* GEmptyStructuredBufferWithUAV = new TGlobalResource<FEmptyStructuredBuffer, FRenderResource::EInitPhase::Pre>;
|
|
FVertexBufferWithSRV* GBlackFloat4StructuredBufferWithSRV = new TGlobalResource<FBlackFloat4StructuredBufferWithSRV, FRenderResource::EInitPhase::Pre>;
|
|
FVertexBufferWithSRV* GBlackFloat4VertexBufferWithSRV = new TGlobalResource<FBlackFloat4VertexBuffer, FRenderResource::EInitPhase::Pre>;
|
|
|
|
class FWhiteVertexBuffer : public FVertexBufferWithSRV
|
|
{
|
|
public:
|
|
virtual void InitRHI(FRHICommandListBase& RHICmdList) override
|
|
{
|
|
const FRHIBufferCreateDesc CreateDesc =
|
|
FRHIBufferCreateDesc::CreateVertex(TEXT("WhiteVertexBuffer"), sizeof(FVector4f))
|
|
.AddUsage(BUF_Static | BUF_ShaderResource)
|
|
.DetermineInitialState();
|
|
|
|
const FVector4f InitialData(1.0f, 1.0f, 1.0f, 1.0f);
|
|
|
|
// Create the buffer RHI.
|
|
VertexBufferRHI = UE::RHIResourceUtils::CreateBufferWithValue(RHICmdList, CreateDesc, InitialData);
|
|
|
|
// Create a view of the buffer
|
|
ShaderResourceViewRHI = RHICmdList.CreateShaderResourceView(
|
|
VertexBufferRHI,
|
|
FRHIViewDesc::CreateBufferSRV()
|
|
.SetType(FRHIViewDesc::EBufferType::Typed)
|
|
.SetFormat(PF_A32B32G32R32F));
|
|
}
|
|
};
|
|
|
|
FVertexBufferWithSRV* GWhiteVertexBufferWithSRV = new TGlobalResource<FWhiteVertexBuffer, FRenderResource::EInitPhase::Pre>;
|
|
|
|
class FBlackVertexBuffer : public FVertexBufferWithSRV
|
|
{
|
|
public:
|
|
virtual void InitRHI(FRHICommandListBase& RHICmdList) override
|
|
{
|
|
const FRHIBufferCreateDesc CreateDesc =
|
|
FRHIBufferCreateDesc::CreateVertex(TEXT("BlackVertexBuffer"), sizeof(FVector4f))
|
|
.AddUsage(BUF_Static | BUF_ShaderResource)
|
|
.DetermineInitialState();
|
|
|
|
const FVector4f InitialData(0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
// Create the buffer RHI.
|
|
VertexBufferRHI = UE::RHIResourceUtils::CreateBufferWithValue(RHICmdList, CreateDesc, InitialData);
|
|
|
|
// Create a view of the buffer
|
|
ShaderResourceViewRHI = RHICmdList.CreateShaderResourceView(
|
|
VertexBufferRHI,
|
|
FRHIViewDesc::CreateBufferSRV()
|
|
.SetType(FRHIViewDesc::EBufferType::Typed)
|
|
.SetFormat(PF_A32B32G32R32F));
|
|
}
|
|
};
|
|
|
|
FVertexBufferWithSRV* GBlackVertexBufferWithSRV = new TGlobalResource<FBlackVertexBuffer, FRenderResource::EInitPhase::Pre>;
|
|
|
|
class FWhiteVertexBufferWithRDG : public FBufferWithRDG
|
|
{
|
|
public:
|
|
virtual void InitRHI(FRHICommandListBase& RHICmdList) override
|
|
{
|
|
if (!Buffer.IsValid())
|
|
{
|
|
Buffer = AllocatePooledBuffer(FRDGBufferDesc::CreateBufferDesc(sizeof(FVector4f), 1), TEXT("WhiteVertexBufferWithRDG"));
|
|
|
|
FVector4f* BufferData = (FVector4f*)RHICmdList.LockBuffer(Buffer->GetRHI(), 0, sizeof(FVector4f), RLM_WriteOnly);
|
|
*BufferData = FVector4f(1.0f, 1.0f, 1.0f, 1.0f);
|
|
RHICmdList.UnlockBuffer(Buffer->GetRHI());
|
|
}
|
|
}
|
|
};
|
|
|
|
FBufferWithRDG* GWhiteVertexBufferWithRDG = new TGlobalResource<FWhiteVertexBufferWithRDG, FRenderResource::EInitPhase::Pre>();
|
|
|
|
/**
|
|
* A class representing a 1x1x1 black volume texture.
|
|
*/
|
|
template <EPixelFormat PixelFormat, uint8 Alpha>
|
|
class FBlackVolumeTexture : public FTexture
|
|
{
|
|
public:
|
|
virtual void InitRHI(FRHICommandListBase& RHICmdList) override
|
|
{
|
|
FColorBulkData BulkData(Alpha);
|
|
|
|
FRHITextureCreateDesc Desc =
|
|
GSupportsTexture3D
|
|
? FRHITextureCreateDesc::Create3D(TEXT("BlackVolumeTexture3D"), 1, 1, 1, PixelFormat)
|
|
: FRHITextureCreateDesc::Create2D(TEXT("BlackVolumeTexture2D"), 1, 1, PixelFormat);
|
|
|
|
Desc.SetFlags(ETextureCreateFlags::ShaderResource)
|
|
.SetClassName(TEXT("FBlackVolumeTexture"))
|
|
.SetInitActionBulkData(&BulkData);
|
|
|
|
TextureRHI = RHICmdList.CreateTexture(Desc);
|
|
|
|
// Create the sampler state.
|
|
FSamplerStateInitializerRHI SamplerStateInitializer(SF_Point, AM_Wrap, AM_Wrap, AM_Wrap);
|
|
SamplerStateRHI = GetOrCreateSamplerState(SamplerStateInitializer);
|
|
}
|
|
|
|
virtual uint32 GetSizeX() const override { return 1; }
|
|
virtual uint32 GetSizeY() const override { return 1; }
|
|
};
|
|
|
|
/** Global black volume texture resource. */
|
|
FTexture* GBlackVolumeTexture = new TGlobalResource<FBlackVolumeTexture<PF_B8G8R8A8, 0>, FRenderResource::EInitPhase::Pre>();
|
|
FTexture* GBlackAlpha1VolumeTexture = new TGlobalResource<FBlackVolumeTexture<PF_B8G8R8A8, 255>, FRenderResource::EInitPhase::Pre>();
|
|
|
|
/** Global black volume texture resource. */
|
|
FTexture* GBlackUintVolumeTexture = new TGlobalResource<FBlackVolumeTexture<PF_R8G8B8A8_UINT, 0>, FRenderResource::EInitPhase::Pre>();
|
|
|
|
class FBlackArrayTexture : public FTexture
|
|
{
|
|
public:
|
|
virtual void InitRHI(FRHICommandListBase& RHICmdList) override
|
|
{
|
|
const FRHITextureCreateDesc Desc =
|
|
FRHITextureCreateDesc::Create2DArray(TEXT("BlackArrayTexture"), 1, 1, 1, PF_B8G8R8A8)
|
|
.SetFlags(ETextureCreateFlags::ShaderResource)
|
|
.SetClassName(TEXT("FBlackArrayTexture"))
|
|
.SetInitActionInitializer();
|
|
|
|
FRHITextureInitializer Initializer = RHICmdList.CreateTextureInitializer(Desc);
|
|
|
|
const FColor ColorValue(0, 0, 0, 0);
|
|
Initializer.GetTexture2DSubresource(0).WriteColor(ColorValue);
|
|
|
|
// Create the texture RHI.
|
|
TextureRHI = Initializer.Finalize();
|
|
|
|
// Create the sampler state RHI resource.
|
|
FSamplerStateInitializerRHI SamplerStateInitializer(SF_Point, AM_Wrap, AM_Wrap, AM_Wrap);
|
|
SamplerStateRHI = GetOrCreateSamplerState(SamplerStateInitializer);
|
|
}
|
|
|
|
virtual uint32 GetSizeX() const override { return 1; }
|
|
virtual uint32 GetSizeY() const override { return 1; }
|
|
};
|
|
|
|
FTexture* GBlackArrayTexture = new TGlobalResource<FBlackArrayTexture, FRenderResource::EInitPhase::Pre>;
|
|
|
|
//
|
|
// 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(FRHICommandListBase& RHICmdList) override
|
|
{
|
|
// Create the texture RHI.
|
|
int32 TextureSize = 1 << (NumMips - 1);
|
|
|
|
const FRHITextureCreateDesc Desc =
|
|
FRHITextureCreateDesc::Create2D(TEXT("FMipColorTexture"), TextureSize, TextureSize, PF_B8G8R8A8)
|
|
.SetNumMips(NumMips)
|
|
.SetFlags(ETextureCreateFlags::ShaderResource)
|
|
.SetClassName(TEXT("FMipColorTexture"))
|
|
.SetInitActionInitializer();
|
|
|
|
FRHITextureInitializer Initializer = RHICmdList.CreateTextureInitializer(Desc);
|
|
|
|
// Write the contents of the texture.
|
|
int32 Size = TextureSize;
|
|
for (int32 MipIndex = 0; MipIndex < NumMips; ++MipIndex)
|
|
{
|
|
const FRHITextureSubresourceInitializer Subresource = Initializer.GetTexture2DSubresource(MipIndex);
|
|
|
|
FColor* DestBuffer = reinterpret_cast<FColor*>(Subresource.Data);
|
|
uint64 DestBufferOffset = 0;
|
|
|
|
for (int32 Y = 0; Y < Size; ++Y)
|
|
{
|
|
for (int32 X = 0; X < Size; ++X)
|
|
{
|
|
DestBuffer[X + DestBufferOffset] = MipColors[NumMips - 1 - MipIndex];
|
|
}
|
|
DestBufferOffset += Subresource.Stride / sizeof(FColor);
|
|
check(DestBufferOffset <= Subresource.Size);
|
|
}
|
|
|
|
Size >>= 1;
|
|
}
|
|
|
|
TextureRHI = Initializer.Finalize();
|
|
|
|
// Create the sampler state RHI resource.
|
|
FSamplerStateInitializerRHI SamplerStateInitializer(SF_Point, AM_Wrap, AM_Wrap, AM_Wrap);
|
|
SamplerStateRHI = GetOrCreateSamplerState(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)
|
|
};
|
|
|
|
FTexture* GMipColorTexture = new FMipColorTexture;
|
|
int32 GMipColorTextureMipLevels = FMipColorTexture::NumMips;
|
|
|
|
// 4: 8x8 cubemap resolution, shader needs to use the same value as preprocessing
|
|
const uint32 GDiffuseConvolveMipLevel = 4;
|
|
|
|
/** A solid color cube texture. */
|
|
class FSolidColorTextureCube : public FTexture
|
|
{
|
|
public:
|
|
FSolidColorTextureCube(const FColor& InColor)
|
|
: bInitToZero(false)
|
|
, PixelFormat(PF_B8G8R8A8)
|
|
, ColorData(InColor.DWColor())
|
|
{}
|
|
|
|
FSolidColorTextureCube(EPixelFormat InPixelFormat)
|
|
: bInitToZero(true)
|
|
, PixelFormat(InPixelFormat)
|
|
, ColorData(0)
|
|
{}
|
|
|
|
// FRenderResource interface.
|
|
virtual void InitRHI(FRHICommandListBase& RHICmdList) override
|
|
{
|
|
// Create the texture RHI.
|
|
const FRHITextureCreateDesc CreateDesc =
|
|
FRHITextureCreateDesc::CreateCube(TEXT("SolidColorCube"), 1, PixelFormat)
|
|
.SetFlags(ETextureCreateFlags::ShaderResource)
|
|
.SetClassName(TEXT("FSolidColorTextureCube"))
|
|
.SetInitialState(ERHIAccess::SRVMask)
|
|
.SetInitActionInitializer();
|
|
|
|
FRHITextureInitializer Initializer = RHICmdList.CreateTextureInitializer(CreateDesc);
|
|
|
|
for (uint32 FaceIndex = 0; FaceIndex < 6; FaceIndex++)
|
|
{
|
|
FRHITextureSubresourceInitializer Subresource = Initializer.GetTextureCubeSubresource(FaceIndex, 0);
|
|
if (bInitToZero)
|
|
{
|
|
FMemory::Memzero(Subresource.Data, GPixelFormats[PixelFormat].BlockBytes);
|
|
}
|
|
else
|
|
{
|
|
FMemory::Memcpy(Subresource.Data, &ColorData, sizeof(ColorData));
|
|
}
|
|
}
|
|
|
|
TextureRHI = Initializer.Finalize();
|
|
|
|
// Create the sampler state RHI resource.
|
|
FSamplerStateInitializerRHI SamplerStateInitializer(SF_Point, AM_Wrap, AM_Wrap, AM_Wrap);
|
|
SamplerStateRHI = GetOrCreateSamplerState(SamplerStateInitializer);
|
|
}
|
|
|
|
virtual uint32 GetSizeX() const override { return 1; }
|
|
virtual uint32 GetSizeY() const override { return 1; }
|
|
|
|
private:
|
|
const bool bInitToZero;
|
|
const EPixelFormat PixelFormat;
|
|
const uint32 ColorData;
|
|
};
|
|
|
|
/** A white cube texture. */
|
|
FTexture* GWhiteTextureCube = new TGlobalResource<FSolidColorTextureCube, FRenderResource::EInitPhase::Pre>(FColor::White);
|
|
|
|
/** A black cube texture. */
|
|
FTexture* GBlackTextureCube = new TGlobalResource<FSolidColorTextureCube, FRenderResource::EInitPhase::Pre>(FColor::Black);
|
|
|
|
/** A black cube texture. */
|
|
FTexture* GBlackTextureDepthCube = new TGlobalResource<FSolidColorTextureCube, FRenderResource::EInitPhase::Pre>(PF_ShadowDepth);
|
|
|
|
class FBlackCubeArrayTexture : public FTexture
|
|
{
|
|
public:
|
|
// FResource interface.
|
|
virtual void InitRHI(FRHICommandListBase& RHICmdList) override
|
|
{
|
|
const FRHITextureCreateDesc CreateDesc =
|
|
FRHITextureCreateDesc::CreateCubeArray(TEXT("BlackCubeArray"), 1, 1, PF_B8G8R8A8)
|
|
.SetFlags(ETextureCreateFlags::ShaderResource)
|
|
.SetClassName(TEXT("FBlackCubeArrayTexture"))
|
|
.SetInitialState(ERHIAccess::SRVMask)
|
|
.SetInitActionInitializer();
|
|
|
|
FRHITextureInitializer Initializer = RHICmdList.CreateTextureInitializer(CreateDesc);
|
|
|
|
for (uint32 FaceIndex = 0; FaceIndex < 6; FaceIndex++)
|
|
{
|
|
FRHITextureSubresourceInitializer Subresource = Initializer.GetTextureCubeSubresource(FaceIndex, 0);
|
|
|
|
// Note: alpha is used by reflection environment to say how much of the foreground texture is visible, so 0 says it is completely invisible
|
|
Subresource.WriteColor(FColor(0, 0, 0, 0));
|
|
}
|
|
|
|
// Create the texture RHI.
|
|
TextureRHI = Initializer.Finalize();
|
|
|
|
// Create the sampler state RHI resource.
|
|
FSamplerStateInitializerRHI SamplerStateInitializer(SF_Point, AM_Wrap, AM_Wrap, AM_Wrap);
|
|
SamplerStateRHI = GetOrCreateSamplerState(SamplerStateInitializer);
|
|
}
|
|
|
|
virtual uint32 GetSizeX() const override { return 1; }
|
|
virtual uint32 GetSizeY() const override { return 1; }
|
|
};
|
|
FTexture* GBlackCubeArrayTexture = new TGlobalResource<FBlackCubeArrayTexture, FRenderResource::EInitPhase::Pre>;
|
|
|
|
/**
|
|
* A UINT 1x1 texture.
|
|
*/
|
|
class FBlackUintTexture : public FTextureWithSRV
|
|
{
|
|
public:
|
|
// FResource interface.
|
|
virtual void InitRHI(FRHICommandListBase& RHICmdList) override
|
|
{
|
|
const FRHITextureCreateDesc CreateDesc =
|
|
FRHITextureCreateDesc::Create2D(TEXT("BlackUintTexture"), 1, 1, PF_R32G32B32A32_UINT)
|
|
.SetFlags(ETextureCreateFlags::ShaderResource)
|
|
.SetClassName(TEXT("FBlackUintTexture"))
|
|
.SetInitActionInitializer();
|
|
|
|
FRHITextureInitializer Initializer = RHICmdList.CreateTextureInitializer(CreateDesc);
|
|
|
|
FRHITextureSubresourceInitializer Subresource = Initializer.GetTextureCubeSubresource(0, 0);
|
|
FMemory::Memzero(Subresource.Data, Subresource.Size);
|
|
|
|
TextureRHI = Initializer.Finalize();
|
|
|
|
// Create the sampler state RHI resource.
|
|
FSamplerStateInitializerRHI SamplerStateInitializer(SF_Point, AM_Wrap, AM_Wrap, AM_Wrap);
|
|
SamplerStateRHI = GetOrCreateSamplerState(SamplerStateInitializer);
|
|
|
|
// Create a view of the texture
|
|
ShaderResourceViewRHI = RHICmdList.CreateShaderResourceView(TextureRHI, FRHIViewDesc::CreateTextureSRV().SetDimensionFromTexture(TextureRHI));
|
|
}
|
|
|
|
virtual uint32 GetSizeX() const override { return 1; }
|
|
virtual uint32 GetSizeY() const override { return 1; }
|
|
};
|
|
|
|
FTexture* GBlackUintTexture = new TGlobalResource<FBlackUintTexture, FRenderResource::EInitPhase::Pre>;
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// FNullColorVertexBuffer
|
|
|
|
FNullColorVertexBuffer::FNullColorVertexBuffer() = default;
|
|
FNullColorVertexBuffer::~FNullColorVertexBuffer() = default;
|
|
|
|
void FNullColorVertexBuffer::InitRHI(FRHICommandListBase& RHICmdList)
|
|
{
|
|
// create a static vertex buffer
|
|
const FRHIBufferCreateDesc CreateDesc =
|
|
FRHIBufferCreateDesc::CreateVertex<uint32>(TEXT("FNullColorVertexBuffer"), 4)
|
|
.AddUsage(EBufferUsageFlags::Static | EBufferUsageFlags::ShaderResource)
|
|
.SetInitialState(ERHIAccess::VertexOrIndexBuffer | ERHIAccess::SRVMask);
|
|
|
|
const uint32 DWWhite = FColor(255, 255, 255, 255).DWColor();
|
|
const uint32 ColorValues[4] = { DWWhite, DWWhite, DWWhite, DWWhite };
|
|
|
|
VertexBufferRHI = UE::RHIResourceUtils::CreateBufferWithArray(RHICmdList, CreateDesc, ColorValues);
|
|
VertexBufferSRV = RHICmdList.CreateShaderResourceView(
|
|
VertexBufferRHI,
|
|
FRHIViewDesc::CreateBufferSRV()
|
|
.SetType(FRHIViewDesc::EBufferType::Typed)
|
|
.SetFormat(PF_R8G8B8A8));
|
|
}
|
|
|
|
void FNullColorVertexBuffer::ReleaseRHI()
|
|
{
|
|
VertexBufferSRV.SafeRelease();
|
|
FVertexBuffer::ReleaseRHI();
|
|
}
|
|
|
|
/** The global null color vertex buffer, which is set with a stride of 0 on meshes without a color component. */
|
|
TGlobalResource<FNullColorVertexBuffer, FRenderResource::EInitPhase::Pre> GNullColorVertexBuffer;
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// FNullVertexBuffer
|
|
|
|
FNullVertexBuffer::FNullVertexBuffer() = default;
|
|
FNullVertexBuffer::~FNullVertexBuffer() = default;
|
|
|
|
void FNullVertexBuffer::InitRHI(FRHICommandListBase& RHICmdList)
|
|
{
|
|
// create a static vertex buffer
|
|
const FRHIBufferCreateDesc CreateDesc =
|
|
FRHIBufferCreateDesc::CreateVertex<FVector3f>(TEXT("FNullVertexBuffer"), 1)
|
|
.AddUsage(EBufferUsageFlags::Static | EBufferUsageFlags::ShaderResource)
|
|
.SetInitialState(ERHIAccess::VertexOrIndexBuffer | ERHIAccess::SRVMask);
|
|
|
|
const FVector3f InitialValue(0.0f);
|
|
|
|
VertexBufferRHI = UE::RHIResourceUtils::CreateBufferWithValue(RHICmdList, CreateDesc, InitialValue);
|
|
VertexBufferSRV = RHICmdList.CreateShaderResourceView(
|
|
VertexBufferRHI,
|
|
FRHIViewDesc::CreateBufferSRV()
|
|
.SetType(FRHIViewDesc::EBufferType::Typed)
|
|
.SetFormat(PF_R8G8B8A8));
|
|
}
|
|
|
|
void FNullVertexBuffer::ReleaseRHI()
|
|
{
|
|
VertexBufferSRV.SafeRelease();
|
|
FVertexBuffer::ReleaseRHI();
|
|
}
|
|
|
|
/** The global null vertex buffer, which is set with a stride of 0 on meshes */
|
|
TGlobalResource<FNullVertexBuffer, FRenderResource::EInitPhase::Pre> GNullVertexBuffer;
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// FScreenSpaceVertexBuffer
|
|
|
|
static const FVector2f GScreenSpaceVertexBufferData[4] =
|
|
{
|
|
FVector2f(-1,-1),
|
|
FVector2f(-1,+1),
|
|
FVector2f(+1,-1),
|
|
FVector2f(+1,+1),
|
|
};
|
|
|
|
void FScreenSpaceVertexBuffer::InitRHI(FRHICommandListBase& RHICmdList)
|
|
{
|
|
// create a static vertex buffer
|
|
VertexBufferRHI = UE::RHIResourceUtils::CreateVertexBufferFromArray(RHICmdList, TEXT("FScreenSpaceVertexBuffer"), EBufferUsageFlags::Static, MakeConstArrayView(GScreenSpaceVertexBufferData));
|
|
}
|
|
|
|
TGlobalResource<FScreenSpaceVertexBuffer, FRenderResource::EInitPhase::Pre> GScreenSpaceVertexBuffer;
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// FTileVertexDeclaration
|
|
|
|
FTileVertexDeclaration::FTileVertexDeclaration() = default;
|
|
FTileVertexDeclaration::~FTileVertexDeclaration() = default;
|
|
|
|
void FTileVertexDeclaration::InitRHI(FRHICommandListBase& RHICmdList)
|
|
{
|
|
FVertexDeclarationElementList Elements;
|
|
uint16 Stride = sizeof(FVector2f);
|
|
Elements.Add(FVertexElement(0, 0, VET_Float2, 0, Stride, false));
|
|
VertexDeclarationRHI = RHICreateVertexDeclaration(Elements);
|
|
}
|
|
|
|
void FTileVertexDeclaration::ReleaseRHI()
|
|
{
|
|
VertexDeclarationRHI.SafeRelease();
|
|
}
|
|
|
|
TGlobalResource<FTileVertexDeclaration, FRenderResource::EInitPhase::Pre> GTileVertexDeclaration;
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// FCubeIndexBuffer
|
|
|
|
void FCubeIndexBuffer::InitRHI(FRHICommandListBase& RHICmdList)
|
|
{
|
|
// create a static index buffer
|
|
IndexBufferRHI = UE::RHIResourceUtils::CreateIndexBufferFromArray(RHICmdList, TEXT("FCubeIndexBuffer"), EBufferUsageFlags::Static, MakeConstArrayView(GCubeIndices));
|
|
}
|
|
|
|
TGlobalResource<FCubeIndexBuffer, FRenderResource::EInitPhase::Pre> GCubeIndexBuffer;
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// FTwoTrianglesIndexBuffer
|
|
|
|
static const uint16 GTwoTrianglesIndexBufferData[6] = { 0, 1, 3, 0, 3, 2 };
|
|
|
|
void FTwoTrianglesIndexBuffer::InitRHI(FRHICommandListBase& RHICmdList)
|
|
{
|
|
// create a static index buffer
|
|
IndexBufferRHI = UE::RHIResourceUtils::CreateIndexBufferFromArray(RHICmdList, TEXT("FTwoTrianglesIndexBuffer"), EBufferUsageFlags::Static, MakeConstArrayView(GTwoTrianglesIndexBufferData));
|
|
}
|
|
|
|
TGlobalResource<FTwoTrianglesIndexBuffer, FRenderResource::EInitPhase::Pre> GTwoTrianglesIndexBuffer;
|
|
|
|
/*------------------------------------------------------------------------------
|
|
FGlobalDynamicVertexBuffer implementation.
|
|
------------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* An individual dynamic vertex buffer.
|
|
*/
|
|
template <typename BufferType>
|
|
class TDynamicBuffer final : public BufferType
|
|
{
|
|
public:
|
|
/** The aligned size of all dynamic vertex buffers. */
|
|
enum { ALIGNMENT = (1 << 16) }; // 64KB
|
|
/** Pointer to the vertex buffer mapped in main memory. */
|
|
uint8* MappedBuffer;
|
|
/** Size of the vertex buffer in bytes. */
|
|
uint32 BufferSize;
|
|
/** Number of bytes currently allocated from the buffer. */
|
|
uint32 AllocatedByteCount;
|
|
/** Stride of the buffer in bytes. */
|
|
uint32 Stride;
|
|
/** Last render thread frame this resource was used in. */
|
|
uint64 LastUsedFrame = 0;
|
|
|
|
/** Default constructor. */
|
|
explicit TDynamicBuffer(uint32 InMinBufferSize, uint32 InStride)
|
|
: MappedBuffer(NULL)
|
|
, BufferSize(FMath::Max<uint32>(Align(InMinBufferSize, ALIGNMENT), ALIGNMENT))
|
|
, AllocatedByteCount(0)
|
|
, Stride(InStride)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Locks the vertex buffer so it may be written to.
|
|
*/
|
|
void Lock(FRHICommandListBase& RHICmdList)
|
|
{
|
|
check(MappedBuffer == NULL);
|
|
check(AllocatedByteCount == 0);
|
|
check(IsValidRef(BufferType::GetRHI()));
|
|
MappedBuffer = (uint8*)RHICmdList.LockBuffer(BufferType::GetRHI(), 0, BufferSize, RLM_WriteOnly);
|
|
}
|
|
|
|
/**
|
|
* Unocks the buffer so the GPU may read from it.
|
|
*/
|
|
void Unlock(FRHICommandListBase& RHICmdList)
|
|
{
|
|
check(MappedBuffer != NULL);
|
|
check(IsValidRef(BufferType::GetRHI()));
|
|
RHICmdList.UnlockBuffer(BufferType::GetRHI());
|
|
MappedBuffer = NULL;
|
|
AllocatedByteCount = 0;
|
|
}
|
|
|
|
// FRenderResource interface.
|
|
virtual void InitRHI(FRHICommandListBase& RHICmdList) override
|
|
{
|
|
check(!IsValidRef(BufferType::GetRHI()));
|
|
|
|
const FRHIBufferCreateDesc CreateDesc =
|
|
FRHIBufferCreateDesc::Create(TEXT("FDynamicBuffer"), BufferSize, Stride, Stride == 0 ? EBufferUsageFlags::VertexBuffer : EBufferUsageFlags::IndexBuffer)
|
|
.AddUsage(EBufferUsageFlags::ShaderResource)
|
|
.SetInitialState(ERHIAccess::VertexOrIndexBuffer | ERHIAccess::SRVCompute);
|
|
|
|
BufferType::SetRHI(RHICmdList.CreateBuffer(CreateDesc));
|
|
MappedBuffer = NULL;
|
|
AllocatedByteCount = 0;
|
|
}
|
|
|
|
virtual void ReleaseRHI() override
|
|
{
|
|
BufferType::ReleaseRHI();
|
|
MappedBuffer = NULL;
|
|
AllocatedByteCount = 0;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* A pool of dynamic buffers.
|
|
*/
|
|
template <typename DynamicBufferType>
|
|
struct TDynamicBufferPool : public FRenderResource
|
|
{
|
|
UE::FMutex Mutex;
|
|
TArray<DynamicBufferType*> LiveList;
|
|
TArray<DynamicBufferType*> FreeList;
|
|
TArray<DynamicBufferType*> LockList;
|
|
TArray<DynamicBufferType*> ReclaimList;
|
|
std::atomic_uint32_t TotalAllocatedMemory{0};
|
|
uint32 CurrentCycle = 0;
|
|
|
|
DynamicBufferType* Acquire(FRHICommandListBase& RHICmdList, uint32 SizeInBytes, uint32 Stride)
|
|
{
|
|
const uint32 MinimumBufferSize = 65536u;
|
|
SizeInBytes = FMath::Max(SizeInBytes, MinimumBufferSize);
|
|
|
|
DynamicBufferType* FoundBuffer = nullptr;
|
|
bool bInitializeBuffer = false;
|
|
|
|
{
|
|
UE::TScopeLock Lock(Mutex);
|
|
|
|
// Traverse the free list like a stack, starting from the top, so recently reclaimed items are allocated first.
|
|
for (int32 Index = FreeList.Num() - 1; Index >= 0; --Index)
|
|
{
|
|
DynamicBufferType* Buffer = FreeList[Index];
|
|
|
|
if (SizeInBytes <= Buffer->BufferSize && Buffer->Stride == Stride)
|
|
{
|
|
FreeList.RemoveAt(Index, EAllowShrinking::No);
|
|
FoundBuffer = Buffer;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!FoundBuffer)
|
|
{
|
|
FoundBuffer = new DynamicBufferType(SizeInBytes, Stride);
|
|
LiveList.Emplace(FoundBuffer);
|
|
bInitializeBuffer = true;
|
|
}
|
|
|
|
check(FoundBuffer);
|
|
}
|
|
|
|
if (IsRenderAlarmLoggingEnabled())
|
|
{
|
|
UE_LOG(LogRendererCore, Warning, TEXT("FGlobalDynamicVertexBuffer::Allocate(%u), will have allocated %u total this frame"), SizeInBytes, TotalAllocatedMemory.load());
|
|
}
|
|
|
|
if (bInitializeBuffer)
|
|
{
|
|
FoundBuffer->InitResource(RHICmdList);
|
|
TotalAllocatedMemory += FoundBuffer->BufferSize;
|
|
}
|
|
|
|
FoundBuffer->Lock(RHICmdList);
|
|
FoundBuffer->LastUsedFrame = GFrameCounterRenderThread;
|
|
|
|
return FoundBuffer;
|
|
}
|
|
|
|
void Forfeit(FRHICommandListBase& RHICmdList, TConstArrayView<DynamicBufferType*> BuffersToForfeit)
|
|
{
|
|
if (!BuffersToForfeit.IsEmpty())
|
|
{
|
|
for (DynamicBufferType* Buffer : BuffersToForfeit)
|
|
{
|
|
Buffer->Unlock(RHICmdList);
|
|
}
|
|
|
|
UE::TScopeLock Lock(Mutex);
|
|
ReclaimList.Append(BuffersToForfeit);
|
|
}
|
|
}
|
|
|
|
void GarbageCollect()
|
|
{
|
|
UE::TScopeLock Lock(Mutex);
|
|
FreeList.Append(ReclaimList);
|
|
ReclaimList.Reset();
|
|
|
|
for (int32 Index = 0; Index < LiveList.Num(); ++Index)
|
|
{
|
|
DynamicBufferType* Buffer = LiveList[Index];
|
|
|
|
if (GGlobalBufferNumFramesUnusedThreshold > 0 && Buffer->LastUsedFrame + GGlobalBufferNumFramesUnusedThreshold <= GFrameCounterRenderThread)
|
|
{
|
|
TotalAllocatedMemory -= Buffer->BufferSize;
|
|
Buffer->ReleaseResource();
|
|
LiveList.RemoveAt(Index, EAllowShrinking::No);
|
|
FreeList.Remove(Buffer);
|
|
delete Buffer;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool IsRenderAlarmLoggingEnabled() const
|
|
{
|
|
return GMaxVertexBytesAllocatedPerFrame > 0 && TotalAllocatedMemory >= (size_t)GMaxVertexBytesAllocatedPerFrame;
|
|
}
|
|
|
|
private:
|
|
void ReleaseRHI() override
|
|
{
|
|
check(LockList.IsEmpty());
|
|
check(FreeList.Num() == LiveList.Num());
|
|
|
|
for (DynamicBufferType* Buffer : LiveList)
|
|
{
|
|
TotalAllocatedMemory -= Buffer->BufferSize;
|
|
Buffer->ReleaseResource();
|
|
delete Buffer;
|
|
}
|
|
LiveList.Empty();
|
|
FreeList.Empty();
|
|
}
|
|
};
|
|
|
|
static TGlobalResource<TDynamicBufferPool<FDynamicVertexBuffer>, FRenderResource::EInitPhase::Pre> GDynamicVertexBufferPool;
|
|
|
|
FGlobalDynamicVertexBuffer::FAllocation FGlobalDynamicVertexBuffer::Allocate(uint32 SizeInBytes)
|
|
{
|
|
checkf(RHICmdList, TEXT("FGlobalDynamicVertexBuffer was not initialized prior to calling Allocate."));
|
|
|
|
FAllocation Allocation;
|
|
|
|
if (VertexBuffers.IsEmpty() || VertexBuffers.Last()->AllocatedByteCount + SizeInBytes > VertexBuffers.Last()->BufferSize)
|
|
{
|
|
VertexBuffers.Emplace(GDynamicVertexBufferPool.Acquire(*RHICmdList, SizeInBytes, 0));
|
|
}
|
|
|
|
FDynamicVertexBuffer* VertexBuffer = VertexBuffers.Last();
|
|
|
|
checkf(VertexBuffer->AllocatedByteCount + SizeInBytes <= VertexBuffer->BufferSize, TEXT("Global vertex buffer allocation failed: BufferSize=%d AllocatedByteCount=%d SizeInBytes=%d"), VertexBuffer->BufferSize, VertexBuffer->AllocatedByteCount, SizeInBytes);
|
|
Allocation.Buffer = VertexBuffer->MappedBuffer + VertexBuffer->AllocatedByteCount;
|
|
Allocation.VertexBuffer = VertexBuffer;
|
|
Allocation.VertexOffset = VertexBuffer->AllocatedByteCount;
|
|
VertexBuffer->AllocatedByteCount += Align(SizeInBytes, RHI_RAW_VIEW_ALIGNMENT);
|
|
return Allocation;
|
|
}
|
|
|
|
bool FGlobalDynamicVertexBuffer::IsRenderAlarmLoggingEnabled() const
|
|
{
|
|
return GDynamicVertexBufferPool.IsRenderAlarmLoggingEnabled();
|
|
}
|
|
|
|
void FGlobalDynamicVertexBuffer::Commit()
|
|
{
|
|
if (RHICmdList)
|
|
{
|
|
GDynamicVertexBufferPool.Forfeit(*RHICmdList, VertexBuffers);
|
|
VertexBuffers.Reset();
|
|
}
|
|
}
|
|
|
|
static TGlobalResource<TDynamicBufferPool<FDynamicIndexBuffer>, FRenderResource::EInitPhase::Pre> GDynamicIndexBufferPool;
|
|
|
|
FGlobalDynamicIndexBuffer::FAllocation FGlobalDynamicIndexBuffer::Allocate(uint32 NumIndices, uint32 IndexStride)
|
|
{
|
|
checkf(RHICmdList, TEXT("FGlobalDynamicIndexBuffer was not initialized prior to calling Allocate."));
|
|
|
|
FAllocation Allocation;
|
|
|
|
if (IndexStride != 2 && IndexStride != 4)
|
|
{
|
|
return Allocation;
|
|
}
|
|
|
|
const uint32 SizeInBytes = NumIndices * IndexStride;
|
|
|
|
TArray<FDynamicIndexBuffer*>& IndexBuffers = (IndexStride == 2)
|
|
? IndexBuffers16
|
|
: IndexBuffers32;
|
|
|
|
if (IndexBuffers.IsEmpty() || IndexBuffers.Last()->AllocatedByteCount + SizeInBytes > IndexBuffers.Last()->BufferSize)
|
|
{
|
|
IndexBuffers.Emplace(GDynamicIndexBufferPool.Acquire(*RHICmdList, SizeInBytes, IndexStride));
|
|
}
|
|
|
|
FDynamicIndexBuffer* IndexBuffer = IndexBuffers.Last();
|
|
|
|
checkf(IndexBuffer->AllocatedByteCount + SizeInBytes <= IndexBuffer->BufferSize, TEXT("Global index buffer allocation failed: BufferSize=%d BufferStride=%d AllocatedByteCount=%d SizeInBytes=%d"), IndexBuffer->BufferSize, IndexBuffer->Stride, IndexBuffer->AllocatedByteCount, SizeInBytes);
|
|
Allocation.Buffer = IndexBuffer->MappedBuffer + IndexBuffer->AllocatedByteCount;
|
|
Allocation.IndexBuffer = IndexBuffer;
|
|
Allocation.FirstIndex = IndexBuffer->AllocatedByteCount / IndexStride;
|
|
IndexBuffer->AllocatedByteCount += Align(SizeInBytes, RHI_RAW_VIEW_ALIGNMENT);
|
|
return Allocation;
|
|
}
|
|
|
|
void FGlobalDynamicIndexBuffer::Commit()
|
|
{
|
|
if (RHICmdList)
|
|
{
|
|
GDynamicIndexBufferPool.Forfeit(*RHICmdList, IndexBuffers16);
|
|
GDynamicIndexBufferPool.Forfeit(*RHICmdList, IndexBuffers32);
|
|
IndexBuffers16.Reset();
|
|
IndexBuffers32.Reset();
|
|
}
|
|
}
|
|
|
|
namespace GlobalDynamicBuffer
|
|
{
|
|
void GarbageCollect()
|
|
{
|
|
GDynamicVertexBufferPool.GarbageCollect();
|
|
GDynamicIndexBufferPool.GarbageCollect();
|
|
}
|
|
} |