UE4 RenderSource
UE4 渲染API重新封装了诸如OpenGL、D3D和Vulkan API。
渲染资源RenderResource
属于渲染线程的渲染资源。
/**
* A rendering resource which is owned by the rendering thread.
*/
class RENDERCORE_API FRenderResource
初始化渲染资源
/**
* 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);
void BeginInitResource(FRenderResource* Resource)
{
ENQUEUE_RENDER_COMMAND(InitCommand)(
[Resource](FRHICommandListImmediate& RHICmdList)
{
Resource->InitResource();
});
}
void FRenderResource::InitResource()
{
check(IsInRenderingThread());
if (ListIndex == INDEX_NONE)
{
TArray<FRenderResource*>& ResourceList = GetResourceList();
TArray<int32>& FreeIndicesList = GetFreeIndicesList();
// If resource list is currently being iterated, new resources must be added to the end of the list, to ensure they're processed during the iteration
// Otherwise empty slots in the list may be re-used for new resources
int32 LocalListIndex = INDEX_NONE;
if (FreeIndicesList.Num() > 0 && ResourceListIterationActive.GetValue() == 0)
{
LocalListIndex = FreeIndicesList.Pop();
check(ResourceList[LocalListIndex] == nullptr);
ResourceList[LocalListIndex] = this;
}
else
{
LocalListIndex = ResourceList.Add(this);
}
if (GIsRHIInitialized)
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE(InitRenderResource);
InitDynamicRHI();
InitRHI();
}
FPlatformMisc::MemoryBarrier(); // there are some multithreaded reads of ListIndex
ListIndex = LocalListIndex;
}
}
void FRenderResource::UpdateRHI()
{
check(IsInRenderingThread());
if(IsInitialized() && GIsRHIInitialized)
{
ReleaseRHI();
ReleaseDynamicRHI();
InitDynamicRHI();
InitRHI();
}
}
namespace
{
inline void InitOrUpdateResource(FRenderResource* Resource)
{
if (!Resource->IsInitialized())
{
Resource->InitResource();
}
else
{
Resource->UpdateRHI();
}
}
} // namespace
索引缓冲区(IBO)
/** Index Buffer */
class ENGINE_API FDynamicMeshIndexBuffer32 : public FIndexBuffer
{
public:
TArray<uint32> Indices;
virtual void InitRHI() override;
//void UpdateRHI();
};
建立索引缓冲区
void FDynamicMeshIndexBuffer32::InitRHI()
{
// create the index buffer
FRHIResourceCreateInfo CreateInfo;
IndexBufferRHI = RHICreateIndexBuffer(sizeof(uint32), Indices.Num() * sizeof(uint32), BUF_Static, CreateInfo);
// Copy the index data into the index buffer.
void* Buffer = RHILockIndexBuffer(IndexBufferRHI, 0, Indices.Num() * sizeof(uint32), RLM_WriteOnly);
FMemory::Memcpy(Buffer, Indices.GetData(), Indices.Num() * sizeof(uint32));
RHIUnlockIndexBuffer(IndexBufferRHI);
}
使用
FDynamicMeshIndexBuffer32 IndexBuffer;
IndexBuffer.Indices.SetNum(Triangles.size());
FMemory::Memcpy(IndexBuffer.Indices.GetData(), Triangles.data(), Triangles.size() * sizeof(uint32));
BeginInitResource(&IndexBuffer);//invoke InitRHI
...准备其他数据...
SetupMeshBatch();
顶点位置缓冲区(VBO)
/** A vertex buffer of positions. */
class FPositionVertexBuffer : public FVertexBuffer
初始化
void FPositionVertexBuffer::InitRHI()
{
VertexBufferRHI = CreateRHIBuffer_RenderThread();
// we have decide to create the SRV based on GMaxRHIShaderPlatform because this is created once and shared between feature levels for editor preview.
// Also check to see whether cpu access has been activated on the vertex data
if (VertexBufferRHI)
{
// we have decide to create the SRV based on GMaxRHIShaderPlatform because this is created once and shared between feature levels for editor preview.
bool bSRV = RHISupportsManualVertexFetch(GMaxRHIShaderPlatform) || IsGPUSkinCacheAvailable(GMaxRHIShaderPlatform);
// When bAllowCPUAccess is true, the meshes is likely going to be used for Niagara to spawn particles on mesh surface.
// And it can be the case for CPU *and* GPU access: no differenciation today. That is why we create a SRV in this case.
// This also avoid setting lots of states on all the members of all the different buffers used by meshes. Follow up: https://jira.it.epicgames.net/browse/UE-69376.
bSRV |= (VertexData && VertexData->GetAllowCPUAccess());
if(bSRV)
{
// When VertexData is null, this buffer hasn't been streamed in yet. We still need to create a FRHIShaderResourceView which will be
// cached in a vertex factory uniform buffer later. The nullptr tells the RHI that the SRV doesn't view on anything yet.
PositionComponentSRV = RHICreateShaderResourceView(FShaderResourceViewInitializer(VertexData ? VertexBufferRHI : nullptr, PF_R32_FLOAT));
}
}
}
顶点缓冲区的数据的拷贝
void CopyGPUBufferLocked(FRHIVertexBuffer* rhiVertexBuffer, void* src, SIZE_T size)
{
void* VertexBufferData = RHILockVertexBuffer(rhiVertexBuffer, 0, size, RLM_WriteOnly);
FMemory::Memcpy(VertexBufferData, src, size);
RHIUnlockVertexBuffer(rhiVertexBuffer);
}
CopyGPUBufferLocked(PositionVertexBuffer.VertexBufferRHI, PositionVertexBuffer.GetVertexData(),
PositionVertexBuffer.GetNumVertices() * PositionVertexBuffer.GetStride());
顶点颜色缓冲区
/**
* A vertex buffer of colors.
*/
class FColorVertexBuffer : public FVertexBuffer
顶点切线空间和UV缓冲区
/** Vertex buffer for a static mesh LOD */
class FStaticMeshVertexBuffer : public FRenderResource
顶点工厂(VAO)
/**
* A vertex factory which simply transforms explicit vertex attributes from local to world space.
*/
class ENGINE_API FLocalVertexFactory : public FVertexFactory
初始化顶点工厂InitVertexFactory
FLocalVertexFactory::FDataType Data;
PositionVertexBuffer.BindPositionVertexBuffer(&VertexFactory, Data);
StaticMeshVertexBuffer.BindTangentVertexBuffer(&VertexFactory, Data);
StaticMeshVertexBuffer.BindPackedTexCoordVertexBuffer(&VertexFactory, Data);
ColorVertexBuffer.BindColorVertexBuffer(&VertexFactory, Data);
VertexFactory.SetData(Data);
InitOrUpdateResource(&VertexFactory);
顶点工厂绑定顶点缓冲区
void FPositionVertexBuffer::BindPositionVertexBuffer(const FVertexFactory* VertexFactory, FStaticMeshDataType& StaticMeshData) const
{
StaticMeshData.PositionComponent = FVertexStreamComponent(
this,
STRUCT_OFFSET(FPositionVertex, Position),
GetStride(),
VET_Float3
);
StaticMeshData.PositionComponentSRV = PositionComponentSRV;
}
void FColorVertexBuffer::BindColorVertexBuffer(const FVertexFactory* VertexFactory, FStaticMeshDataType& StaticMeshData) const
{
if (GetNumVertices() == 0)
{
BindDefaultColorVertexBuffer(VertexFactory, StaticMeshData, NullBindStride::ZeroForDefaultBufferBind);
return;
}
StaticMeshData.ColorComponentsSRV = ColorComponentsSRV;
StaticMeshData.ColorIndexMask = ~0u;
{
StaticMeshData.ColorComponent = FVertexStreamComponent(
this,
0, // Struct offset to color
GetStride(),
VET_Color,
EVertexStreamUsage::ManualFetch
);
}
}
绘制SetupMeshBatch
FMeshBatch MeshBatch;
//绑定VAO
MeshBatch.VertexFactory = &VertexFactory;
BatchElement.FirstIndex = 0;
BatchElement.IndexBuffer = &IndexBuffer;
BatchElement.NumPrimitives = IndexBuffer.Indices.Num() / 3;
BatchElement.MinVertexIndex = 0;
BatchElement.MaxVertexIndex = PositionVertexBuffer.GetNumVertices() - 1;
伪代码示例使用
FDynamicMeshIndexBuffer32 IndexBuffer;
FPositionVertexBuffer PositionVertexBuffer;
FColorVertexBuffer ColorVertexBuffer;
FStaticMeshVertexBuffer StaticMeshVertexBuffer;
FLocalVertexFactory VertexFactory;
//init index
IndexBuffer.Indices.SetNum(Triangles.size());
FMemory::Memcpy(IndexBuffer.Indices.GetData(), Triangles.data(),Triangles.size() * sizeof(uint32));
//init position
int32 NumVertices;
int32 NumUVs ;
PositionVertexBuffer.Init(NumVertices);
for (int32 i = 0; i < NumVertices; ++i)
{
const auto& vertex = *reinterpret_cast<const FVector*>(&Vertices[i]);
PositionVertexBuffer.VertexPosition(i) = vertex;
}
// init color
ColorVertexBuffer.Init(NumVertices);
StaticMeshVertexBuffer.SetUseHighPrecisionTangentBasis(true);
// init tangent noraml uv
StaticMeshVertexBuffer.Init(NumVertices, NumUVs);
for (int32 i = 0; i < NumVertices; ++i)
{
StaticMeshVertexBuffer.SetVertexTangents(i, FVector(0, 1, 0), FVector(1, 0, 0), FVector(0, 0, 1));
for (int32 j = 0; j < NumUVs; ++j)
{
StaticMeshVertexBuffer.SetVertexUV(i, j, FVector2D(0, 0));
}
ColorVertexBuffer.VertexColor(i) = FColor(0);
}
InitOrUpdateResource(&PositionVertexBuffer);
InitOrUpdateResource(&StaticMeshVertexBuffer);
InitOrUpdateResource(&ColorVertexBuffer);
BeginInitResource(&IndexBuffer);
InitVertexFactory();
InitOrUpdateResource(&VertexFactory);
//初次绘制
SetupMeshBatch();
//update position
for (int32 i = 0; i < NumVertices; ++i)
{
const auto& vertex = *reinterpret_cast<const FVector*>(&Vertices[i]);
PositionVertexBuffer.VertexPosition(i) = vertex;
}
//update tangent uv color
for (int32 i = 0; i < NumVertices; ++i)
{
StaticMeshVertexBuffer.SetVertexTangents(i, FVector(0, 1, 0), FVector(1, 0, 0), FVector(0, 0, 1));
for (int32 j = 0; j < NumUVs; ++j)
{
StaticMeshVertexBuffer.SetVertexUV(i, j, FVector2D(0, 0));
}
ColorVertexBuffer.VertexColor(i) = FColor(0);
}
// copy pos to buffer
CopyGPUBufferLocked(PositionVertexBuffer.VertexBufferRHI, PositionVertexBuffer.GetVertexData(),
PositionVertexBuffer.GetNumVertices() * PositionVertexBuffer.GetStride());
// copy color to buffer
CopyGPUBufferLocked(ColorVertexBuffer.VertexBufferRHI, ColorVertexBuffer.GetVertexData(),
ColorVertexBuffer.GetNumVertices() * ColorVertexBuffer.GetStride());
// copy tanget to buffer
CopyGPUBufferLocked(StaticMeshVertexBuffer.TangentsVertexBuffer.VertexBufferRHI, StaticMeshVertexBuffer.GetTangentData(),
StaticMeshVertexBuffer.GetTangentSize());
// copy tex to buffer
CopyGPUBufferLocked(StaticMeshVertexBuffer.TexCoordVertexBuffer.VertexBufferRHI, StaticMeshVertexBuffer.GetTexCoordData(),
StaticMeshVertexBuffer.GetTexCoordSize());
//更新绘制
SetupMeshBatch();
//资源释放
PositionVertexBuffer.ReleaseResource();
StaticMeshVertexBuffer.ReleaseResource();
ColorVertexBuffer.ReleaseResource();
IndexBuffer.ReleaseResource();
VertexFactory.ReleaseResource();