Files
UnrealEngineUWP/Engine/Source/Runtime/RenderCore/Public/RenderResource.h
Marcus Wassmer 8a49574735 Vertex and Index buffer write locks on PS4 no longer require memcopies.
RHi now provides CreateAndLock functions for Vertex and Index buffers which are more efficient on certain platforms.
Use r.PS4StandardWriteLocks=1 to enable the old default functionality for testing if necessary.
#codereview Gil.Grib,Lee.Clark

[CL 2606146 by Marcus Wassmer in Main branch]
2015-06-30 14:18:55 -04:00

668 lines
18 KiB
C++

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
RenderResource.h: Render resource definitions.
=============================================================================*/
#pragma once
#include "RenderCore.h"
#include "RHI.h"
#include "RenderingThread.h"
/**
* A rendering resource which is owned by the rendering thread.
*/
class RENDERCORE_API FRenderResource
{
public:
/** @return The global initialized resource list. */
static TLinkedList<FRenderResource*>*& GetResourceList();
/** Default constructor. */
FRenderResource()
: FeatureLevel(ERHIFeatureLevel::Num)
, bInitialized(false)
{}
/** Constructor when we know what feature level this resource should support */
FRenderResource(ERHIFeatureLevel::Type InFeatureLevel)
: FeatureLevel(InFeatureLevel)
, bInitialized(false)
{}
/** Destructor used to catch unreleased resources. */
virtual ~FRenderResource();
/**
* Initializes the dynamic RHI resource and/or RHI render target used by this resource.
* Called when the resource is initialized, or when reseting all RHI resources.
* Resources that need to initialize after a D3D device reset must implement this function.
* This is only called by the rendering thread.
*/
virtual void InitDynamicRHI() {}
/**
* Releases the dynamic RHI resource and/or RHI render target resources used by this resource.
* Called when the resource is released, or when reseting all RHI resources.
* Resources that need to release before a D3D device reset must implement this function.
* This is only called by the rendering thread.
*/
virtual void ReleaseDynamicRHI() {}
/**
* Initializes the RHI resources used by this resource.
* Called when entering the state where both the resource and the RHI have been initialized.
* This is only called by the rendering thread.
*/
virtual void InitRHI() {}
/**
* Releases the RHI resources used by this resource.
* Called when leaving the state where both the resource and the RHI have been initialized.
* This is only called by the rendering thread.
*/
virtual void ReleaseRHI() {}
/**
* Initializes the resource.
* This is only called by the rendering thread.
*/
virtual void InitResource();
/**
* Prepares the resource for deletion.
* This is only called by the rendering thread.
*/
virtual void ReleaseResource();
/**
* If the resource's RHI resources have been initialized, then release and reinitialize it. Otherwise, do nothing.
* This is only called by the rendering thread.
*/
void UpdateRHI();
// Probably temporary code that sends a task back to renderthread_local and blocks waiting for it to call InitResource
void InitResourceFromPossiblyParallelRendering();
/** @return The resource's friendly name. Typically a UObject name. */
virtual FString GetFriendlyName() const { return TEXT("undefined"); }
// Accessors.
FORCEINLINE bool IsInitialized() const { return bInitialized; }
// For those situations when the default ctor had to be used
inline void SetFeatureLevel(ERHIFeatureLevel::Type InFeatureLevel)
{
FeatureLevel = InFeatureLevel;
}
protected:
// This is used during mobile editor preview refactor, this will eventually be replaced with a parameter to InitRHI() etc..
ERHIFeatureLevel::Type GetFeatureLevel() const { return FeatureLevel == ERHIFeatureLevel::Num ? GMaxRHIFeatureLevel : FeatureLevel; }
FORCEINLINE bool HasValidFeatureLevel() const { return FeatureLevel < ERHIFeatureLevel::Num; }
private:
ERHIFeatureLevel::Type FeatureLevel;
/** This resource's link in the global resource list. */
TLinkedList<FRenderResource*> ResourceLink;
/** True if the resource has been initialized. */
bool bInitialized;
};
/**
* Sends a message to the rendering thread to initialize a resource.
* This is called in the game thread.
*/
extern RENDERCORE_API void BeginInitResource(FRenderResource* Resource);
/**
* Sends a message to the rendering thread to update a resource.
* This is called in the game thread.
*/
extern RENDERCORE_API void BeginUpdateResourceRHI(FRenderResource* Resource);
/**
* Sends a message to the rendering thread to release a resource.
* This is called in the game thread.
*/
extern RENDERCORE_API void BeginReleaseResource(FRenderResource* Resource);
/**
* Sends a message to the rendering thread to release a resource, and spins until the rendering thread has processed the message.
* This is called in the game thread.
*/
extern RENDERCORE_API void ReleaseResourceAndFlush(FRenderResource* Resource);
/** Used to declare a render resource that is initialized/released by static initialization/destruction. */
template<class ResourceType>
class TGlobalResource : public ResourceType
{
public:
/** Default constructor. */
TGlobalResource()
{
InitGlobalResource();
}
/** Initialization constructor: 1 parameter. */
template<typename T1>
explicit TGlobalResource(T1 Param1)
: ResourceType(Param1)
{
InitGlobalResource();
}
/** Destructor. */
virtual ~TGlobalResource()
{
ReleaseGlobalResource();
}
private:
/**
* Initialize the global resource.
*/
void InitGlobalResource()
{
if(IsInRenderingThread())
{
// If the resource is constructed in the rendering thread, directly initialize it.
((ResourceType*)this)->InitResource();
}
else
{
// If the resource is constructed outside of the rendering thread, enqueue a command to initialize it.
BeginInitResource((ResourceType*)this);
}
}
/**
* Release the global resource.
*/
void ReleaseGlobalResource()
{
// This should be called in the rendering thread, or at shutdown when the rendering thread has exited.
// However, it may also be called at shutdown after an error, when the rendering thread is still running.
// To avoid a second error in that case we don't assert.
#if 0
check(IsInRenderingThread());
#endif
// Cleanup the resource.
((ResourceType*)this)->ReleaseResource();
}
};
enum EMipFadeSettings
{
MipFade_Normal = 0,
MipFade_Slow,
MipFade_NumSettings,
};
/** Mip fade settings, selectable by chosing a different EMipFadeSettings. */
struct FMipFadeSettings
{
FMipFadeSettings( float InFadeInSpeed, float InFadeOutSpeed )
: FadeInSpeed( InFadeInSpeed )
, FadeOutSpeed( InFadeOutSpeed )
{
}
/** How many seconds to fade in one mip-level. */
float FadeInSpeed;
/** How many seconds to fade out one mip-level. */
float FadeOutSpeed;
};
/** Whether to enable mip-level fading or not: +1.0f if enabled, -1.0f if disabled. */
extern RENDERCORE_API float GEnableMipLevelFading;
/** Global mip fading settings, indexed by EMipFadeSettings. */
extern RENDERCORE_API FMipFadeSettings GMipFadeSettings[MipFade_NumSettings];
/**
* Functionality for fading in/out texture mip-levels.
*/
struct FMipBiasFade
{
/** Default constructor that sets all values to default (no mips). */
FMipBiasFade()
: TotalMipCount(0.0f)
, MipCountDelta(0.0f)
, StartTime(0.0f)
, MipCountFadingRate(0.0f)
, BiasOffset(0.0f)
{
}
/** Number of mip-levels in the texture. */
float TotalMipCount;
/** Number of mip-levels to fade (negative if fading out / decreasing the mipcount). */
float MipCountDelta;
/** Timestamp when the fade was started. */
float StartTime;
/** Number of seconds to interpolate through all MipCountDelta (inverted). */
float MipCountFadingRate;
/** Difference between total texture mipcount and the starting mipcount for the fade. */
float BiasOffset;
/**
* Sets up a new interpolation target for the mip-bias.
* @param ActualMipCount Number of mip-levels currently in memory
* @param TargetMipCount Number of mip-levels we're changing to
* @param LastRenderTime Timestamp when it was last rendered (FApp::CurrentTime time space)
* @param FadeSetting Which fade speed settings to use
*/
RENDERCORE_API void SetNewMipCount( float ActualMipCount, float TargetMipCount, double LastRenderTime, EMipFadeSettings FadeSetting );
/**
* Calculates the interpolated mip-bias based on the current time.
* @return Interpolated mip-bias value
*/
inline float CalcMipBias() const
{
float DeltaTime = GRenderingRealtimeClock.GetCurrentTime() - StartTime;
float TimeFactor = FMath::Min<float>(DeltaTime * MipCountFadingRate, 1.0f);
float MipBias = BiasOffset - MipCountDelta*TimeFactor;
return FMath::FloatSelect(GEnableMipLevelFading, MipBias, 0.0f);
}
/**
* Checks whether the mip-bias is still interpolating.
* @return true if the mip-bias is still interpolating
*/
inline bool IsFading( ) const
{
float DeltaTime = GRenderingRealtimeClock.GetCurrentTime() - StartTime;
float TimeFactor = DeltaTime * MipCountFadingRate;
return (FMath::Abs<float>(MipCountDelta) > SMALL_NUMBER && TimeFactor < 1.0f);
}
};
/** A textures resource. */
class FTexture : public FRenderResource
{
public:
/** The texture's RHI resource. */
FTextureRHIRef TextureRHI;
/** The sampler state to use for the texture. */
FSamplerStateRHIRef SamplerStateRHI;
/** Sampler state to be used in deferred passes when discontinuities in ddx / ddy would cause too blurry of a mip to be used. */
FSamplerStateRHIRef DeferredPassSamplerStateRHI;
/** The last time the texture has been bound */
mutable double LastRenderTime;
/** Base values for fading in/out mip-levels. */
FMipBiasFade MipBiasFade;
/** true if the texture is in a greyscale texture format. */
bool bGreyScaleFormat;
/**
* true if the texture is in the same gamma space as the intended rendertarget (e.g. screenshots).
* The texture will have sRGB==false and bIgnoreGammaConversions==true, causing a non-sRGB texture lookup
* and no gamma-correction in the shader.
*/
bool bIgnoreGammaConversions;
/**
* Is the pixel data in this texture sRGB?
**/
bool bSRGB;
/** Default constructor. */
FTexture()
: TextureRHI(NULL)
, SamplerStateRHI(NULL)
, DeferredPassSamplerStateRHI(NULL)
, LastRenderTime(-FLT_MAX)
, bGreyScaleFormat(false)
, bIgnoreGammaConversions(false)
, bSRGB(false)
{}
// Destructor
virtual ~FTexture() {}
/** Returns the width of the texture in pixels. */
virtual uint32 GetSizeX() const
{
return 0;
}
/** Returns the height of the texture in pixels. */
virtual uint32 GetSizeY() const
{
return 0;
}
// FRenderResource interface.
virtual void ReleaseRHI() override
{
TextureRHI.SafeRelease();
SamplerStateRHI.SafeRelease();
DeferredPassSamplerStateRHI.SafeRelease();
}
virtual FString GetFriendlyName() const override { return TEXT("FTexture"); }
};
/** A texture reference resource. */
class RENDERCORE_API FTextureReference : public FRenderResource
{
public:
/** The texture reference's RHI resource. */
FTextureReferenceRHIRef TextureReferenceRHI;
private:
/** The last time the texture has been rendered via this reference. */
FLastRenderTimeContainer LastRenderTimeRHI;
/** True if the texture reference has been initialized from the game thread. */
bool bInitialized_GameThread;
public:
/** Default constructor. */
FTextureReference();
// Destructor
virtual ~FTextureReference();
/** Returns the last time the texture has been rendered via this reference. */
double GetLastRenderTime() const { return LastRenderTimeRHI.GetLastRenderTime(); }
/** Invalidates the last render time. */
void InvalidateLastRenderTime();
/** Returns true if the texture reference has been initialized from the game thread. */
bool IsInitialized_GameThread() const { return bInitialized_GameThread; }
/** Kicks off the initialization process on the game thread. */
void BeginInit_GameThread();
/** Kicks off the release process on the game thread. */
void BeginRelease_GameThread();
// FRenderResource interface.
virtual void InitRHI();
virtual void ReleaseRHI();
virtual FString GetFriendlyName() const;
};
/** A vertex buffer resource */
class RENDERCORE_API FVertexBuffer : public FRenderResource
{
public:
FVertexBufferRHIRef VertexBufferRHI;
/** Destructor. */
virtual ~FVertexBuffer() {}
// FRenderResource interface.
virtual void ReleaseRHI() override
{
VertexBufferRHI.SafeRelease();
}
virtual FString GetFriendlyName() const override { return TEXT("FVertexBuffer"); }
};
/**
* A vertex buffer with a single color component. This is used on meshes that don't have a color component
* to keep from needing a separate vertex factory to handle this case.
*/
class FNullColorVertexBuffer : public FVertexBuffer
{
public:
/**
* Initialize the RHI for this rendering resource
*/
virtual void InitRHI() override
{
// create a static vertex buffer
FRHIResourceCreateInfo CreateInfo;
void* LockedData = nullptr;
VertexBufferRHI = RHICreateAndLockVertexBuffer(sizeof(uint32), BUF_Static | BUF_ZeroStride, CreateInfo, LockedData);
uint32* Vertices = (uint32*)LockedData;
Vertices[0] = FColor(255, 255, 255, 255).DWColor();
RHIUnlockVertexBuffer(VertexBufferRHI);
}
};
/** The global null color vertex buffer, which is set with a stride of 0 on meshes without a color component. */
extern RENDERCORE_API TGlobalResource<FNullColorVertexBuffer> GNullColorVertexBuffer;
/** An index buffer resource. */
class FIndexBuffer : public FRenderResource
{
public:
FIndexBufferRHIRef IndexBufferRHI;
/** Destructor. */
virtual ~FIndexBuffer() {}
// FRenderResource interface.
virtual void ReleaseRHI() override
{
IndexBufferRHI.SafeRelease();
}
virtual FString GetFriendlyName() const override { return TEXT("FIndexBuffer"); }
};
/**
* A system for dynamically allocating GPU memory for vertices.
*/
class RENDERCORE_API FGlobalDynamicVertexBuffer
{
public:
/**
* Information regarding an allocation from this buffer.
*/
struct FAllocation
{
/** The location of the buffer in main memory. */
uint8* Buffer;
/** The vertex buffer to bind for draw calls. */
FVertexBuffer* VertexBuffer;
/** The offset in to the vertex buffer. */
uint32 VertexOffset;
/** Default constructor. */
FAllocation()
: Buffer(NULL)
, VertexBuffer(NULL)
, VertexOffset(0)
{
}
/** Returns true if the allocation is valid. */
FORCEINLINE bool IsValid() const
{
return Buffer != NULL;
}
};
/** Default constructor. */
FGlobalDynamicVertexBuffer();
/** Destructor. */
~FGlobalDynamicVertexBuffer();
/**
* Allocates space in the global vertex buffer.
* @param SizeInBytes - The amount of memory to allocate in bytes.
* @returns An FAllocation with information regarding the allocated memory.
*/
FAllocation Allocate(uint32 SizeInBytes);
/**
* Commits allocated memory to the GPU.
* WARNING: Once this buffer has been committed to the GPU, allocations
* remain valid only until the next call to Allocate!
*/
void Commit();
/**
* Obtain a reference to the global dynamic vertex buffer instance.
*/
static FGlobalDynamicVertexBuffer& Get();
private:
/** The pool of vertex buffers from which allocations are made. */
struct FDynamicVertexBufferPool* Pool;
};
/**
* A system for dynamically allocating GPU memory for indices.
*/
class RENDERCORE_API FGlobalDynamicIndexBuffer
{
public:
/**
* Information regarding an allocation from this buffer.
*/
struct FAllocation
{
/** The location of the buffer in main memory. */
uint8* Buffer;
/** The vertex buffer to bind for draw calls. */
FIndexBuffer* IndexBuffer;
/** The offset in to the index buffer. */
uint32 FirstIndex;
/** Default constructor. */
FAllocation()
: Buffer(NULL)
, IndexBuffer(NULL)
, FirstIndex(0)
{
}
/** Returns true if the allocation is valid. */
FORCEINLINE bool IsValid() const
{
return Buffer != NULL;
}
};
/** Default constructor. */
FGlobalDynamicIndexBuffer();
/** Destructor. */
~FGlobalDynamicIndexBuffer();
/**
* Allocates space in the global index buffer.
* @param NumIndices - The number of indices to allocate.
* @param IndexStride - The size of an index (2 or 4 bytes).
* @returns An FAllocation with information regarding the allocated memory.
*/
FAllocation Allocate(uint32 NumIndices, uint32 IndexStride);
/**
* Helper function to allocate.
* @param NumIndices - The number of indices to allocate.
* @returns an FAllocation with information regarding the allocated memory.
*/
template <typename IndexType>
inline FAllocation Allocate(uint32 NumIndices)
{
return Allocate(NumIndices, sizeof(IndexType));
}
/**
* Commits allocated memory to the GPU.
* WARNING: Once this buffer has been committed to the GPU, allocations
* remain valid only until the next call to Allocate!
*/
void Commit();
/**
* Obtain a reference to the global dynamic index buffer instance.
*/
static FGlobalDynamicIndexBuffer& Get();
private:
/** The pool of vertex buffers from which allocations are made. */
struct FDynamicIndexBufferPool* Pools[2];
};
/**
* A list of the most recently used bound shader states.
* This is used to keep bound shader states that have been used recently from being freed, as they're likely to be used again soon.
*/
template<uint32 Size>
class TBoundShaderStateHistory : public FRenderResource
{
public:
/** Initialization constructor. */
TBoundShaderStateHistory():
NextBoundShaderStateIndex(0)
{}
/** Adds a bound shader state to the history. */
void Add(FBoundShaderStateRHIParamRef BoundShaderState)
{
if (GRHISupportsParallelRHIExecute)
{
BoundShaderStateHistoryLock.Lock();
}
BoundShaderStates[NextBoundShaderStateIndex] = BoundShaderState;
NextBoundShaderStateIndex = (NextBoundShaderStateIndex + 1) % Size;
if (GRHISupportsParallelRHIExecute)
{
BoundShaderStateHistoryLock.Unlock();
}
}
FBoundShaderStateRHIParamRef GetLast()
{
check(!GRHISupportsParallelRHIExecute);
// % doesn't work as we want on negative numbers, so handle the wraparound manually
uint32 LastIndex = NextBoundShaderStateIndex == 0 ? Size - 1 : NextBoundShaderStateIndex - 1;
return BoundShaderStates[LastIndex];
}
// FRenderResource interface.
virtual void ReleaseRHI()
{
if (GRHISupportsParallelRHIExecute)
{
BoundShaderStateHistoryLock.Lock();
}
for(uint32 Index = 0;Index < Size;Index++)
{
BoundShaderStates[Index].SafeRelease();
}
if (GRHISupportsParallelRHIExecute)
{
BoundShaderStateHistoryLock.Unlock();
}
}
private:
FBoundShaderStateRHIRef BoundShaderStates[Size];
uint32 NextBoundShaderStateIndex;
FCriticalSection BoundShaderStateHistoryLock;
};