Refactor scene rendering to use batched culling for main view and shadow projections

This commit is contained in:
Wojtek Figat
2022-10-28 09:13:28 +02:00
parent e217d5e79b
commit eb52d333ae
44 changed files with 1064 additions and 514 deletions

View File

@@ -31,11 +31,11 @@
#define STREAM_TASK_BASE MainThreadTask
#endif
#define CHECK_INVALID_BUFFER(buffer) \
if (buffer->IsValidFor(this) == false) \
#define CHECK_INVALID_BUFFER(model, buffer) \
if (buffer->IsValidFor(model) == false) \
{ \
LOG(Warning, "Invalid Model Instance Buffer size {0} for Model {1}. It should be {2}. Manual update to proper size.", buffer->Count(), ToString(), MaterialSlots.Count()); \
buffer->Setup(this); \
LOG(Warning, "Invalid Model Instance Buffer size {0} for Model {1}. It should be {2}. Manual update to proper size.", buffer->Count(), model->ToString(), model->MaterialSlots.Count()); \
buffer->Setup(model); \
}
REGISTER_BINARY_ASSET_ABSTRACT(ModelBase, "FlaxEngine.ModelBase");
@@ -206,14 +206,15 @@ void Model::Draw(const RenderContext& renderContext, MaterialBase* material, con
LODs[lodIndex].Draw(renderContext, material, world, flags, receiveDecals);
}
void Model::Draw(const RenderContext& renderContext, const Mesh::DrawInfo& info)
template<typename ContextType>
FORCE_INLINE void ModelDraw(Model* model, const RenderContext& renderContext, const ContextType& context, const Mesh::DrawInfo& info)
{
ASSERT(info.Buffer);
if (!CanBeRendered())
if (!model->CanBeRendered())
return;
const auto frame = Engine::FrameCount;
const auto modelFrame = info.DrawState->PrevFrame + 1;
CHECK_INVALID_BUFFER(info.Buffer);
CHECK_INVALID_BUFFER(model, info.Buffer);
// Select a proper LOD index (model may be culled)
int32 lodIndex;
@@ -223,7 +224,7 @@ void Model::Draw(const RenderContext& renderContext, const Mesh::DrawInfo& info)
}
else
{
lodIndex = RenderTools::ComputeModelLOD(this, info.Bounds.Center, (float)info.Bounds.Radius, renderContext);
lodIndex = RenderTools::ComputeModelLOD(model, info.Bounds.Center, (float)info.Bounds.Radius, renderContext);
if (lodIndex == -1)
{
// Handling model fade-out transition
@@ -244,9 +245,9 @@ void Model::Draw(const RenderContext& renderContext, const Mesh::DrawInfo& info)
}
else
{
const auto prevLOD = ClampLODIndex(info.DrawState->PrevLOD);
const auto prevLOD = model->ClampLODIndex(info.DrawState->PrevLOD);
const float normalizedProgress = static_cast<float>(info.DrawState->LODTransition) * (1.0f / 255.0f);
LODs[prevLOD].Draw(renderContext, info, normalizedProgress);
model->LODs.Get()[prevLOD].Draw(renderContext, info, normalizedProgress);
}
}
@@ -254,7 +255,7 @@ void Model::Draw(const RenderContext& renderContext, const Mesh::DrawInfo& info)
}
}
lodIndex += info.LODBias + renderContext.View.ModelLODBias;
lodIndex = ClampLODIndex(lodIndex);
lodIndex = model->ClampLODIndex(lodIndex);
if (renderContext.View.IsSingleFrame)
{
@@ -287,22 +288,32 @@ void Model::Draw(const RenderContext& renderContext, const Mesh::DrawInfo& info)
// Draw
if (info.DrawState->PrevLOD == lodIndex || renderContext.View.IsSingleFrame)
{
LODs[lodIndex].Draw(renderContext, info, 0.0f);
model->LODs.Get()[lodIndex].Draw(context, info, 0.0f);
}
else if (info.DrawState->PrevLOD == -1)
{
const float normalizedProgress = static_cast<float>(info.DrawState->LODTransition) * (1.0f / 255.0f);
LODs[lodIndex].Draw(renderContext, info, 1.0f - normalizedProgress);
model->LODs.Get()[lodIndex].Draw(context, info, 1.0f - normalizedProgress);
}
else
{
const auto prevLOD = ClampLODIndex(info.DrawState->PrevLOD);
const auto prevLOD = model->ClampLODIndex(info.DrawState->PrevLOD);
const float normalizedProgress = static_cast<float>(info.DrawState->LODTransition) * (1.0f / 255.0f);
LODs[prevLOD].Draw(renderContext, info, normalizedProgress);
LODs[lodIndex].Draw(renderContext, info, normalizedProgress - 1.0f);
model->LODs.Get()[prevLOD].Draw(context, info, normalizedProgress);
model->LODs.Get()[lodIndex].Draw(context, info, normalizedProgress - 1.0f);
}
}
void Model::Draw(const RenderContext& renderContext, const Mesh::DrawInfo& info)
{
ModelDraw(this, renderContext, renderContext, info);
}
void Model::Draw(const RenderContextBatch& renderContextBatch, const Mesh::DrawInfo& info)
{
ModelDraw(this, renderContextBatch.GetMainContext(), renderContextBatch, info);
}
bool Model::SetupLODs(const Span<int32>& meshesCountPerLod)
{
ScopeLock lock(Locker);

View File

@@ -190,6 +190,13 @@ public:
/// <param name="info">The packed drawing info data.</param>
void Draw(const RenderContext& renderContext, const Mesh::DrawInfo& info);
/// <summary>
/// Draws the model.
/// </summary>
/// <param name="renderContextBatch">The rendering context batch.</param>
/// <param name="info">The packed drawing info data.</param>
void Draw(const RenderContextBatch& renderContextBatch, const Mesh::DrawInfo& info);
public:
/// <summary>
/// Setups the model LODs collection including meshes creation.

View File

@@ -18,6 +18,7 @@
#define MODEL_LOD_TO_CHUNK_INDEX(lod) (lod + 1)
class MeshBase;
struct RenderContextBatch;
/// <summary>
/// Base class for asset types that can contain a model resource.

View File

@@ -17,11 +17,11 @@
#include "Engine/Debug/Exceptions/ArgumentOutOfRangeException.h"
#include "Engine/Renderer/DrawCall.h"
#define CHECK_INVALID_BUFFER(buffer) \
if (buffer->IsValidFor(this) == false) \
#define CHECK_INVALID_BUFFER(model, buffer) \
if (buffer->IsValidFor(model) == false) \
{ \
LOG(Warning, "Invalid Skinned Model Instance Buffer size {0} for Skinned Model {1}. It should be {2}. Manual update to proper size.", buffer->Count(), ToString(), MaterialSlots.Count()); \
buffer->Setup(this); \
LOG(Warning, "Invalid Skinned Model Instance Buffer size {0} for Skinned Model {1}. It should be {2}. Manual update to proper size.", buffer->Count(), model->ToString(), model->MaterialSlots.Count()); \
buffer->Setup(model); \
}
/// <summary>
@@ -172,24 +172,25 @@ BoundingBox SkinnedModel::GetBox(int32 lodIndex) const
return LODs[lodIndex].GetBox();
}
void SkinnedModel::Draw(RenderContext& renderContext, const SkinnedMesh::DrawInfo& info)
template<typename ContextType>
FORCE_INLINE void SkinnedModelDraw(SkinnedModel* model, const RenderContext& renderContext, const ContextType& context, const SkinnedMesh::DrawInfo& info)
{
ASSERT(info.Buffer);
if (!CanBeRendered())
if (!model->CanBeRendered())
return;
const auto frame = Engine::FrameCount;
const auto modelFrame = info.DrawState->PrevFrame + 1;
CHECK_INVALID_BUFFER(info.Buffer);
CHECK_INVALID_BUFFER(model, info.Buffer);
// Select a proper LOD index (model may be culled)
int32 lodIndex;
if (info.ForcedLOD != -1)
{
lodIndex = (int32)info.ForcedLOD;
lodIndex = info.ForcedLOD;
}
else
{
lodIndex = RenderTools::ComputeSkinnedModelLOD(this, info.Bounds.Center, (float)info.Bounds.Radius, renderContext);
lodIndex = RenderTools::ComputeSkinnedModelLOD(model, info.Bounds.Center, (float)info.Bounds.Radius, renderContext);
if (lodIndex == -1)
{
// Handling model fade-out transition
@@ -210,9 +211,9 @@ void SkinnedModel::Draw(RenderContext& renderContext, const SkinnedMesh::DrawInf
}
else
{
const auto prevLOD = ClampLODIndex(info.DrawState->PrevLOD);
const auto prevLOD = model->ClampLODIndex(info.DrawState->PrevLOD);
const float normalizedProgress = static_cast<float>(info.DrawState->LODTransition) * (1.0f / 255.0f);
LODs[prevLOD].Draw(renderContext, info, normalizedProgress);
model->LODs.Get()[prevLOD].Draw(renderContext, info, normalizedProgress);
}
}
@@ -220,7 +221,7 @@ void SkinnedModel::Draw(RenderContext& renderContext, const SkinnedMesh::DrawInf
}
}
lodIndex += info.LODBias + renderContext.View.ModelLODBias;
lodIndex = ClampLODIndex(lodIndex);
lodIndex = model->ClampLODIndex(lodIndex);
if (renderContext.View.IsSingleFrame)
{
@@ -253,22 +254,32 @@ void SkinnedModel::Draw(RenderContext& renderContext, const SkinnedMesh::DrawInf
// Draw
if (info.DrawState->PrevLOD == lodIndex || renderContext.View.IsSingleFrame)
{
LODs[lodIndex].Draw(renderContext, info, 0.0f);
model->LODs.Get()[lodIndex].Draw(context, info, 0.0f);
}
else if (info.DrawState->PrevLOD == -1)
{
const float normalizedProgress = static_cast<float>(info.DrawState->LODTransition) * (1.0f / 255.0f);
LODs[lodIndex].Draw(renderContext, info, 1.0f - normalizedProgress);
model->LODs.Get()[lodIndex].Draw(context, info, 1.0f - normalizedProgress);
}
else
{
const auto prevLOD = ClampLODIndex(info.DrawState->PrevLOD);
const auto prevLOD = model->ClampLODIndex(info.DrawState->PrevLOD);
const float normalizedProgress = static_cast<float>(info.DrawState->LODTransition) * (1.0f / 255.0f);
LODs[prevLOD].Draw(renderContext, info, normalizedProgress);
LODs[lodIndex].Draw(renderContext, info, normalizedProgress - 1.0f);
model->LODs.Get()[prevLOD].Draw(context, info, normalizedProgress);
model->LODs.Get()[lodIndex].Draw(context, info, normalizedProgress - 1.0f);
}
}
void SkinnedModel::Draw(const RenderContext& renderContext, const SkinnedMesh::DrawInfo& info)
{
SkinnedModelDraw(this, renderContext, renderContext, info);
}
void SkinnedModel::Draw(const RenderContextBatch& renderContextBatch, const SkinnedMesh::DrawInfo& info)
{
SkinnedModelDraw(this, renderContextBatch.GetMainContext(), renderContextBatch, info);
}
bool SkinnedModel::SetupLODs(const Span<int32>& meshesCountPerLod)
{
ScopeLock lock(Locker);

View File

@@ -210,7 +210,14 @@ public:
/// </summary>
/// <param name="renderContext">The rendering context.</param>
/// <param name="info">The packed drawing info data.</param>
void Draw(RenderContext& renderContext, const SkinnedMesh::DrawInfo& info);
void Draw(const RenderContext& renderContext, const SkinnedMesh::DrawInfo& info);
/// <summary>
/// Draws the model.
/// </summary>
/// <param name="renderContextBatch">The rendering context batch.</param>
/// <param name="info">The packed drawing info data.</param>
void Draw(const RenderContextBatch& renderContextBatch, const SkinnedMesh::DrawInfo& info);
public:
/// <summary>

View File

@@ -407,6 +407,9 @@ void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, cons
{
if (!material || !material->IsSurface() || !IsInitialized())
return;
drawModes &= material->GetDrawModes();
if (drawModes == DrawPass::None)
return;
// Submit draw call
DrawCall drawCall;
@@ -436,18 +439,70 @@ void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, cons
void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float lodDitherFactor) const
{
// Cache data
const auto& entry = info.Buffer->At(_materialSlotIndex);
if (!entry.Visible || !IsInitialized())
return;
const MaterialSlot& slot = _model->MaterialSlots[_materialSlotIndex];
// Select material
MaterialBase* material;
if (entry.Material && entry.Material->IsLoaded())
material = entry.Material;
else if (slot.Material && slot.Material->IsLoaded())
material = slot.Material;
else
material = GPUDevice::Instance->GetDefaultMaterial();
if (!material || !material->IsSurface())
return;
// Check if skip rendering
const auto shadowsMode = static_cast<ShadowsCastingMode>(entry.ShadowsMode & slot.ShadowsMode);
const auto drawModes = static_cast<DrawPass>(info.DrawModes & renderContext.View.GetShadowsDrawPassMask(shadowsMode));
const auto shadowsMode = (ShadowsCastingMode)(entry.ShadowsMode & slot.ShadowsMode);
const auto drawModes = (DrawPass)((uint32)info.DrawModes & (uint32)renderContext.View.Pass & (uint32)renderContext.View.GetShadowsDrawPassMask(shadowsMode) & (uint32)material->GetDrawModes());
if (drawModes == DrawPass::None)
return;
// Submit draw call
DrawCall drawCall;
drawCall.Geometry.IndexBuffer = _indexBuffer;
drawCall.Geometry.VertexBuffers[0] = _vertexBuffers[0];
drawCall.Geometry.VertexBuffers[1] = _vertexBuffers[1];
drawCall.Geometry.VertexBuffers[2] = _vertexBuffers[2];
drawCall.Geometry.VertexBuffersOffsets[0] = 0;
drawCall.Geometry.VertexBuffersOffsets[1] = 0;
drawCall.Geometry.VertexBuffersOffsets[2] = 0;
if (info.VertexColors && info.VertexColors[_lodIndex])
{
// TODO: cache vertexOffset within the model LOD per-mesh
uint32 vertexOffset = 0;
for (int32 meshIndex = 0; meshIndex < _index; meshIndex++)
vertexOffset += ((Model*)_model)->LODs[_lodIndex].Meshes[meshIndex].GetVertexCount();
drawCall.Geometry.VertexBuffers[2] = info.VertexColors[_lodIndex];
drawCall.Geometry.VertexBuffersOffsets[2] = vertexOffset * sizeof(VB2ElementType);
}
drawCall.Draw.StartIndex = 0;
drawCall.Draw.IndicesCount = _triangles * 3;
drawCall.InstanceCount = 1;
drawCall.Material = material;
drawCall.World = *info.World;
drawCall.ObjectPosition = drawCall.World.GetTranslation();
drawCall.Surface.GeometrySize = _box.GetSize();
drawCall.Surface.PrevWorld = info.DrawState->PrevWorld;
drawCall.Surface.Lightmap = info.Flags & StaticFlags::Lightmap ? info.Lightmap : nullptr;
drawCall.Surface.LightmapUVsArea = info.LightmapUVs ? *info.LightmapUVs : Rectangle::Empty;
drawCall.Surface.Skinning = nullptr;
drawCall.Surface.LODDitherFactor = lodDitherFactor;
drawCall.WorldDeterminantSign = Math::FloatSelect(drawCall.World.RotDeterminant(), 1, -1);
drawCall.PerInstanceRandom = info.PerInstanceRandom;
renderContext.List->AddDrawCall(drawModes, info.Flags, drawCall, entry.ReceiveDecals);
}
void Mesh::Draw(const RenderContextBatch& renderContextBatch, const DrawInfo& info, float lodDitherFactor) const
{
const auto& entry = info.Buffer->At(_materialSlotIndex);
if (!entry.Visible || !IsInitialized())
return;
const MaterialSlot& slot = _model->MaterialSlots[_materialSlotIndex];
// Select material
MaterialBase* material;
if (entry.Material && entry.Material->IsLoaded())
@@ -491,7 +546,18 @@ void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float
drawCall.Surface.LODDitherFactor = lodDitherFactor;
drawCall.WorldDeterminantSign = Math::FloatSelect(drawCall.World.RotDeterminant(), 1, -1);
drawCall.PerInstanceRandom = info.PerInstanceRandom;
renderContext.List->AddDrawCall(drawModes, info.Flags, drawCall, entry.ReceiveDecals);
// Push draw call to every context
const auto shadowsMode = (ShadowsCastingMode)(entry.ShadowsMode & slot.ShadowsMode);
const auto materialDrawModes = material->GetDrawModes();
for (const RenderContext& renderContext : renderContextBatch.Contexts)
{
const DrawPass drawModes = (DrawPass)((uint32)info.DrawModes & (uint32)renderContext.View.Pass & (uint32)renderContext.View.GetShadowsDrawPassMask(shadowsMode) & (uint32)materialDrawModes);
if (drawModes != DrawPass::None && renderContext.View.CullingFrustum.Intersects(info.Bounds))
{
renderContext.List->AddDrawCall(drawModes, info.Flags, drawCall, entry.ReceiveDecals);
}
}
}
bool Mesh::DownloadDataGPU(MeshBufferType type, BytesContainer& result) const

View File

@@ -367,6 +367,14 @@ public:
/// <param name="lodDitherFactor">The LOD transition dither factor.</param>
void Draw(const RenderContext& renderContext, const DrawInfo& info, float lodDitherFactor) const;
/// <summary>
/// Draws the mesh.
/// </summary>
/// <param name="renderContextBatch">The rendering context batch.</param>
/// <param name="info">The packed drawing info data.</param>
/// <param name="lodDitherFactor">The LOD transition dither factor.</param>
void Draw(const RenderContextBatch& renderContextBatch, const DrawInfo& info, float lodDitherFactor) const;
public:
// [MeshBase]
bool DownloadDataGPU(MeshBufferType type, BytesContainer& result) const override;

View File

@@ -10,6 +10,7 @@
class Task;
class ModelBase;
struct RenderContextBatch;
/// <summary>
/// Base class for model resources meshes.

View File

@@ -116,7 +116,7 @@ public:
{
for (int32 i = 0; i < Meshes.Count(); i++)
{
Meshes[i].Render(context);
Meshes.Get()[i].Render(context);
}
}
@@ -134,7 +134,7 @@ public:
{
for (int32 i = 0; i < Meshes.Count(); i++)
{
Meshes[i].Draw(renderContext, material, world, flags, receiveDecals, drawModes, perInstanceRandom);
Meshes.Get()[i].Draw(renderContext, material, world, flags, receiveDecals, drawModes, perInstanceRandom);
}
}
@@ -148,7 +148,21 @@ public:
{
for (int32 i = 0; i < Meshes.Count(); i++)
{
Meshes[i].Draw(renderContext, info, lodDitherFactor);
Meshes.Get()[i].Draw(renderContext, info, lodDitherFactor);
}
}
/// <summary>
/// Draws all the meshes from the model LOD.
/// </summary>
/// <param name="renderContextBatch">The rendering context batch.</param>
/// <param name="info">The packed drawing info data.</param>
/// <param name="lodDitherFactor">The LOD transition dither factor.</param>
FORCE_INLINE void Draw(const RenderContextBatch& renderContextBatch, const Mesh::DrawInfo& info, float lodDitherFactor) const
{
for (int32 i = 0; i < Meshes.Count(); i++)
{
Meshes.Get()[i].Draw(renderContextBatch, info, lodDitherFactor);
}
}
};

View File

@@ -152,18 +152,75 @@ void SkinnedMesh::Render(GPUContext* context) const
void SkinnedMesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float lodDitherFactor) const
{
// Cache data
const auto& entry = info.Buffer->At(_materialSlotIndex);
if (!entry.Visible || !IsInitialized())
return;
const MaterialSlot& slot = _model->MaterialSlots[_materialSlotIndex];
// Select material
MaterialBase* material;
if (entry.Material && entry.Material->IsLoaded())
material = entry.Material;
else if (slot.Material && slot.Material->IsLoaded())
material = slot.Material;
else
material = GPUDevice::Instance->GetDefaultMaterial();
if (!material || !material->IsSurface())
return;
// Check if skip rendering
const auto shadowsMode = static_cast<ShadowsCastingMode>(entry.ShadowsMode & slot.ShadowsMode);
const auto drawModes = static_cast<DrawPass>(info.DrawModes & renderContext.View.GetShadowsDrawPassMask(shadowsMode));
const auto shadowsMode = (ShadowsCastingMode)(entry.ShadowsMode & slot.ShadowsMode);
const auto drawModes = (DrawPass)((uint32)info.DrawModes & (uint32)renderContext.View.Pass & (uint32)renderContext.View.GetShadowsDrawPassMask(shadowsMode) & (uint32)material->GetDrawModes());
if (drawModes == DrawPass::None)
return;
// Submit draw call
DrawCall drawCall;
drawCall.Geometry.IndexBuffer = _indexBuffer;
BlendShapesInstance::MeshInstance* blendShapeMeshInstance;
if (info.BlendShapes && info.BlendShapes->Meshes.TryGet(this, blendShapeMeshInstance) && blendShapeMeshInstance->IsUsed)
{
// Use modified vertex buffer from the blend shapes
if (blendShapeMeshInstance->IsDirty)
{
blendShapeMeshInstance->VertexBuffer.Flush();
blendShapeMeshInstance->IsDirty = false;
}
drawCall.Geometry.VertexBuffers[0] = blendShapeMeshInstance->VertexBuffer.GetBuffer();
}
else
{
drawCall.Geometry.VertexBuffers[0] = _vertexBuffer;
}
drawCall.Geometry.VertexBuffers[1] = nullptr;
drawCall.Geometry.VertexBuffers[2] = nullptr;
drawCall.Geometry.VertexBuffersOffsets[0] = 0;
drawCall.Geometry.VertexBuffersOffsets[1] = 0;
drawCall.Geometry.VertexBuffersOffsets[2] = 0;
drawCall.Draw.StartIndex = 0;
drawCall.Draw.IndicesCount = _triangles * 3;
drawCall.InstanceCount = 1;
drawCall.Material = material;
drawCall.World = *info.World;
drawCall.ObjectPosition = drawCall.World.GetTranslation();
drawCall.Surface.GeometrySize = _box.GetSize();
drawCall.Surface.PrevWorld = info.DrawState->PrevWorld;
drawCall.Surface.Lightmap = nullptr;
drawCall.Surface.LightmapUVsArea = Rectangle::Empty;
drawCall.Surface.Skinning = info.Skinning;
drawCall.Surface.LODDitherFactor = lodDitherFactor;
drawCall.WorldDeterminantSign = Math::FloatSelect(drawCall.World.RotDeterminant(), 1, -1);
drawCall.PerInstanceRandom = info.PerInstanceRandom;
renderContext.List->AddDrawCall(drawModes, StaticFlags::None, drawCall, entry.ReceiveDecals);
}
void SkinnedMesh::Draw(const RenderContextBatch& renderContextBatch, const DrawInfo& info, float lodDitherFactor) const
{
const auto& entry = info.Buffer->At(_materialSlotIndex);
if (!entry.Visible || !IsInitialized())
return;
const MaterialSlot& slot = _model->MaterialSlots[_materialSlotIndex];
// Select material
MaterialBase* material;
if (entry.Material && entry.Material->IsLoaded())
@@ -212,7 +269,18 @@ void SkinnedMesh::Draw(const RenderContext& renderContext, const DrawInfo& info,
drawCall.Surface.LODDitherFactor = lodDitherFactor;
drawCall.WorldDeterminantSign = Math::FloatSelect(drawCall.World.RotDeterminant(), 1, -1);
drawCall.PerInstanceRandom = info.PerInstanceRandom;
renderContext.List->AddDrawCall(drawModes, StaticFlags::None, drawCall, entry.ReceiveDecals);
// Push draw call to every context
const auto shadowsMode = (ShadowsCastingMode)(entry.ShadowsMode & slot.ShadowsMode);
const auto materialDrawModes = material->GetDrawModes();
for (const RenderContext& renderContext : renderContextBatch.Contexts)
{
const DrawPass drawModes = (DrawPass)((uint32)info.DrawModes & (uint32)renderContext.View.Pass & (uint32)renderContext.View.GetShadowsDrawPassMask(shadowsMode) & (uint32)materialDrawModes);
if (drawModes != DrawPass::None && renderContext.View.CullingFrustum.Intersects(info.Bounds))
{
renderContext.List->AddDrawCall(drawModes, StaticFlags::None, drawCall, entry.ReceiveDecals);
}
}
}
bool SkinnedMesh::DownloadDataGPU(MeshBufferType type, BytesContainer& result) const

View File

@@ -240,6 +240,14 @@ public:
/// <param name="lodDitherFactor">The LOD transition dither factor.</param>
void Draw(const RenderContext& renderContext, const DrawInfo& info, float lodDitherFactor) const;
/// <summary>
/// Draws the mesh.
/// </summary>
/// <param name="renderContextBatch">The rendering context batch.</param>
/// <param name="info">The packed drawing info data.</param>
/// <param name="lodDitherFactor">The LOD transition dither factor.</param>
void Draw(const RenderContextBatch& renderContextBatch, const DrawInfo& info, float lodDitherFactor) const;
public:
// [MeshBase]
bool DownloadDataGPU(MeshBufferType type, BytesContainer& result) const override;

View File

@@ -102,7 +102,7 @@ public:
{
for (int32 i = 0; i < Meshes.Count(); i++)
{
Meshes[i].Render(context);
Meshes.Get()[i].Render(context);
}
}
@@ -116,7 +116,21 @@ public:
{
for (int32 i = 0; i < Meshes.Count(); i++)
{
Meshes[i].Draw(renderContext, info, lodDitherFactor);
Meshes.Get()[i].Draw(renderContext, info, lodDitherFactor);
}
}
/// <summary>
/// Draws all the meshes from the model LOD.
/// </summary>
/// <param name="renderContextBatch">The rendering context batch.</param>
/// <param name="info">The packed drawing info data.</param>
/// <param name="lodDitherFactor">The LOD transition dither factor.</param>
FORCE_INLINE void Draw(const RenderContextBatch& renderContextBatch, const SkinnedMesh::DrawInfo& info, float lodDitherFactor) const
{
for (int32 i = 0; i < Meshes.Count(); i++)
{
Meshes.Get()[i].Draw(renderContextBatch, info, lodDitherFactor);
}
}
};

View File

@@ -285,7 +285,7 @@ void AddActorToSceneRendering(SceneRendering* s, Actor* a)
}
}
void SceneRenderTask::OnCollectDrawCalls(RenderContext& renderContext, byte category)
void SceneRenderTask::OnCollectDrawCalls(RenderContextBatch& renderContextBatch, byte category)
{
// Draw actors (collect draw calls)
if ((ActorsSource & ActorsSources::CustomActors) != 0)
@@ -296,15 +296,16 @@ void SceneRenderTask::OnCollectDrawCalls(RenderContext& renderContext, byte cate
_customActorsScene->Clear();
for (Actor* a : CustomActors)
AddActorToSceneRendering(_customActorsScene, a);
_customActorsScene->Draw(renderContext, (SceneRendering::DrawCategory)category);
_customActorsScene->Draw(renderContextBatch, (SceneRendering::DrawCategory)category);
}
if ((ActorsSource & ActorsSources::Scenes) != 0)
{
Level::DrawActors(renderContext, category);
Level::DrawActors(renderContextBatch, category);
}
// External drawing event
CollectDrawCalls(renderContext);
for (RenderContext& renderContext : renderContextBatch.Contexts)
CollectDrawCalls(renderContext);
}
void SceneRenderTask::OnPreRender(GPUContext* context, RenderContext& renderContext)
@@ -313,14 +314,16 @@ void SceneRenderTask::OnPreRender(GPUContext* context, RenderContext& renderCont
// Collect initial draw calls
renderContext.View.Pass = DrawPass::GBuffer;
OnCollectDrawCalls(renderContext, SceneRendering::PreRender);
RenderContextBatch renderContextBatch(renderContext);
OnCollectDrawCalls(renderContextBatch, SceneRendering::PreRender);
}
void SceneRenderTask::OnPostRender(GPUContext* context, RenderContext& renderContext)
{
// Collect final draw calls
renderContext.View.Pass = DrawPass::GBuffer;
OnCollectDrawCalls(renderContext, SceneRendering::PostRender);
RenderContextBatch renderContextBatch(renderContext);
OnCollectDrawCalls(renderContextBatch, SceneRendering::PostRender);
PostRender(context, renderContext);
}
@@ -478,9 +481,22 @@ void MainRenderTask::OnBegin(GPUContext* context)
SceneRenderTask::OnBegin(context);
}
RenderContext::RenderContext(SceneRenderTask* task)
RenderContext::RenderContext(SceneRenderTask* task) noexcept
{
Buffers = task->Buffers;
Task = task;
View = task->View;
}
RenderContextBatch::RenderContextBatch(SceneRenderTask* task)
{
Buffers = task->Buffers;
Task = task;
}
RenderContextBatch::RenderContextBatch(const RenderContext& context)
{
Buffers = context.Buffers;
Task = context.Task;
Contexts.Add(context);
}

View File

@@ -310,9 +310,9 @@ public:
/// <summary>
/// Calls drawing scene objects.
/// </summary>
/// <param name="renderContext">The rendering context.</param>
/// <param name="renderContextBatch">The rendering context batch.</param>
/// <param name="category">The actors category to draw (see SceneRendering::DrawCategory).</param>
virtual void OnCollectDrawCalls(RenderContext& renderContext, byte category = 0);
virtual void OnCollectDrawCalls(RenderContextBatch& renderContextBatch, byte category = 0);
/// <summary>
/// The action called after scene rendering. Can be used to perform custom pre-rendering or to modify the render view.
@@ -424,9 +424,43 @@ API_STRUCT(NoDefault) struct RenderContext
/// </summary>
API_FIELD() RenderView View;
RenderContext()
RenderContext() = default;
RenderContext(SceneRenderTask* task) noexcept;
};
/// <summary>
/// The high-level renderer context batch that encapsulates multiple rendering requests within a single task (eg. optimize main view scene rendering and shadow projections at once).
/// </summary>
API_STRUCT(NoDefault) struct RenderContextBatch
{
DECLARE_SCRIPTING_TYPE_MINIMAL(RenderContextBatch);
/// <summary>
/// The render buffers.
/// </summary>
API_FIELD() RenderBuffers* Buffers = nullptr;
/// <summary>
/// The scene rendering task that is a source of renderable objects (optional).
/// </summary>
API_FIELD() SceneRenderTask* Task = nullptr;
/// <summary>
/// The all render views collection for the current rendering (main view, shadow projections, etc.).
/// </summary>
API_FIELD() Array<RenderContext> Contexts;
RenderContextBatch() = default;
RenderContextBatch(SceneRenderTask* task);
RenderContextBatch(const RenderContext& context);
FORCE_INLINE RenderContext& GetMainContext()
{
return Contexts.Get()[0];
}
RenderContext(SceneRenderTask* task);
FORCE_INLINE const RenderContext& GetMainContext() const
{
return Contexts.Get()[0];
}
};

View File

@@ -60,7 +60,7 @@ void RenderView::Prepare(RenderContext& renderContext)
PrepareCache(renderContext, width, height, taaJitter);
}
void RenderView::PrepareCache(RenderContext& renderContext, float width, float height, const Float2& temporalAAJitter, RenderView* mainView)
void RenderView::PrepareCache(const RenderContext& renderContext, float width, float height, const Float2& temporalAAJitter, const RenderView* mainView)
{
// The same format used by the Flax common shaders and postFx materials
ViewInfo = Float4(1.0f / Projection.M11, 1.0f / Projection.M22, Far / (Far - Near), (-Far * Near) / (Far - Near) / Far);

View File

@@ -12,7 +12,9 @@ namespace FlaxEngine
MaxShadowsQuality = Quality.Ultra;
ModelLODDistanceFactor = 1.0f;
ModelLODDistanceFactorSqrt = 1.0f;
#pragma warning disable 0612
ShadowModelLODDistanceFactor = 1.0f;
#pragma warning restore 0612
Flags = ViewFlags.DefaultGame;
Mode = ViewMode.Default;
}

View File

@@ -10,6 +10,7 @@
#include "Enums.h"
struct RenderContext;
struct RenderContextBatch;
struct Viewport;
class Camera;
class RenderList;
@@ -148,13 +149,15 @@ public:
/// <summary>
/// The model LOD bias. Default is 0. Applied to all the objects in the shadow maps render views. Can be used to improve shadows rendering performance or increase quality.
/// [Deprecated on 26.10.2022, expires on 26.10.2024]
/// </summary>
API_FIELD() int32 ShadowModelLODBias = 0;
API_FIELD() DEPRECATED int32 ShadowModelLODBias = 0;
/// <summary>
/// The model LOD distance scale factor. Default is 1. Applied to all the objects in the shadow maps render views. Higher values increase LODs quality. Can be used to improve shadows rendering performance or increase quality.
/// [Deprecated on 26.10.2022, expires on 26.10.2024]
/// </summary>
API_FIELD() float ShadowModelLODDistanceFactor = 1.0f;
API_FIELD() DEPRECATED float ShadowModelLODDistanceFactor = 1.0f;
/// <summary>
/// The Temporal Anti-Aliasing jitter frame index.
@@ -231,7 +234,7 @@ public:
/// <param name="height">The rendering height.</param>
/// <param name="temporalAAJitter">The temporal jitter for this frame.</param>
/// <param name="mainView">The main rendering viewport. Use null if it's top level view; pass pointer to main view for sub-passes like shadow depths.</param>
void PrepareCache(RenderContext& renderContext, float width, float height, const Float2& temporalAAJitter, RenderView* mainView = nullptr);
void PrepareCache(const RenderContext& renderContext, float width, float height, const Float2& temporalAAJitter, const RenderView* mainView = nullptr);
/// <summary>
/// Determines whether view is perspective projection or orthographic.

View File

@@ -1218,6 +1218,13 @@ void Actor::Draw(RenderContext& renderContext)
{
}
void Actor::Draw(RenderContextBatch& renderContextBatch)
{
// Default impl calls single-context
for (RenderContext& renderContext : renderContextBatch.Contexts)
Draw(renderContext);
}
#if USE_EDITOR
void Actor::OnDebugDraw()

View File

@@ -12,6 +12,7 @@
struct RenderView;
struct RenderContext;
struct RenderContextBatch;
class GPUContext;
class MemoryWriteStream;
class PhysicsScene;
@@ -675,6 +676,12 @@ public:
/// <param name="renderContext">The rendering context.</param>
virtual void Draw(RenderContext& renderContext);
/// <summary>
/// Draws this actor. Called by Scene Rendering service. This call is more optimized than generic Draw (eg. geometry is rendered during all pass types but other actors are drawn only during GBufferFill pass).
/// </summary>
/// <param name="renderContextBatch">The rendering context batch (eg, main view and shadow projections).</param>
virtual void Draw(RenderContextBatch& renderContextBatch);
#if USE_EDITOR
/// <summary>

View File

@@ -707,8 +707,7 @@ void AnimatedModel::Draw(RenderContext& renderContext)
renderContext.View.GetWorldMatrix(_transform, world);
GEOMETRY_DRAW_STATE_EVENT_BEGIN(_drawState, world);
const DrawPass drawModes = (DrawPass)(DrawModes & renderContext.View.Pass & (int32)renderContext.View.GetShadowsDrawPassMask(ShadowsMode));
if (SkinnedModel && SkinnedModel->IsLoaded() && drawModes != DrawPass::None)
if (SkinnedModel && SkinnedModel->IsLoaded())
{
_lastMinDstSqr = Math::Min(_lastMinDstSqr, Vector3::DistanceSquared(_transform.Translation, renderContext.View.Position + renderContext.View.Origin));
@@ -728,7 +727,9 @@ void AnimatedModel::Draw(RenderContext& renderContext)
draw.BlendShapes = &_blendShapes;
draw.World = &world;
draw.DrawState = &_drawState;
draw.DrawModes = drawModes;
PRAGMA_DISABLE_DEPRECATION_WARNINGS
draw.DrawModes = (DrawPass)(DrawModes & renderContext.View.GetShadowsDrawPassMask(ShadowsMode));
PRAGMA_ENABLE_DEPRECATION_WARNINGS
draw.Bounds = _sphere;
draw.Bounds.Center -= renderContext.View.Origin;
draw.PerInstanceRandom = GetPerInstanceRandom();
@@ -742,6 +743,58 @@ void AnimatedModel::Draw(RenderContext& renderContext)
GEOMETRY_DRAW_STATE_EVENT_END(_drawState, world);
}
void AnimatedModel::Draw(RenderContextBatch& renderContextBatch)
{
if (!SkinnedModel || !SkinnedModel->IsLoaded())
return;
const RenderContext& renderContext = renderContextBatch.GetMainContext();
Matrix world;
renderContext.View.GetWorldMatrix(_transform, world);
GEOMETRY_DRAW_STATE_EVENT_BEGIN(_drawState, world);
_lastMinDstSqr = Math::Min(_lastMinDstSqr, Vector3::DistanceSquared(_transform.Translation, renderContext.View.Position + renderContext.View.Origin));
if (_skinningData.IsReady())
{
#if USE_EDITOR
// Disable motion blur effects in editor without play mode enabled to hide minor artifacts on objects moving
if (!Editor::IsPlayMode)
_drawState.PrevWorld = world;
#endif
_skinningData.Flush(GPUDevice::Instance->GetMainContext());
SkinnedMesh::DrawInfo draw;
draw.Buffer = &Entries;
draw.Skinning = &_skinningData;
draw.BlendShapes = &_blendShapes;
draw.World = &world;
draw.DrawState = &_drawState;
draw.DrawModes = DrawModes;
draw.Bounds = _sphere;
draw.Bounds.Center -= renderContext.View.Origin;
draw.PerInstanceRandom = GetPerInstanceRandom();
draw.LODBias = LODBias;
draw.ForcedLOD = ForcedLOD;
PRAGMA_DISABLE_DEPRECATION_WARNINGS
if (ShadowsMode != ShadowsCastingMode::All)
{
// To handle old ShadowsMode option for all meshes we need to call per-context drawing (no batching opportunity)
// TODO: maybe deserialize ShadowsMode into ModelInstanceBuffer entries options?
for (auto& e : renderContextBatch.Contexts)
{
draw.DrawModes = (DrawPass)(DrawModes & e.View.GetShadowsDrawPassMask(ShadowsMode));
SkinnedModel->Draw(e, draw);
}
}
else
{
SkinnedModel->Draw(renderContextBatch, draw);
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
}
GEOMETRY_DRAW_STATE_EVENT_END(_drawState, world);
}
#if USE_EDITOR
#include "Engine/Debug/DebugDraw.h"
@@ -795,7 +848,9 @@ void AnimatedModel::Serialize(SerializeStream& stream, const void* otherObj)
SERIALIZE(LODBias);
SERIALIZE(ForcedLOD);
SERIALIZE(DrawModes);
PRAGMA_DISABLE_DEPRECATION_WARNINGS
SERIALIZE(ShadowsMode);
PRAGMA_ENABLE_DEPRECATION_WARNINGS
SERIALIZE(RootMotionTarget);
stream.JKEY("Buffer");
@@ -819,7 +874,9 @@ void AnimatedModel::Deserialize(DeserializeStream& stream, ISerializeModifier* m
DESERIALIZE(LODBias);
DESERIALIZE(ForcedLOD);
DESERIALIZE(DrawModes);
PRAGMA_DISABLE_DEPRECATION_WARNINGS
DESERIALIZE(ShadowsMode);
PRAGMA_ENABLE_DEPRECATION_WARNINGS
DESERIALIZE(RootMotionTarget);
Entries.DeserializeIfExists(stream, "Buffer", modifier);
@@ -889,7 +946,7 @@ void AnimatedModel::OnDeleteObject()
{
// Ensure this object is no longer referenced for anim update
Animations::RemoveFromUpdate(this);
ModelInstanceActor::OnDeleteObject();
}

View File

@@ -139,9 +139,10 @@ public:
/// <summary>
/// The shadows casting mode.
/// [Deprecated on 26.10.2022, expires on 26.10.2024]
/// </summary>
API_FIELD(Attributes="EditorOrder(110), DefaultValue(ShadowsCastingMode.All), EditorDisplay(\"Skinned Model\")")
ShadowsCastingMode ShadowsMode = ShadowsCastingMode::All;
DEPRECATED ShadowsCastingMode ShadowsMode = ShadowsCastingMode::All;
/// <summary>
/// The animation root motion apply target. If not specified the animated model will apply it itself.
@@ -358,6 +359,7 @@ public:
// [ModelInstanceActor]
bool HasContentLoaded() const override;
void Draw(RenderContext& renderContext) override;
void Draw(RenderContextBatch& renderContextBatch) override;
#if USE_EDITOR
void OnDebugDrawSelected() override;
BoundingBox GetEditorBox() const override;

View File

@@ -410,12 +410,6 @@ void SplineModel::Draw(RenderContext& renderContext)
continue;
const MaterialSlot& slot = model->MaterialSlots[mesh->GetMaterialSlotIndex()];
// Check if skip rendering
const auto shadowsMode = static_cast<ShadowsCastingMode>(entry.ShadowsMode & slot.ShadowsMode);
const auto drawModes = static_cast<DrawPass>(actorDrawModes & renderContext.View.GetShadowsDrawPassMask(shadowsMode));
if (drawModes == DrawPass::None)
continue;
// Select material
MaterialBase* material = nullptr;
if (entry.Material && entry.Material->IsLoaded())
@@ -427,6 +421,12 @@ void SplineModel::Draw(RenderContext& renderContext)
if (!material || !material->IsDeformable())
continue;
// Check if skip rendering
const auto shadowsMode = static_cast<ShadowsCastingMode>(entry.ShadowsMode & slot.ShadowsMode);
const auto drawModes = static_cast<DrawPass>(actorDrawModes & renderContext.View.GetShadowsDrawPassMask(shadowsMode) & (uint32)material->GetDrawModes());
if (drawModes == DrawPass::None)
continue;
// Submit draw call
mesh->GetDrawCallGeometry(drawCall);
drawCall.Material = material;

View File

@@ -222,6 +222,31 @@ void StaticModel::UpdateBounds()
GetSceneRendering()->UpdateActor(this, _sceneRenderingKey);
}
void StaticModel::FlushVertexColors()
{
for (int32 lodIndex = 0; lodIndex < _vertexColorsCount; lodIndex++)
{
auto& vertexColorsData = _vertexColorsData[lodIndex];
auto& vertexColorsBuffer = _vertexColorsBuffer[lodIndex];
if (vertexColorsData.HasItems())
{
const uint32 size = vertexColorsData.Count() * sizeof(Color32);
if (!vertexColorsBuffer)
vertexColorsBuffer = GPUDevice::Instance->CreateBuffer(TEXT("VertexColors"));
if (vertexColorsBuffer->GetSize() != size)
{
if (vertexColorsBuffer->Init(GPUBufferDescription::Vertex(sizeof(Color32), vertexColorsData.Count())))
return;
}
GPUDevice::Instance->GetMainContext()->UpdateBuffer(vertexColorsBuffer, vertexColorsData.Get(), size);
}
else
{
SAFE_DELETE_GPU_RESOURCE(vertexColorsBuffer);
}
}
}
bool StaticModel::HasContentLoaded() const
{
return (Model == nullptr || Model->IsLoaded()) && Entries.HasContentLoaded();
@@ -229,8 +254,7 @@ bool StaticModel::HasContentLoaded() const
void StaticModel::Draw(RenderContext& renderContext)
{
const DrawPass drawModes = (DrawPass)(DrawModes & renderContext.View.Pass);
if (!Model || !Model->IsLoaded() || !Model->CanBeRendered() || drawModes == DrawPass::None)
if (!Model || !Model->IsLoaded() || !Model->CanBeRendered())
return;
if (renderContext.View.Pass == DrawPass::GlobalSDF)
{
@@ -246,32 +270,8 @@ void StaticModel::Draw(RenderContext& renderContext)
renderContext.View.GetWorldMatrix(_transform, world);
GEOMETRY_DRAW_STATE_EVENT_BEGIN(_drawState, world);
// Flush vertex colors if need to
if (_vertexColorsDirty)
{
for (int32 lodIndex = 0; lodIndex < _vertexColorsCount; lodIndex++)
{
auto& vertexColorsData = _vertexColorsData[lodIndex];
auto& vertexColorsBuffer = _vertexColorsBuffer[lodIndex];
if (vertexColorsData.HasItems())
{
const uint32 size = vertexColorsData.Count() * sizeof(Color32);
if (!vertexColorsBuffer)
vertexColorsBuffer = GPUDevice::Instance->CreateBuffer(TEXT("VertexColors"));
if (vertexColorsBuffer->GetSize() != size)
{
if (vertexColorsBuffer->Init(GPUBufferDescription::Vertex(sizeof(Color32), vertexColorsData.Count())))
return;
}
GPUDevice::Instance->GetMainContext()->UpdateBuffer(vertexColorsBuffer, vertexColorsData.Get(), size);
}
else
{
SAFE_DELETE_GPU_RESOURCE(vertexColorsBuffer);
}
}
_vertexColorsDirty = false;
}
FlushVertexColors();
#if USE_EDITOR
// Disable motion blur effects in editor without play mode enabled to hide minor artifacts on objects moving
@@ -286,7 +286,7 @@ void StaticModel::Draw(RenderContext& renderContext)
draw.Lightmap = _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex);
draw.LightmapUVs = &Lightmap.UVsArea;
draw.Flags = _staticFlags;
draw.DrawModes = drawModes;
draw.DrawModes = DrawModes;
draw.Bounds = _sphere;
draw.Bounds.Center -= renderContext.View.Origin;
draw.PerInstanceRandom = GetPerInstanceRandom();
@@ -299,6 +299,44 @@ void StaticModel::Draw(RenderContext& renderContext)
GEOMETRY_DRAW_STATE_EVENT_END(_drawState, world);
}
void StaticModel::Draw(RenderContextBatch& renderContextBatch)
{
if (!Model || !Model->IsLoaded())
return;
const RenderContext& renderContext = renderContextBatch.GetMainContext();
Matrix world;
renderContext.View.GetWorldMatrix(_transform, world);
GEOMETRY_DRAW_STATE_EVENT_BEGIN(_drawState, world);
if (_vertexColorsDirty)
FlushVertexColors();
#if USE_EDITOR
// Disable motion blur effects in editor without play mode enabled to hide minor artifacts on objects moving
if (!Editor::IsPlayMode)
_drawState.PrevWorld = world;
#endif
Mesh::DrawInfo draw;
draw.Buffer = &Entries;
draw.World = &world;
draw.DrawState = &_drawState;
draw.Lightmap = _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex);
draw.LightmapUVs = &Lightmap.UVsArea;
draw.Flags = _staticFlags;
draw.DrawModes = DrawModes;
draw.Bounds = _sphere;
draw.Bounds.Center -= renderContext.View.Origin;
draw.PerInstanceRandom = GetPerInstanceRandom();
draw.LODBias = _lodBias;
draw.ForcedLOD = _forcedLod;
draw.VertexColors = _vertexColorsCount ? _vertexColorsBuffer : nullptr;
Model->Draw(renderContextBatch, draw);
GEOMETRY_DRAW_STATE_EVENT_END(_drawState, world);
}
bool StaticModel::IntersectsItself(const Ray& ray, Real& distance, Vector3& normal)
{
bool result = false;

View File

@@ -171,11 +171,13 @@ private:
void OnModelLoaded();
void OnModelResidencyChanged();
void UpdateBounds();
void FlushVertexColors();
public:
// [ModelInstanceActor]
bool HasContentLoaded() const override;
void Draw(RenderContext& renderContext) override;
void Draw(RenderContextBatch& renderContextBatch) override;
bool IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) override;
void Serialize(SerializeStream& stream, const void* otherObj) override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;

View File

@@ -389,7 +389,7 @@ void Level::CallBeginPlay(Actor* obj)
}
}
void Level::DrawActors(RenderContext& renderContext, byte category)
void Level::DrawActors(RenderContextBatch& renderContextBatch, byte category)
{
PROFILE_CPU();
@@ -398,7 +398,7 @@ void Level::DrawActors(RenderContext& renderContext, byte category)
for (Scene* scene : Scenes)
{
if (scene->IsActiveInHierarchy())
scene->Rendering.Draw(renderContext, (SceneRendering::DrawCategory)category);
scene->Rendering.Draw(renderContextBatch, (SceneRendering::DrawCategory)category);
}
}

View File

@@ -15,6 +15,7 @@ class JsonWriter;
class Engine;
struct RenderView;
struct RenderContext;
struct RenderContextBatch;
/// <summary>
/// The scene manager that contains the loaded scenes collection and spawns/deleted actors.
@@ -163,9 +164,9 @@ public:
/// <summary>
/// Draws all the actors.
/// </summary>
/// <param name="renderContext">The rendering context.</param>
/// <param name="renderContextBatch">The rendering context batch.</param>
/// <param name="category">The actors category to draw (see SceneRendering::DrawCategory).</param>
static void DrawActors(RenderContext& renderContext, byte category = 0);
static void DrawActors(RenderContextBatch& renderContextBatch, byte category = 0);
/// <summary>
/// Collects all the post fx volumes.

View File

@@ -11,6 +11,16 @@
#include "Engine/Profiler/ProfilerCPU.h"
#endif
FORCE_INLINE bool FrustumsListCull(const BoundingSphere& bounds, const BoundingFrustum* frustums, int32 frustumsCount)
{
for (int32 i = 0; i < frustumsCount; i++)
{
if (frustums[i].Intersects(bounds))
return true;
}
return false;
}
ISceneRenderingListener::~ISceneRenderingListener()
{
for (SceneRendering* scene : _scenes)
@@ -28,60 +38,90 @@ void ISceneRenderingListener::ListenSceneRendering(SceneRendering* scene)
}
}
void SceneRendering::Draw(RenderContext& renderContext, DrawCategory category)
void SceneRendering::Draw(RenderContextBatch& renderContextBatch, DrawCategory category)
{
ScopeLock lock(Locker);
auto& view = renderContext.View;
const BoundingFrustum frustum = view.CullingFrustum;
auto& view = renderContextBatch.GetMainContext().View;
const Vector3 origin = view.Origin;
renderContext.List->Scenes.Add(this);
for (auto& renderContext : renderContextBatch.Contexts)
renderContext.List->Scenes.Add(this);
auto& list = Actors[(int32)category];
// Setup frustum data
Array<BoundingFrustum, RenderListAllocation> frustumsData;
BoundingFrustum* frustums = &view.CullingFrustum;
int32 frustumsCount = renderContextBatch.Contexts.Count();
if (frustumsCount != 1)
{
frustumsData.Resize(frustumsCount);
frustums = frustumsData.Get();
for (int32 i = 0; i < frustumsCount; i++)
frustums[i] = renderContextBatch.Contexts[i].View.CullingFrustum;
}
#define CHECK_ACTOR ((view.RenderLayersMask.Mask & e.LayerMask) && (e.NoCulling || FrustumsListCull(e.Bounds, frustums, frustumsCount)))
#define CHECK_ACTOR_SINGLE_FRUSTUM ((view.RenderLayersMask.Mask & e.LayerMask) && (e.NoCulling || frustums->Intersects(e.Bounds)))
#if SCENE_RENDERING_USE_PROFILER
#define DRAW_ACTOR(mode) PROFILE_CPU_ACTOR(e.Actor); e.Actor->Draw(mode)
#else
#define DRAW_ACTOR(mode) e.Actor->Draw(mode)
#endif
// Draw all visual components
if (view.IsOfflinePass)
{
// Offline pass with additional static flags culling
for (int32 i = 0; i < list.Count(); i++)
{
auto e = list.Get()[i];
e.Bounds.Center -= origin;
if (view.RenderLayersMask.Mask & e.LayerMask && (e.NoCulling || frustum.Intersects(e.Bounds)) && e.Actor->GetStaticFlags() & view.StaticFlagsMask)
if (CHECK_ACTOR && e.Actor->GetStaticFlags() & view.StaticFlagsMask)
{
#if SCENE_RENDERING_USE_PROFILER
PROFILE_CPU_ACTOR(e.Actor);
#endif
e.Actor->Draw(renderContext);
DRAW_ACTOR(renderContextBatch);
}
}
}
else if (origin.IsZero() && frustumsCount == 1)
{
// Fast path for no origin shifting with a single context
auto& renderContext = renderContextBatch.Contexts[0];
for (int32 i = 0; i < list.Count(); i++)
{
auto e = list.Get()[i];
if (CHECK_ACTOR_SINGLE_FRUSTUM)
{
DRAW_ACTOR(renderContext);
}
}
}
else if (origin.IsZero())
{
// Fast path for no origin shifting
for (int32 i = 0; i < list.Count(); i++)
{
auto e = list.Get()[i];
if (view.RenderLayersMask.Mask & e.LayerMask && (e.NoCulling || frustum.Intersects(e.Bounds)))
if (CHECK_ACTOR)
{
#if SCENE_RENDERING_USE_PROFILER
PROFILE_CPU_ACTOR(e.Actor);
#endif
e.Actor->Draw(renderContext);
DRAW_ACTOR(renderContextBatch);
}
}
}
else
{
// Generic case
for (int32 i = 0; i < list.Count(); i++)
{
auto e = list.Get()[i];
e.Bounds.Center -= origin;
if (view.RenderLayersMask.Mask & e.LayerMask && (e.NoCulling || frustum.Intersects(e.Bounds)))
if (CHECK_ACTOR)
{
#if SCENE_RENDERING_USE_PROFILER
PROFILE_CPU_ACTOR(e.Actor);
#endif
e.Actor->Draw(renderContext);
DRAW_ACTOR(renderContextBatch);
}
}
}
#undef CHECK_ACTOR
#undef DRAW_ACTOR
#if USE_EDITOR
if (view.Pass & DrawPass::GBuffer && category == SceneDraw)
{

View File

@@ -12,6 +12,7 @@ class SceneRenderTask;
class SceneRendering;
struct PostProcessSettings;
struct RenderContext;
struct RenderContextBatch;
struct RenderView;
/// <summary>
@@ -103,9 +104,9 @@ public:
/// <summary>
/// Draws the scene. Performs the optimized actors culling and draw calls submission for the current render pass (defined by the render view).
/// </summary>
/// <param name="renderContext">The rendering context.</param>
/// <param name="renderContextBatch">The rendering context batch.</param>
/// <param name="category">The actors category to draw.</param>
void Draw(RenderContext& renderContext, DrawCategory category = DrawCategory::SceneDraw);
void Draw(RenderContextBatch& renderContextBatch, DrawCategory category = SceneDraw);
/// <summary>
/// Collects the post fx volumes for the given rendering view.

View File

@@ -408,12 +408,15 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
{
const auto material = (MaterialBase*)module->Assets[0].Get();
const auto moduleDrawModes = module->Values.Count() > 3 ? (DrawPass)module->Values[3].AsInt : DrawPass::Default;
auto dp = (DrawPass)(drawModes & moduleDrawModes & (uint32)material->GetDrawModes());
if (dp == DrawPass::None)
break;
drawCall.Material = material;
// Submit draw call
SpriteRenderer.SetupDrawCall(drawCall);
drawCall.InstanceCount = buffer->CPU.Count;
renderContext.List->AddDrawCall((DrawPass)(drawModes & moduleDrawModes), staticFlags, drawCall, false);
renderContext.List->AddDrawCall(dp, staticFlags, drawCall, false);
break;
}
@@ -423,6 +426,9 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
const auto model = (Model*)module->Assets[0].Get();
const auto material = (MaterialBase*)module->Assets[1].Get();
const auto moduleDrawModes = module->Values.Count() > 4 ? (DrawPass)module->Values[4].AsInt : DrawPass::Default;
auto dp = (DrawPass)(drawModes & moduleDrawModes & (uint32)material->GetDrawModes());
if (dp == DrawPass::None)
break;
drawCall.Material = material;
// TODO: model LOD picking for particles?
@@ -438,7 +444,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
// Submit draw call
mesh.GetDrawCallGeometry(drawCall);
drawCall.InstanceCount = buffer->CPU.Count;
renderContext.List->AddDrawCall((DrawPass)(drawModes & moduleDrawModes), staticFlags, drawCall, false);
renderContext.List->AddDrawCall(dp, staticFlags, drawCall, false);
}
break;
@@ -450,6 +456,9 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
break;
const auto material = (MaterialBase*)module->Assets[0].Get();
const auto moduleDrawModes = module->Values.Count() > 6 ? (DrawPass)module->Values[6].AsInt : DrawPass::Default;
auto dp = (DrawPass)(drawModes & moduleDrawModes & (uint32)material->GetDrawModes());
if (dp == DrawPass::None)
break;
drawCall.Material = material;
// Node properties
@@ -495,7 +504,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
drawCall.Draw.StartIndex = ribbonModulesDrawIndicesStart[ribbonModuleIndex];
drawCall.Draw.IndicesCount = ribbonModulesDrawIndicesCount[ribbonModuleIndex];
drawCall.InstanceCount = 1;
renderContext.List->AddDrawCall((DrawPass)(drawModes & moduleDrawModes), staticFlags, drawCall, false);
renderContext.List->AddDrawCall(dp, staticFlags, drawCall, false);
ribbonModuleIndex++;
@@ -810,6 +819,7 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
{
const auto material = (MaterialBase*)module->Assets[0].Get();
const auto moduleDrawModes = module->Values.Count() > 3 ? (DrawPass)module->Values[3].AsInt : DrawPass::Default;
auto dp = (DrawPass)(drawModes & moduleDrawModes & (uint32)material->GetDrawModes());
drawCall.Material = material;
// Submit draw call
@@ -817,7 +827,8 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
drawCall.InstanceCount = 0;
drawCall.Draw.IndirectArgsBuffer = buffer->GPU.IndirectDrawArgsBuffer;
drawCall.Draw.IndirectArgsOffset = indirectDrawCallIndex * sizeof(GPUDrawIndexedIndirectArgs);
renderContext.List->AddDrawCall((DrawPass)(drawModes & moduleDrawModes), staticFlags, drawCall, false);
if (dp != DrawPass::None)
renderContext.List->AddDrawCall(dp, staticFlags, drawCall, false);
indirectDrawCallIndex++;
break;
@@ -828,6 +839,7 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
const auto model = (Model*)module->Assets[0].Get();
const auto material = (MaterialBase*)module->Assets[1].Get();
const auto moduleDrawModes = module->Values.Count() > 4 ? (DrawPass)module->Values[4].AsInt : DrawPass::Default;
auto dp = (DrawPass)(drawModes & moduleDrawModes & (uint32)material->GetDrawModes());
drawCall.Material = material;
// TODO: model LOD picking for particles?
@@ -845,7 +857,8 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
drawCall.InstanceCount = 0;
drawCall.Draw.IndirectArgsBuffer = buffer->GPU.IndirectDrawArgsBuffer;
drawCall.Draw.IndirectArgsOffset = indirectDrawCallIndex * sizeof(GPUDrawIndexedIndirectArgs);
renderContext.List->AddDrawCall((DrawPass)(drawModes & moduleDrawModes), staticFlags, drawCall, false);
if (dp != DrawPass::None)
renderContext.List->AddDrawCall(dp, staticFlags, drawCall, false);
indirectDrawCallIndex++;
}

View File

@@ -63,12 +63,12 @@ void TAA::Dispose()
_shader = nullptr;
}
bool TAA::NeedMotionVectors(RenderContext& renderContext)
bool TAA::NeedMotionVectors(const RenderContext& renderContext)
{
return renderContext.List->Settings.AntiAliasing.Mode == AntialiasingMode::TemporalAntialiasing;
}
void TAA::Render(RenderContext& renderContext, GPUTexture* input, GPUTextureView* output)
void TAA::Render(const RenderContext& renderContext, GPUTexture* input, GPUTextureView* output)
{
auto context = GPUDevice::Instance->GetMainContext();

View File

@@ -22,7 +22,7 @@ public:
/// </summary>
/// <param name="renderContext">The rendering context.</param>
/// <returns>True if need to render motion vectors, otherwise false.</returns>
static bool NeedMotionVectors(RenderContext& renderContext);
static bool NeedMotionVectors(const RenderContext& renderContext);
/// <summary>
/// Performs AA pass rendering for the input task.
@@ -30,7 +30,7 @@ public:
/// <param name="renderContext">The rendering context.</param>
/// <param name="input">The input render target.</param>
/// <param name="output">The output render target.</param>
void Render(RenderContext& renderContext, GPUTexture* input, GPUTextureView* output);
void Render(const RenderContext& renderContext, GPUTexture* input, GPUTextureView* output);
private:

View File

@@ -518,7 +518,6 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
renderContextTiles.View.Pass = DrawPass::GBuffer | DrawPass::GlobalSurfaceAtlas;
renderContextTiles.View.Mode = ViewMode::Default;
renderContextTiles.View.ModelLODBias += 100000;
renderContextTiles.View.ShadowModelLODBias += 100000;
renderContextTiles.View.IsSingleFrame = true;
renderContextTiles.View.IsCullingDisabled = true;
renderContextTiles.View.Near = 0.0f;

View File

@@ -150,7 +150,7 @@ void LightPass::Dispose()
_sphereModel = nullptr;
}
void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightBuffer)
void LightPass::RenderLight(RenderContextBatch& renderContextBatch, GPUTextureView* lightBuffer)
{
const float sphereModelScale = 3.0f;
@@ -166,6 +166,7 @@ void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightB
// Cache data
auto device = GPUDevice::Instance;
auto context = device->GetMainContext();
auto& renderContext = renderContextBatch.Contexts[0];
auto& view = renderContext.View;
auto mainCache = renderContext.List;
const auto lightShader = _shader->GetShader();
@@ -219,12 +220,6 @@ void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightB
auto cb1 = lightShader->GetCB(1);
context->UpdateCB(cb1, &perFrame);
// Prepare shadows rendering (is will be used)
if (useShadows)
{
ShadowsPass::Instance()->Prepare(renderContext, context);
}
// Bind inputs
context->BindSR(0, renderContext.Buffers->GBuffer0);
context->BindSR(1, renderContext.Buffers->GBuffer1);
@@ -250,7 +245,7 @@ void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightB
auto& light = mainCache->PointLights[lightIndex];
float lightRadius = light.Radius;
Float3 lightPosition = light.Position;
const bool renderShadow = useShadows && CanRenderShadow(view, light) && ShadowsPass::Instance()->CanRenderShadow(renderContext, light);
const bool renderShadow = useShadows && light.ShadowDataIndex != -1;
bool useIES = light.IESTexture != nullptr;
// Get distance from view center to light center less radius (check if view is inside a sphere)
@@ -268,7 +263,7 @@ void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightB
if (renderShadow)
{
GET_SHADOW_MASK();
ShadowsPass::Instance()->RenderShadow(renderContext, light, shadowMaskView);
ShadowsPass::Instance()->RenderShadow(renderContextBatch, light, shadowMaskView);
// Bind output
context->SetRenderTarget(depthBufferRTV, lightBuffer);
@@ -307,7 +302,7 @@ void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightB
auto& light = mainCache->SpotLights[lightIndex];
float lightRadius = light.Radius;
Float3 lightPosition = light.Position;
const bool renderShadow = useShadows && CanRenderShadow(view, light) && ShadowsPass::Instance()->CanRenderShadow(renderContext, light);
const bool renderShadow = useShadows && light.ShadowDataIndex != -1;
bool useIES = light.IESTexture != nullptr;
// Get distance from view center to light center less radius (check if view is inside a sphere)
@@ -325,7 +320,7 @@ void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightB
if (renderShadow)
{
GET_SHADOW_MASK();
ShadowsPass::Instance()->RenderShadow(renderContext, light, shadowMaskView);
ShadowsPass::Instance()->RenderShadow(renderContextBatch, light, shadowMaskView);
// Bind output
context->SetRenderTarget(depthBufferRTV, lightBuffer);
@@ -362,13 +357,13 @@ void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightB
// Cache data
auto& light = mainCache->DirectionalLights[lightIndex];
const bool renderShadow = useShadows && CanRenderShadow(view, light) && ShadowsPass::Instance()->CanRenderShadow(renderContext, light);
const bool renderShadow = useShadows && light.ShadowDataIndex != -1;
// Check if render shadow
if (renderShadow)
{
GET_SHADOW_MASK();
ShadowsPass::Instance()->RenderShadow(renderContext, light, lightIndex, shadowMaskView);
ShadowsPass::Instance()->RenderShadow(renderContextBatch, light, lightIndex, shadowMaskView);
// Bind output
context->SetRenderTarget(depthBufferRTV, lightBuffer);

View File

@@ -27,13 +27,12 @@ private:
PixelFormat _shadowMaskFormat;
public:
/// <summary>
/// Performs the lighting rendering for the input task.
/// </summary>
/// <param name="renderContext">The rendering context.</param>
/// <param name="renderContextBatch">The rendering context batch.</param>
/// <param name="lightBuffer">The light accumulation buffer (input and output).</param>
void RenderLight(RenderContext& renderContext, GPUTextureView* lightBuffer);
void RenderLight(RenderContextBatch& renderContextBatch, GPUTextureView* lightBuffer);
private:

View File

@@ -416,36 +416,37 @@ void RenderList::Clear()
void RenderList::AddDrawCall(DrawPass drawModes, StaticFlags staticFlags, DrawCall& drawCall, bool receivesDecals)
{
// Mix object mask with material mask
const auto mask = (DrawPass)(drawModes & drawCall.Material->GetDrawModes());
if (mask == DrawPass::None)
return;
#if ENABLE_ASSERTION_LOW_LAYERS
// Ensure that draw modes are non-empty and in conjunction with material draw modes
auto materialDrawModes = drawCall.Material->GetDrawModes();
ASSERT_LOW_LAYER(drawModes != DrawPass::None && ((uint32)drawModes & ~(uint32)materialDrawModes) == 0);
#endif
// Append draw call data
const int32 index = DrawCalls.Count();
DrawCalls.Add(drawCall);
// Add draw call to proper draw lists
if (mask & DrawPass::Depth)
if (drawModes & DrawPass::Depth)
{
DrawCallsLists[(int32)DrawCallsListType::Depth].Indices.Add(index);
}
if (mask & (DrawPass::GBuffer | DrawPass::GlobalSurfaceAtlas))
if (drawModes & (DrawPass::GBuffer | DrawPass::GlobalSurfaceAtlas))
{
if (receivesDecals)
DrawCallsLists[(int32)DrawCallsListType::GBuffer].Indices.Add(index);
else
DrawCallsLists[(int32)DrawCallsListType::GBufferNoDecals].Indices.Add(index);
}
if (mask & DrawPass::Forward)
if (drawModes & DrawPass::Forward)
{
DrawCallsLists[(int32)DrawCallsListType::Forward].Indices.Add(index);
}
if (mask & DrawPass::Distortion)
if (drawModes & DrawPass::Distortion)
{
DrawCallsLists[(int32)DrawCallsListType::Distortion].Indices.Add(index);
}
if (mask & DrawPass::MotionVectors && (staticFlags & StaticFlags::Transform) == 0)
if (drawModes & DrawPass::MotionVectors && (staticFlags & StaticFlags::Transform) == 0)
{
DrawCallsLists[(int32)DrawCallsListType::MotionVectors].Indices.Add(index);
}
@@ -477,7 +478,7 @@ void RenderList::SortDrawCalls(const RenderContext& renderContext, bool reverseD
PROFILE_CPU();
const int32 listSize = (int32)list.Indices.Count();
const Float3 planeNormal = renderContext.View.Direction;
const Float3 planeNormal = renderContext.View.Direction;
const float planePoint = -Float3::Dot(planeNormal, renderContext.View.Position);
// Peek shared memory

View File

@@ -36,6 +36,7 @@ struct RendererDirectionalLightData
StaticFlags StaticFlags;
float IndirectLightingIntensity;
int16 ShadowDataIndex = -1;
int8 CastVolumetricShadow : 1;
int8 RenderedVolumetricFog : 1;
@@ -80,6 +81,7 @@ struct RendererSpotLightData
ShadowsCastingMode ShadowsMode;
StaticFlags StaticFlags;
int16 ShadowDataIndex = -1;
int8 CastVolumetricShadow : 1;
int8 RenderedVolumetricFog : 1;
int8 UseInverseSquaredFalloff : 1;
@@ -117,6 +119,7 @@ struct RendererPointLightData
ShadowsCastingMode ShadowsMode;
StaticFlags StaticFlags;
int16 ShadowDataIndex = -1;
int8 CastVolumetricShadow : 1;
int8 RenderedVolumetricFog : 1;
int8 UseInverseSquaredFalloff : 1;
@@ -508,7 +511,7 @@ public:
/// <param name="renderContext">The rendering context.</param>
/// <param name="postProcess">The PostFx location to check (for scripts).</param>
/// <returns>True if render any postFx of the given type, otherwise false.</returns>
bool HasAnyPostFx(RenderContext& renderContext, PostProcessEffectLocation postProcess) const;
bool HasAnyPostFx(const RenderContext& renderContext, PostProcessEffectLocation postProcess) const;
/// <summary>
/// Determines whether any Material PostFx specified by given type. Used to pick a faster rendering path by the frame rendering module.
@@ -516,7 +519,7 @@ public:
/// <param name="renderContext">The rendering context.</param>
/// <param name="materialPostFx">The PostFx location to check (for materials).</param>
/// <returns>True if render any postFx of the given type, otherwise false.</returns>
bool HasAnyPostFx(RenderContext& renderContext, MaterialPostFxLocation materialPostFx) const;
bool HasAnyPostFx(const RenderContext& renderContext, MaterialPostFxLocation materialPostFx) const;
/// <summary>
/// Determines whether any Custom PostFx or Material PostFx specified by given type. Used to pick a faster rendering path by the frame rendering module.
@@ -525,7 +528,7 @@ public:
/// <param name="postProcess">The PostFx location to check (for scripts).</param>
/// <param name="materialPostFx">The PostFx location to check (for materials).</param>
/// <returns>True if render any postFx of the given type, otherwise false.</returns>
bool HasAnyPostFx(RenderContext& renderContext, PostProcessEffectLocation postProcess, MaterialPostFxLocation materialPostFx) const
bool HasAnyPostFx(const RenderContext& renderContext, PostProcessEffectLocation postProcess, MaterialPostFxLocation materialPostFx) const
{
return HasAnyPostFx(renderContext, postProcess) || HasAnyPostFx(renderContext, materialPostFx);
}

View File

@@ -60,7 +60,7 @@ public:
RendererService RendererServiceInstance;
void RenderInner(SceneRenderTask* task, RenderContext& renderContext);
void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderContextBatch& renderContextBatch);
bool RendererService::Init()
{
@@ -165,15 +165,18 @@ void Renderer::Render(SceneRenderTask* task)
{
PROFILE_GPU_CPU_NAMED("Render Frame");
// Prepare GPU context
auto context = GPUDevice::Instance->GetMainContext();
context->ClearState();
context->FlushState();
const Viewport viewport = task->GetViewport();
context->SetViewportAndScissors(viewport);
// Prepare
// Prepare render context
RenderContext renderContext(task);
renderContext.List = RenderList::GetFromPool();
RenderContextBatch renderContextBatch(task);
renderContextBatch.Contexts.Add(renderContext);
#if USE_EDITOR
// Turn on low quality rendering during baking lightmaps (leave more GPU power for baking)
@@ -204,7 +207,7 @@ void Renderer::Render(SceneRenderTask* task)
// Perform the actual rendering
task->OnPreRender(context, renderContext);
RenderInner(task, renderContext);
RenderInner(task, renderContext, renderContextBatch);
task->OnPostRender(context, renderContext);
#if USE_EDITOR
@@ -216,10 +219,11 @@ void Renderer::Render(SceneRenderTask* task)
task->View = renderContext.View;
// Cleanup
RenderList::ReturnToPool(renderContext.List);
for (const auto& e : renderContextBatch.Contexts)
RenderList::ReturnToPool(e.List);
}
bool Renderer::NeedMotionVectors(RenderContext& renderContext)
bool Renderer::NeedMotionVectors(const RenderContext& renderContext)
{
const int32 screenWidth = renderContext.Buffers->GetWidth();
const int32 screenHeight = renderContext.Buffers->GetHeight();
@@ -255,7 +259,8 @@ void Renderer::DrawSceneDepth(GPUContext* context, SceneRenderTask* task, GPUTex
else
{
// Draw scene actors
Level::DrawActors(renderContext);
RenderContextBatch renderContextBatch(renderContext);
Level::DrawActors(renderContextBatch);
}
// Sort draw calls
@@ -290,7 +295,7 @@ void Renderer::DrawPostFxMaterial(GPUContext* context, const RenderContext& rend
context->ResetRenderTarget();
}
void RenderInner(SceneRenderTask* task, RenderContext& renderContext)
void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderContextBatch& renderContextBatch)
{
auto context = GPUDevice::Instance->GetMainContext();
auto* graphicsSettings = GraphicsSettings::Get();
@@ -316,17 +321,47 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext)
renderContext.List->PostFx.Add(&postFx);
}
// Collect renderable objects and construct draw call list
view.Pass = DrawPass::GBuffer | DrawPass::Forward | DrawPass::Distortion;
if (Renderer::NeedMotionVectors(renderContext))
view.Pass |= DrawPass::MotionVectors;
task->OnCollectDrawCalls(renderContext);
// Build batch of render contexts (main view and shadow projections)
const bool isGBufferDebug = GBufferPass::IsDebugView(renderContext.View.Mode);
{
PROFILE_CPU_NAMED("Collect Draw Calls");
view.Pass = DrawPass::GBuffer | DrawPass::Forward | DrawPass::Distortion;
if (Renderer::NeedMotionVectors(renderContext))
view.Pass |= DrawPass::MotionVectors;
renderContextBatch.GetMainContext() = renderContext; // Sync render context in batch with the current value
bool drawShadows = !isGBufferDebug && ((view.Flags & ViewFlags::Shadows) != 0) && ShadowsPass::Instance()->IsReady();
switch (renderContext.View.Mode)
{
case ViewMode::QuadOverdraw:
case ViewMode::Emissive:
case ViewMode::LightmapUVsDensity:
case ViewMode::GlobalSurfaceAtlas:
case ViewMode::GlobalSDF:
case ViewMode::MaterialComplexity:
drawShadows = false;
break;
}
if (drawShadows)
ShadowsPass::Instance()->SetupShadows(renderContext, renderContextBatch);
task->OnCollectDrawCalls(renderContextBatch);
}
// Sort draw calls
renderContext.List->SortDrawCalls(renderContext, false, DrawCallsListType::GBuffer);
renderContext.List->SortDrawCalls(renderContext, false, DrawCallsListType::GBufferNoDecals);
renderContext.List->SortDrawCalls(renderContext, true, DrawCallsListType::Forward);
renderContext.List->SortDrawCalls(renderContext, false, DrawCallsListType::Distortion);
{
PROFILE_CPU_NAMED("Sort Draw Calls");
renderContext.List->SortDrawCalls(renderContext, false, DrawCallsListType::GBuffer);
renderContext.List->SortDrawCalls(renderContext, false, DrawCallsListType::GBufferNoDecals);
renderContext.List->SortDrawCalls(renderContext, true, DrawCallsListType::Forward);
renderContext.List->SortDrawCalls(renderContext, false, DrawCallsListType::Distortion);
for (int32 i = 1; i < renderContextBatch.Contexts.Count(); i++)
{
auto& shadowContext = renderContextBatch.Contexts[i];
shadowContext.List->SortDrawCalls(shadowContext, false, DrawCallsListType::Depth);
}
}
// Get the light accumulation buffer
auto outputFormat = renderContext.Buffers->GetOutputFormat();
@@ -389,7 +424,7 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext)
AmbientOcclusionPass::Instance()->Render(renderContext);
// Check if use custom view mode
if (GBufferPass::IsDebugView(renderContext.View.Mode))
if (isGBufferDebug)
{
context->ResetRenderTarget();
context->SetRenderTarget(task->GetOutputView());
@@ -400,7 +435,8 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext)
}
// Render lighting
LightPass::Instance()->RenderLight(renderContext, *lightBuffer);
renderContextBatch.GetMainContext() = renderContext; // Sync render context in batch with the current value
LightPass::Instance()->RenderLight(renderContextBatch, *lightBuffer);
if (renderContext.View.Flags & ViewFlags::GI)
{
switch (renderContext.List->Settings.GlobalIllumination.Mode)

View File

@@ -38,7 +38,7 @@ public:
/// </summary>
/// <param name="renderContext">The rendering context.</param>
/// <returns>True if need to render motion vectors, otherwise false.</returns>
static bool NeedMotionVectors(RenderContext& renderContext);
static bool NeedMotionVectors(const RenderContext& renderContext);
public:

View File

@@ -54,7 +54,7 @@ PACK_STRUCT(struct Data
GlobalSurfaceAtlasPass::ConstantsData GlobalSurfaceAtlas;
});
bool ScreenSpaceReflectionsPass::NeedMotionVectors(RenderContext& renderContext)
bool ScreenSpaceReflectionsPass::NeedMotionVectors(const RenderContext& renderContext)
{
auto& settings = renderContext.List->Settings.ScreenSpaceReflections;
return settings.TemporalEffect && renderContext.View.Flags & ViewFlags::SSR;

View File

@@ -41,7 +41,7 @@ public:
/// </summary>
/// <param name="renderContext">The rendering context.</param>
/// <returns>True if need to render motion vectors, otherwise false.</returns>
static bool NeedMotionVectors(RenderContext& renderContext);
static bool NeedMotionVectors(const RenderContext& renderContext);
/// <summary>
/// Perform SSR rendering for the input task (blends reflections to given texture using alpha blending).

View File

@@ -189,298 +189,37 @@ void ShadowsPass::updateShadowMapSize()
}
}
void ShadowsPass::Dispose()
void ShadowsPass::SetupRenderContext(RenderContext& renderContext, RenderContext& shadowContext)
{
// Base
RendererPass::Dispose();
// Cleanup
_psShadowDir.Delete();
_psShadowPoint.Delete();
_psShadowSpot.Delete();
_shader = nullptr;
_sphereModel = nullptr;
SAFE_DELETE_GPU_RESOURCE(_shadowMapCSM);
SAFE_DELETE_GPU_RESOURCE(_shadowMapCube);
}
bool ShadowsPass::CanRenderShadow(RenderContext& renderContext, const RendererPointLightData& light)
{
const Float3 lightPosition = light.Position;
const float dstLightToView = Float3::Distance(lightPosition, renderContext.View.Position);
// Fade shadow on distance
const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance);
return fade > ZeroTolerance && _supportsShadows;
}
bool ShadowsPass::CanRenderShadow(RenderContext& renderContext, const RendererSpotLightData& light)
{
const Float3 lightPosition = light.Position;
const float dstLightToView = Float3::Distance(lightPosition, renderContext.View.Position);
// Fade shadow on distance
const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance);
return fade > ZeroTolerance && _supportsShadows;
}
bool ShadowsPass::CanRenderShadow(RenderContext& renderContext, const RendererDirectionalLightData& light)
{
return _supportsShadows;
}
void ShadowsPass::Prepare(RenderContext& renderContext, GPUContext* context)
{
ASSERT(IsReady());
auto& view = renderContext.View;
const auto shader = _shader->GetShader();
const auto shadowMapsQuality = Graphics::ShadowMapsQuality;
if (shadowMapsQuality != _currentShadowMapsQuality)
updateShadowMapSize();
auto shadowsQuality = Graphics::ShadowsQuality;
maxShadowsQuality = Math::Clamp(Math::Min<int32>(static_cast<int32>(shadowsQuality), static_cast<int32>(view.MaxShadowsQuality)), 0, static_cast<int32>(Quality::MAX) - 1);
const auto& view = renderContext.View;
// Use the current render view to sync model LODs with the shadow maps rendering stage
_shadowContext.LodProxyView = &renderContext.View;
shadowContext.LodProxyView = &renderContext.View;
// Prepare properties
auto& shadowView = _shadowContext.View;
auto& shadowView = shadowContext.View;
shadowView.Flags = view.Flags;
shadowView.StaticFlagsMask = view.StaticFlagsMask;
shadowView.RenderLayersMask = view.RenderLayersMask;
shadowView.IsOfflinePass = view.IsOfflinePass;
shadowView.ModelLODBias = view.ModelLODBias + view.ShadowModelLODBias;
shadowView.ModelLODDistanceFactor = view.ModelLODDistanceFactor * view.ShadowModelLODDistanceFactor;
shadowView.ModelLODBias = view.ModelLODBias;
shadowView.ModelLODDistanceFactor = view.ModelLODDistanceFactor;
shadowView.Pass = DrawPass::Depth;
shadowView.Origin = view.Origin;
_shadowContext.List = &_shadowCache;
shadowContext.List = RenderList::GetFromPool();
shadowContext.Buffers = renderContext.Buffers;
shadowContext.Task = renderContext.Task;
}
void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererPointLightData& light, GPUTextureView* shadowMask)
void ShadowsPass::SetupLight(RenderContext& renderContext, RenderContextBatch& renderContextBatch, RendererDirectionalLightData& light)
{
const float sphereModelScale = 3.0f;
PROFILE_GPU_CPU("Shadow");
// Cache data
auto device = GPUDevice::Instance;
auto context = device->GetMainContext();
auto& view = renderContext.View;
auto shader = _shader->GetShader();
Data sperLight;
float lightRadius = light.Radius;
Float3 lightPosition = light.Position;
Float3 lightDirection = light.Direction;
float dstLightToView = Float3::Distance(lightPosition, view.Position);
// TODO: here we can use lower shadows quality based on light distance to view (LOD switching) and per light setting for max quality
int32 shadowQuality = maxShadowsQuality;
// Fade shadow on distance
const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance);
// Set up GPU context and render view
const auto shadowMapsSizeCube = (float)_shadowMapsSizeCube;
context->SetViewportAndScissors(shadowMapsSizeCube, shadowMapsSizeCube);
_shadowContext.View.SetUpCube(PointLight_NearPlane, lightRadius, lightPosition);
_shadowContext.View.PrepareCache(_shadowContext, shadowMapsSizeCube, shadowMapsSizeCube, Float2::Zero, &view);
// Render depth to all 6 faces of the cube map
for (int32 faceIndex = 0; faceIndex < 6; faceIndex++)
{
// Set up view
_shadowCache.Clear();
_shadowContext.View.SetFace(faceIndex);
Matrix::Transpose(_shadowContext.View.ViewProjection(), sperLight.LightShadow.ShadowVP[faceIndex]);
// Set render target
auto rt = _shadowMapCube->View(faceIndex);
context->ResetSR();
context->SetRenderTarget(rt, static_cast<GPUTextureView*>(nullptr));
context->ClearDepth(rt);
// Render actors to the shadow map
renderContext.Task->OnCollectDrawCalls(_shadowContext);
_shadowCache.SortDrawCalls(_shadowContext, false, DrawCallsListType::Depth);
_shadowCache.ExecuteDrawCalls(_shadowContext, DrawCallsListType::Depth);
}
// Restore GPU context
context->ResetSR();
context->ResetRenderTarget();
const Viewport viewport = renderContext.Task->GetViewport();
GPUTexture* depthBuffer = renderContext.Buffers->DepthBuffer;
GPUTextureView* depthBufferSRV = depthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView ? depthBuffer->ViewReadOnlyDepth() : depthBuffer->View();
context->SetViewportAndScissors(viewport);
context->BindSR(0, renderContext.Buffers->GBuffer0);
context->BindSR(1, renderContext.Buffers->GBuffer1);
context->BindSR(2, renderContext.Buffers->GBuffer2);
context->BindSR(3, depthBufferSRV);
context->BindSR(4, renderContext.Buffers->GBuffer3);
// Setup shader data
GBufferPass::SetInputs(view, sperLight.GBuffer);
light.SetupLightData(&sperLight.Light, true);
sperLight.LightShadow.ShadowMapSize = shadowMapsSizeCube;
sperLight.LightShadow.Sharpness = light.ShadowsSharpness;
sperLight.LightShadow.Fade = Math::Saturate(light.ShadowsStrength * fade);
sperLight.LightShadow.NormalOffsetScale = light.ShadowsNormalOffsetScale * NormalOffsetScaleTweak * (1.0f / shadowMapsSizeCube);
sperLight.LightShadow.Bias = light.ShadowsDepthBias;
sperLight.LightShadow.FadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
sperLight.LightShadow.NumCascades = 1;
sperLight.LightShadow.CascadeSplits = Float4::Zero;
Matrix::Transpose(view.ViewProjection(), sperLight.ViewProjectionMatrix);
sperLight.ContactShadowsDistance = light.ShadowsDistance;
sperLight.ContactShadowsLength = view.Flags & ViewFlags::ContactShadows ? light.ContactShadowsLength : 0.0f;
// Calculate world view projection matrix for the light sphere
Matrix world, wvp, matrix;
Matrix::Scaling(lightRadius * sphereModelScale, wvp);
Matrix::Translation(lightPosition, matrix);
Matrix::Multiply(wvp, matrix, world);
Matrix::Multiply(world, view.ViewProjection(), wvp);
Matrix::Transpose(wvp, sperLight.WVP);
// Render shadow in screen space
context->UpdateCB(shader->GetCB(0), &sperLight);
context->BindCB(0, shader->GetCB(0));
context->BindCB(1, shader->GetCB(1));
context->BindSR(5, _shadowMapCube->ViewArray());
context->SetRenderTarget(shadowMask);
context->SetState(_psShadowPoint.Get(shadowQuality + (sperLight.ContactShadowsLength > ZeroTolerance ? 4 : 0)));
_sphereModel->Render(context);
// Cleanup
context->ResetRenderTarget();
context->UnBindSR(5);
// Render volumetric light with shadow
VolumetricFogPass::Instance()->RenderLight(renderContext, context, light, _shadowMapCube->ViewArray(), sperLight.LightShadow);
}
void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererSpotLightData& light, GPUTextureView* shadowMask)
{
const float sphereModelScale = 3.0f;
PROFILE_GPU_CPU("Shadow");
// Cache data
auto device = GPUDevice::Instance;
auto context = device->GetMainContext();
auto& view = renderContext.View;
auto shader = _shader->GetShader();
Data sperLight;
float lightRadius = light.Radius;
Float3 lightPosition = light.Position;
Float3 lightDirection = light.Direction;
float dstLightToView = Float3::Distance(lightPosition, view.Position);
// TODO: here we can use lower shadows quality based on light distance to view (LOD switching) and per light setting for max quality
int32 shadowQuality = maxShadowsQuality;
// Fade shadow on distance
const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance);
// Set up GPU context and render view
const auto shadowMapsSizeCube = (float)_shadowMapsSizeCube;
context->SetViewportAndScissors(shadowMapsSizeCube, shadowMapsSizeCube);
_shadowContext.View.SetProjector(SpotLight_NearPlane, lightRadius, lightPosition, lightDirection, light.UpVector, light.OuterConeAngle * 2.0f);
_shadowContext.View.PrepareCache(_shadowContext, shadowMapsSizeCube, shadowMapsSizeCube, Float2::Zero, &view);
// Render depth to all 1 face of the cube map
const int32 cubeFaceIndex = 0;
{
// Set up view
_shadowCache.Clear();
Matrix::Transpose(_shadowContext.View.ViewProjection(), sperLight.LightShadow.ShadowVP[cubeFaceIndex]);
// Set render target
auto rt = _shadowMapCube->View(cubeFaceIndex);
context->ResetSR();
context->SetRenderTarget(rt, static_cast<GPUTextureView*>(nullptr));
context->ClearDepth(rt);
// Render actors to the shadow map
renderContext.Task->OnCollectDrawCalls(_shadowContext);
_shadowCache.SortDrawCalls(_shadowContext, false, DrawCallsListType::Depth);
_shadowCache.ExecuteDrawCalls(_shadowContext, DrawCallsListType::Depth);
}
// Restore GPU context
context->ResetSR();
context->ResetRenderTarget();
const Viewport viewport = renderContext.Task->GetViewport();
GPUTexture* depthBuffer = renderContext.Buffers->DepthBuffer;
GPUTextureView* depthBufferSRV = depthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView ? depthBuffer->ViewReadOnlyDepth() : depthBuffer->View();
context->SetViewportAndScissors(viewport);
context->BindSR(0, renderContext.Buffers->GBuffer0);
context->BindSR(1, renderContext.Buffers->GBuffer1);
context->BindSR(2, renderContext.Buffers->GBuffer2);
context->BindSR(3, depthBufferSRV);
context->BindSR(4, renderContext.Buffers->GBuffer3);
// Setup shader data
GBufferPass::SetInputs(view, sperLight.GBuffer);
light.SetupLightData(&sperLight.Light, true);
sperLight.LightShadow.ShadowMapSize = shadowMapsSizeCube;
sperLight.LightShadow.Sharpness = light.ShadowsSharpness;
sperLight.LightShadow.Fade = Math::Saturate(light.ShadowsStrength * fade);
sperLight.LightShadow.NormalOffsetScale = light.ShadowsNormalOffsetScale * NormalOffsetScaleTweak * (1.0f / shadowMapsSizeCube);
sperLight.LightShadow.Bias = light.ShadowsDepthBias;
sperLight.LightShadow.FadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
sperLight.LightShadow.NumCascades = 1;
sperLight.LightShadow.CascadeSplits = Float4::Zero;
Matrix::Transpose(view.ViewProjection(), sperLight.ViewProjectionMatrix);
sperLight.ContactShadowsDistance = light.ShadowsDistance;
sperLight.ContactShadowsLength = view.Flags & ViewFlags::ContactShadows ? light.ContactShadowsLength : 0.0f;
// Calculate world view projection matrix for the light sphere
Matrix world, wvp, matrix;
Matrix::Scaling(lightRadius * sphereModelScale, wvp);
Matrix::Translation(lightPosition, matrix);
Matrix::Multiply(wvp, matrix, world);
Matrix::Multiply(world, view.ViewProjection(), wvp);
Matrix::Transpose(wvp, sperLight.WVP);
// Render shadow in screen space
context->UpdateCB(shader->GetCB(0), &sperLight);
context->BindCB(0, shader->GetCB(0));
context->BindCB(1, shader->GetCB(1));
context->BindSR(5, _shadowMapCube->View(cubeFaceIndex));
context->SetRenderTarget(shadowMask);
context->SetState(_psShadowSpot.Get(shadowQuality + (sperLight.ContactShadowsLength > ZeroTolerance ? 4 : 0)));
_sphereModel->Render(context);
// Cleanup
context->ResetRenderTarget();
context->UnBindSR(5);
// Render volumetric light with shadow
VolumetricFogPass::Instance()->RenderLight(renderContext, context, light, _shadowMapCube->View(cubeFaceIndex), sperLight.LightShadow);
}
void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererDirectionalLightData& light, int32 index, GPUTextureView* shadowMask)
{
PROFILE_GPU_CPU("Shadow");
// Cache data
auto device = GPUDevice::Instance;
auto context = device->GetMainContext();
auto& view = renderContext.View;
const RenderView& view = renderContext.View;
auto mainCache = renderContext.List;
auto shader = _shader->GetShader();
Data sperLight;
Float3 lightDirection = light.Direction;
float shadowsDistance = Math::Min(view.Far, light.ShadowsDistance);
int32 csmCount = Math::Clamp(light.CascadeCount, 0, MAX_CSM_CASCADES);
bool blendCSM = Graphics::AllowCSMBlending;
const auto shadowMapsSizeCSM = (float)_shadowMapsSizeCSM;
#if USE_EDITOR
if (IsRunningRadiancePass)
blendCSM = false;
@@ -561,9 +300,7 @@ void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererDirectional
// Convert distance splits to ratios cascade in the range [0, 1]
for (int32 i = 0; i < MAX_CSM_CASCADES; i++)
{
cascadeSplits[i] = (cascadeSplits[i] - cameraNear) / cameraRange;
}
}
// Select best Up vector
@@ -585,10 +322,13 @@ void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererDirectional
Float3 frustumCorners[8];
Matrix shadowView, shadowProjection, shadowVP;
// Set up GPU context and render view
const auto shadowMapsSizeCSM = (float)_shadowMapsSizeCSM;
context->SetViewportAndScissors(shadowMapsSizeCSM, shadowMapsSizeCSM);
_shadowContext.View.PrepareCache(_shadowContext, shadowMapsSizeCSM, shadowMapsSizeCSM, Float2::Zero, &view);
// Init shadow data
light.ShadowDataIndex = _shadowData.Count();
auto& shadowData = _shadowData.AddOne();
shadowData.ContextIndex = renderContextBatch.Contexts.Count();
shadowData.ContextCount = csmCount;
shadowData.BlendCSM = blendCSM;
renderContextBatch.Contexts.AddDefault(shadowData.ContextCount);
// Create the different view and projection matrices for each split
float splitMinRatio = 0;
@@ -684,26 +424,213 @@ void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererDirectional
0.5f, 0.5f, 0.0f, 1.0f);
Matrix m;
Matrix::Multiply(shadowVP, T, m);
Matrix::Transpose(m, sperLight.LightShadow.ShadowVP[cascadeIndex]);
Matrix::Transpose(m, shadowData.Constants.ShadowVP[cascadeIndex]);
}
// Set up view and cache
_shadowCache.Clear();
_shadowContext.View.Position = -lightDirection * shadowsDistance + view.Position;
_shadowContext.View.Direction = lightDirection;
_shadowContext.View.SetUp(shadowView, shadowProjection);
_shadowContext.View.CullingFrustum.SetMatrix(cullingVP);
// Setup context for cascade
auto& shadowContext = renderContextBatch.Contexts[shadowData.ContextIndex + cascadeIndex];
SetupRenderContext(renderContext, shadowContext);
shadowContext.List->Clear();
shadowContext.View.Position = -lightDirection * shadowsDistance + view.Position;
shadowContext.View.Direction = lightDirection;
shadowContext.View.SetUp(shadowView, shadowProjection);
shadowContext.View.CullingFrustum.SetMatrix(cullingVP);
shadowContext.View.PrepareCache(shadowContext, shadowMapsSizeCSM, shadowMapsSizeCSM, Float2::Zero, &view);
}
// Set render target
const auto rt = _shadowMapCSM->View(cascadeIndex);
// Setup constant buffer data
shadowData.Constants.ShadowMapSize = shadowMapsSizeCSM;
shadowData.Constants.Sharpness = light.ShadowsSharpness;
shadowData.Constants.Fade = Math::Saturate(light.ShadowsStrength);
shadowData.Constants.NormalOffsetScale = light.ShadowsNormalOffsetScale * NormalOffsetScaleTweak * (1.0f / shadowMapsSizeCSM);
shadowData.Constants.Bias = light.ShadowsDepthBias;
shadowData.Constants.FadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
shadowData.Constants.NumCascades = csmCount;
shadowData.Constants.CascadeSplits = view.Near + Float4(cascadeSplits) * cameraRange;
}
void ShadowsPass::SetupLight(RenderContext& renderContext, RenderContextBatch& renderContextBatch, RendererPointLightData& light)
{
// Init shadow data
light.ShadowDataIndex = _shadowData.Count();
auto& shadowData = _shadowData.AddOne();
shadowData.ContextIndex = renderContextBatch.Contexts.Count();
shadowData.ContextCount = 6;
renderContextBatch.Contexts.AddDefault(shadowData.ContextCount);
const auto& view = renderContext.View;
const auto shadowMapsSizeCube = (float)_shadowMapsSizeCube;
// Fade shadow on distance
const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
const float dstLightToView = Float3::Distance(light.Position, view.Position);
const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance);
// Render depth to all 6 faces of the cube map
for (int32 faceIndex = 0; faceIndex < 6; faceIndex++)
{
auto& shadowContext = renderContextBatch.Contexts[shadowData.ContextIndex + faceIndex];
SetupRenderContext(renderContext, shadowContext);
shadowContext.List->Clear();
shadowContext.View.SetUpCube(PointLight_NearPlane, light.Radius, light.Position);
shadowContext.View.SetFace(faceIndex);
shadowContext.View.PrepareCache(shadowContext, shadowMapsSizeCube, shadowMapsSizeCube, Float2::Zero, &view);
Matrix::Transpose(shadowContext.View.ViewProjection(), shadowData.Constants.ShadowVP[faceIndex]);
}
// Setup constant buffer data
shadowData.Constants.ShadowMapSize = shadowMapsSizeCube;
shadowData.Constants.Sharpness = light.ShadowsSharpness;
shadowData.Constants.Fade = Math::Saturate(light.ShadowsStrength * fade);
shadowData.Constants.NormalOffsetScale = light.ShadowsNormalOffsetScale * NormalOffsetScaleTweak * (1.0f / shadowMapsSizeCube);
shadowData.Constants.Bias = light.ShadowsDepthBias;
shadowData.Constants.FadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
shadowData.Constants.NumCascades = 1;
shadowData.Constants.CascadeSplits = Float4::Zero;
}
void ShadowsPass::SetupLight(RenderContext& renderContext, RenderContextBatch& renderContextBatch, RendererSpotLightData& light)
{
// Init shadow data
light.ShadowDataIndex = _shadowData.Count();
auto& shadowData = _shadowData.AddOne();
shadowData.ContextIndex = renderContextBatch.Contexts.Count();
shadowData.ContextCount = 1;
renderContextBatch.Contexts.AddDefault(shadowData.ContextCount);
const auto& view = renderContext.View;
const auto shadowMapsSizeCube = (float)_shadowMapsSizeCube;
// Fade shadow on distance
const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
const float dstLightToView = Float3::Distance(light.Position, view.Position);
const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance);
// Render depth to all 1 face of the cube map
constexpr int32 faceIndex = 0;
{
auto& shadowContext = renderContextBatch.Contexts[shadowData.ContextIndex + faceIndex];
SetupRenderContext(renderContext, shadowContext);
shadowContext.List->Clear();
shadowContext.View.SetProjector(SpotLight_NearPlane, light.Radius, light.Position, light.Direction, light.UpVector, light.OuterConeAngle * 2.0f);
shadowContext.View.PrepareCache(shadowContext, shadowMapsSizeCube, shadowMapsSizeCube, Float2::Zero, &view);
Matrix::Transpose(shadowContext.View.ViewProjection(), shadowData.Constants.ShadowVP[faceIndex]);
}
// Setup constant buffer data
shadowData.Constants.ShadowMapSize = shadowMapsSizeCube;
shadowData.Constants.Sharpness = light.ShadowsSharpness;
shadowData.Constants.Fade = Math::Saturate(light.ShadowsStrength * fade);
shadowData.Constants.NormalOffsetScale = light.ShadowsNormalOffsetScale * NormalOffsetScaleTweak * (1.0f / shadowMapsSizeCube);
shadowData.Constants.Bias = light.ShadowsDepthBias;
shadowData.Constants.FadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
shadowData.Constants.NumCascades = 1;
shadowData.Constants.CascadeSplits = Float4::Zero;
}
void ShadowsPass::Dispose()
{
// Base
RendererPass::Dispose();
// Cleanup
_psShadowDir.Delete();
_psShadowPoint.Delete();
_psShadowSpot.Delete();
_shader = nullptr;
_sphereModel = nullptr;
SAFE_DELETE_GPU_RESOURCE(_shadowMapCSM);
SAFE_DELETE_GPU_RESOURCE(_shadowMapCube);
}
void ShadowsPass::SetupShadows(RenderContext& renderContext, RenderContextBatch& renderContextBatch)
{
PROFILE_CPU();
_shadowData.Clear();
auto& view = renderContext.View;
// Update shadow map
const auto shadowMapsQuality = Graphics::ShadowMapsQuality;
if (shadowMapsQuality != _currentShadowMapsQuality)
updateShadowMapSize();
auto shadowsQuality = Graphics::ShadowsQuality;
maxShadowsQuality = Math::Clamp(Math::Min<int32>(static_cast<int32>(shadowsQuality), static_cast<int32>(view.MaxShadowsQuality)), 0, static_cast<int32>(Quality::MAX) - 1);
// Create shadow projections for lights
for (auto& light : renderContext.List->DirectionalLights)
{
if (::CanRenderShadow(view, light) && CanRenderShadow(renderContext, light))
SetupLight(renderContext, renderContextBatch, light);
}
for (auto& light : renderContext.List->PointLights)
{
if (::CanRenderShadow(view, light) && CanRenderShadow(renderContext, light))
SetupLight(renderContext, renderContextBatch, light);
}
for (auto& light : renderContext.List->SpotLights)
{
if (::CanRenderShadow(view, light) && CanRenderShadow(renderContext, light))
SetupLight(renderContext, renderContextBatch, light);
}
}
bool ShadowsPass::CanRenderShadow(const RenderContext& renderContext, const RendererPointLightData& light)
{
const Float3 lightPosition = light.Position;
const float dstLightToView = Float3::Distance(lightPosition, renderContext.View.Position);
// Fade shadow on distance
const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance);
return fade > ZeroTolerance && _supportsShadows;
}
bool ShadowsPass::CanRenderShadow(const RenderContext& renderContext, const RendererSpotLightData& light)
{
const Float3 lightPosition = light.Position;
const float dstLightToView = Float3::Distance(lightPosition, renderContext.View.Position);
// Fade shadow on distance
const float fadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
const float fade = 1 - Math::Saturate((dstLightToView - light.Radius - light.ShadowsDistance + fadeDistance) / fadeDistance);
return fade > ZeroTolerance && _supportsShadows;
}
bool ShadowsPass::CanRenderShadow(const RenderContext& renderContext, const RendererDirectionalLightData& light)
{
return _supportsShadows;
}
void ShadowsPass::RenderShadow(RenderContextBatch& renderContextBatch, RendererPointLightData& light, GPUTextureView* shadowMask)
{
if (light.ShadowDataIndex == -1)
return;
PROFILE_GPU_CPU("Shadow");
GPUContext* context = GPUDevice::Instance->GetMainContext();
RenderContext& renderContext = renderContextBatch.Contexts[0];
ShadowData& shadowData = _shadowData[light.ShadowDataIndex];
const float sphereModelScale = 3.0f;
auto& view = renderContext.View;
auto shader = _shader->GetShader();
// TODO: here we can use lower shadows quality based on light distance to view (LOD switching) and per light setting for max quality
int32 shadowQuality = maxShadowsQuality;
// Set up GPU context and render view
const auto shadowMapsSizeCube = (float)_shadowMapsSizeCube;
context->SetViewportAndScissors(shadowMapsSizeCube, shadowMapsSizeCube);
// Render depth to all 6 faces of the cube map
for (int32 faceIndex = 0; faceIndex < 6; faceIndex++)
{
auto rt = _shadowMapCube->View(faceIndex);
context->ResetSR();
context->SetRenderTarget(rt, static_cast<GPUTextureView*>(nullptr));
context->ClearDepth(rt);
// Render actors to the shadow map
renderContext.Task->OnCollectDrawCalls(_shadowContext);
_shadowCache.SortDrawCalls(_shadowContext, false, DrawCallsListType::Depth);
_shadowCache.ExecuteDrawCalls(_shadowContext, DrawCallsListType::Depth);
auto& shadowContext = renderContextBatch.Contexts[shadowData.ContextIndex + faceIndex];
shadowContext.List->ExecuteDrawCalls(shadowContext, DrawCallsListType::Depth);
}
// Restore GPU context
@@ -720,27 +647,168 @@ void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererDirectional
context->BindSR(4, renderContext.Buffers->GBuffer3);
// Setup shader data
Data sperLight;
GBufferPass::SetInputs(view, sperLight.GBuffer);
light.SetupLightData(&sperLight.Light, true);
sperLight.LightShadow.ShadowMapSize = shadowMapsSizeCSM;
sperLight.LightShadow.Sharpness = light.ShadowsSharpness;
sperLight.LightShadow.Fade = Math::Saturate(light.ShadowsStrength);
sperLight.LightShadow.NormalOffsetScale = light.ShadowsNormalOffsetScale * NormalOffsetScaleTweak * (1.0f / shadowMapsSizeCSM);
sperLight.LightShadow.Bias = light.ShadowsDepthBias;
sperLight.LightShadow.FadeDistance = Math::Max(light.ShadowsFadeDistance, 0.1f);
sperLight.LightShadow.NumCascades = csmCount;
sperLight.LightShadow.CascadeSplits = view.Near + Float4(cascadeSplits) * cameraRange;
sperLight.LightShadow = shadowData.Constants;
Matrix::Transpose(view.ViewProjection(), sperLight.ViewProjectionMatrix);
sperLight.ContactShadowsDistance = light.ShadowsDistance;
sperLight.ContactShadowsLength = view.Flags & ViewFlags::ContactShadows ? light.ContactShadowsLength : 0.0f;
// Calculate world view projection matrix for the light sphere
Matrix world, wvp, matrix;
Matrix::Scaling(light.Radius * sphereModelScale, wvp);
Matrix::Translation(light.Position, matrix);
Matrix::Multiply(wvp, matrix, world);
Matrix::Multiply(world, view.ViewProjection(), wvp);
Matrix::Transpose(wvp, sperLight.WVP);
// Render shadow in screen space
context->UpdateCB(shader->GetCB(0), &sperLight);
context->BindCB(0, shader->GetCB(0));
context->BindCB(1, shader->GetCB(1));
context->BindSR(5, _shadowMapCube->ViewArray());
context->SetRenderTarget(shadowMask);
context->SetState(_psShadowPoint.Get(shadowQuality + (sperLight.ContactShadowsLength > ZeroTolerance ? 4 : 0)));
_sphereModel->Render(context);
// Cleanup
context->ResetRenderTarget();
context->UnBindSR(5);
// Render volumetric light with shadow
VolumetricFogPass::Instance()->RenderLight(renderContext, context, light, _shadowMapCube->ViewArray(), sperLight.LightShadow);
}
void ShadowsPass::RenderShadow(RenderContextBatch& renderContextBatch, RendererSpotLightData& light, GPUTextureView* shadowMask)
{
if (light.ShadowDataIndex == -1)
return;
PROFILE_GPU_CPU("Shadow");
GPUContext* context = GPUDevice::Instance->GetMainContext();
RenderContext& renderContext = renderContextBatch.Contexts[0];
ShadowData& shadowData = _shadowData[light.ShadowDataIndex];
const float sphereModelScale = 3.0f;
auto& view = renderContext.View;
auto shader = _shader->GetShader();
// TODO: here we can use lower shadows quality based on light distance to view (LOD switching) and per light setting for max quality
int32 shadowQuality = maxShadowsQuality;
// Set up GPU context and render view
const auto shadowMapsSizeCube = (float)_shadowMapsSizeCube;
context->SetViewportAndScissors(shadowMapsSizeCube, shadowMapsSizeCube);
// Render depth to all 1 face of the cube map
constexpr int32 faceIndex = 0;
{
auto rt = _shadowMapCube->View(faceIndex);
context->ResetSR();
context->SetRenderTarget(rt, static_cast<GPUTextureView*>(nullptr));
context->ClearDepth(rt);
auto& shadowContext = renderContextBatch.Contexts[shadowData.ContextIndex + faceIndex];
shadowContext.List->ExecuteDrawCalls(shadowContext, DrawCallsListType::Depth);
}
// Restore GPU context
context->ResetSR();
context->ResetRenderTarget();
const Viewport viewport = renderContext.Task->GetViewport();
GPUTexture* depthBuffer = renderContext.Buffers->DepthBuffer;
GPUTextureView* depthBufferSRV = depthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView ? depthBuffer->ViewReadOnlyDepth() : depthBuffer->View();
context->SetViewportAndScissors(viewport);
context->BindSR(0, renderContext.Buffers->GBuffer0);
context->BindSR(1, renderContext.Buffers->GBuffer1);
context->BindSR(2, renderContext.Buffers->GBuffer2);
context->BindSR(3, depthBufferSRV);
context->BindSR(4, renderContext.Buffers->GBuffer3);
// Setup shader data
Data sperLight;
GBufferPass::SetInputs(view, sperLight.GBuffer);
light.SetupLightData(&sperLight.Light, true);
sperLight.LightShadow = shadowData.Constants;
Matrix::Transpose(view.ViewProjection(), sperLight.ViewProjectionMatrix);
sperLight.ContactShadowsDistance = light.ShadowsDistance;
sperLight.ContactShadowsLength = view.Flags & ViewFlags::ContactShadows ? light.ContactShadowsLength : 0.0f;
// Calculate world view projection matrix for the light sphere
Matrix world, wvp, matrix;
Matrix::Scaling(light.Radius * sphereModelScale, wvp);
Matrix::Translation(light.Position, matrix);
Matrix::Multiply(wvp, matrix, world);
Matrix::Multiply(world, view.ViewProjection(), wvp);
Matrix::Transpose(wvp, sperLight.WVP);
// Render shadow in screen space
context->UpdateCB(shader->GetCB(0), &sperLight);
context->BindCB(0, shader->GetCB(0));
context->BindCB(1, shader->GetCB(1));
context->BindSR(5, _shadowMapCube->View(faceIndex));
context->SetRenderTarget(shadowMask);
context->SetState(_psShadowSpot.Get(shadowQuality + (sperLight.ContactShadowsLength > ZeroTolerance ? 4 : 0)));
_sphereModel->Render(context);
// Cleanup
context->ResetRenderTarget();
context->UnBindSR(5);
// Render volumetric light with shadow
VolumetricFogPass::Instance()->RenderLight(renderContext, context, light, _shadowMapCube->View(faceIndex), sperLight.LightShadow);
}
void ShadowsPass::RenderShadow(RenderContextBatch& renderContextBatch, RendererDirectionalLightData& light, int32 index, GPUTextureView* shadowMask)
{
if (light.ShadowDataIndex == -1)
return;
PROFILE_GPU_CPU("Shadow");
GPUContext* context = GPUDevice::Instance->GetMainContext();
RenderContext& renderContext = renderContextBatch.Contexts[0];
ShadowData& shadowData = _shadowData[light.ShadowDataIndex];
const float shadowMapsSizeCSM = (float)_shadowMapsSizeCSM;
context->SetViewportAndScissors(shadowMapsSizeCSM, shadowMapsSizeCSM);
// Render shadow map for each projection
for (int32 cascadeIndex = 0; cascadeIndex < shadowData.ContextCount; cascadeIndex++)
{
const auto rt = _shadowMapCSM->View(cascadeIndex);
context->ResetSR();
context->SetRenderTarget(rt, static_cast<GPUTextureView*>(nullptr));
context->ClearDepth(rt);
auto& shadowContext = renderContextBatch.Contexts[shadowData.ContextIndex + cascadeIndex];
shadowContext.List->ExecuteDrawCalls(shadowContext, DrawCallsListType::Depth);
}
// Restore GPU context
context->ResetSR();
context->ResetRenderTarget();
GPUTexture* depthBuffer = renderContext.Buffers->DepthBuffer;
GPUTextureView* depthBufferSRV = depthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView ? depthBuffer->ViewReadOnlyDepth() : depthBuffer->View();
context->SetViewportAndScissors(renderContext.Task->GetViewport());
context->BindSR(0, renderContext.Buffers->GBuffer0);
context->BindSR(1, renderContext.Buffers->GBuffer1);
context->BindSR(2, renderContext.Buffers->GBuffer2);
context->BindSR(3, depthBufferSRV);
context->BindSR(4, renderContext.Buffers->GBuffer3);
// Setup shader data
Data sperLight;
auto& view = renderContext.View;
GBufferPass::SetInputs(view, sperLight.GBuffer);
light.SetupLightData(&sperLight.Light, true);
sperLight.LightShadow = shadowData.Constants;
Matrix::Transpose(view.ViewProjection(), sperLight.ViewProjectionMatrix);
sperLight.ContactShadowsDistance = light.ShadowsDistance;
sperLight.ContactShadowsLength = view.Flags & ViewFlags::ContactShadows ? light.ContactShadowsLength : 0.0f;
// Render shadow in screen space
auto shader = _shader->GetShader();
context->UpdateCB(shader->GetCB(0), &sperLight);
context->BindCB(0, shader->GetCB(0));
context->BindCB(1, shader->GetCB(1));
context->BindSR(5, _shadowMapCSM->ViewArray());
context->SetRenderTarget(shadowMask);
context->SetState(_psShadowDir.Get(maxShadowsQuality + static_cast<int32>(Quality::MAX) * blendCSM + (sperLight.ContactShadowsLength > ZeroTolerance ? 8 : 0)));
context->SetState(_psShadowDir.Get(maxShadowsQuality + static_cast<int32>(Quality::MAX) * shadowData.BlendCSM + (sperLight.ContactShadowsLength > ZeroTolerance ? 8 : 0)));
context->DrawFullscreenTriangle();
// Cleanup

View File

@@ -15,7 +15,7 @@
#define SHADOWS_PASS_SS_RR_FORMAT PixelFormat::R11G11B10_Float
template<typename T>
bool CanRenderShadow(RenderView& view, const T& light)
bool CanRenderShadow(const RenderView& view, const T& light)
{
bool result = false;
switch ((ShadowsCastingMode)light.ShadowsMode)
@@ -42,6 +42,14 @@ class ShadowsPass : public RendererPass<ShadowsPass>
{
private:
struct ShadowData
{
int32 ContextIndex;
int32 ContextCount;
bool BlendCSM;
LightShadowData Constants;
};
// Shader stuff
AssetReference<Shader> _shader;
GPUPipelineStatePermutationsPs<static_cast<int32>(Quality::MAX) * 2 * 2> _psShadowDir;
@@ -57,9 +65,8 @@ private:
Quality _currentShadowMapsQuality;
// Shadow map rendering stuff
RenderContext _shadowContext;
RenderList _shadowCache;
AssetReference<Model> _sphereModel;
Array<ShadowData> _shadowData;
// Cached state for the current frame rendering (setup via Prepare)
int32 maxShadowsQuality;
@@ -87,6 +94,10 @@ public:
LightShadowData LastDirLight;
public:
/// <summary>
/// Setups the shadows rendering for batched scene drawing. Checks which lights will cast a shadow.
/// </summary>
void SetupShadows(RenderContext& renderContext, RenderContextBatch& renderContextBatch);
/// <summary>
/// Determines whether can render shadow for the specified light.
@@ -94,7 +105,7 @@ public:
/// <param name="renderContext">The rendering context.</param>
/// <param name="light">The light.</param>
/// <returns><c>true</c> if can render shadow for the specified light; otherwise, <c>false</c>.</returns>
bool CanRenderShadow(RenderContext& renderContext, const RendererPointLightData& light);
bool CanRenderShadow(const RenderContext& renderContext, const RendererPointLightData& light);
/// <summary>
/// Determines whether can render shadow for the specified light.
@@ -102,7 +113,7 @@ public:
/// <param name="renderContext">The rendering context.</param>
/// <param name="light">The light.</param>
/// <returns><c>true</c> if can render shadow for the specified light; otherwise, <c>false</c>.</returns>
bool CanRenderShadow(RenderContext& renderContext, const RendererSpotLightData& light);
bool CanRenderShadow(const RenderContext& renderContext, const RendererSpotLightData& light);
/// <summary>
/// Determines whether can render shadow for the specified light.
@@ -110,43 +121,40 @@ public:
/// <param name="renderContext">The rendering context.</param>
/// <param name="light">The light.</param>
/// <returns><c>true</c> if can render shadow for the specified light; otherwise, <c>false</c>.</returns>
bool CanRenderShadow(RenderContext& renderContext, const RendererDirectionalLightData& light);
/// <summary>
/// Prepares the shadows rendering. Called by the light pass once per frame.
/// </summary>
/// <param name="renderContext">The rendering context.</param>
/// <param name="context">The GPU command context.</param>
void Prepare(RenderContext& renderContext, GPUContext* context);
bool CanRenderShadow(const RenderContext& renderContext, const RendererDirectionalLightData& light);
/// <summary>
/// Renders the shadow mask for the given light.
/// </summary>
/// <param name="renderContext">The rendering context.</param>
/// <param name="renderContextBatch">The rendering context batch.</param>
/// <param name="light">The light.</param>
/// <param name="shadowMask">The shadow mask (output).</param>
void RenderShadow(RenderContext& renderContext, RendererPointLightData& light, GPUTextureView* shadowMask);
void RenderShadow(RenderContextBatch& renderContextBatch, RendererPointLightData& light, GPUTextureView* shadowMask);
/// <summary>
/// Renders the shadow mask for the given light.
/// </summary>
/// <param name="renderContext">The rendering context.</param>
/// <param name="renderContextBatch">The rendering context batch.</param>
/// <param name="light">The light.</param>
/// <param name="shadowMask">The shadow mask (output).</param>
void RenderShadow(RenderContext& renderContext, RendererSpotLightData& light, GPUTextureView* shadowMask);
void RenderShadow(RenderContextBatch& renderContextBatch, RendererSpotLightData& light, GPUTextureView* shadowMask);
/// <summary>
/// Renders the shadow mask for the given light.
/// </summary>
/// <param name="renderContext">The rendering context.</param>
/// <param name="renderContextBatch">The rendering context batch.</param>
/// <param name="light">The light.</param>
/// <param name="index">The light index.</param>
/// <param name="shadowMask">The shadow mask (output).</param>
void RenderShadow(RenderContext& renderContext, RendererDirectionalLightData& light, int32 index, GPUTextureView* shadowMask);
void RenderShadow(RenderContextBatch& renderContextBatch, RendererDirectionalLightData& light, int32 index, GPUTextureView* shadowMask);
private:
void updateShadowMapSize();
void SetupRenderContext(RenderContext& renderContext, RenderContext& shadowContext);
void SetupLight(RenderContext& renderContext, RenderContextBatch& renderContextBatch, RendererDirectionalLightData& light);
void SetupLight(RenderContext& renderContext, RenderContextBatch& renderContextBatch, RendererPointLightData& light);
void SetupLight(RenderContext& renderContext, RenderContextBatch& renderContextBatch, RendererSpotLightData& light);
#if COMPILE_WITH_DEV_ENV
void OnShaderReloading(Asset* obj)

View File

@@ -121,8 +121,9 @@ void TerrainChunk::Draw(const RenderContext& renderContext) const
//drawCall.TerrainData.HeightmapUVScaleBias.W += halfTexelOffset;
// Submit draw call
auto drawModes = (DrawPass)(_patch->_terrain->DrawModes & renderContext.View.Pass);
renderContext.List->AddDrawCall(drawModes, flags, drawCall, true);
auto drawModes = (DrawPass)(_patch->_terrain->DrawModes & renderContext.View.Pass & (uint32)drawCall.Material->GetDrawModes());
if (drawModes != DrawPass::None)
renderContext.List->AddDrawCall(drawModes, flags, drawCall, true);
}
void TerrainChunk::Draw(const RenderContext& renderContext, MaterialBase* material, int32 lodIndex) const
@@ -176,8 +177,9 @@ void TerrainChunk::Draw(const RenderContext& renderContext, MaterialBase* materi
//drawCall.TerrainData.HeightmapUVScaleBias.W += halfTexelOffset;
// Submit draw call
auto drawModes = (DrawPass)(_patch->_terrain->DrawModes & renderContext.View.Pass);
renderContext.List->AddDrawCall(drawModes, flags, drawCall, true);
auto drawModes = (DrawPass)(_patch->_terrain->DrawModes & renderContext.View.Pass & (uint32)drawCall.Material->GetDrawModes());
if (drawModes != DrawPass::None)
renderContext.List->AddDrawCall(drawModes, flags, drawCall, true);
}
bool TerrainChunk::Intersects(const Ray& ray, Real& distance)

View File

@@ -344,14 +344,12 @@ void TextRender::Draw(RenderContext& renderContext)
if (renderContext.View.Pass == DrawPass::GlobalSurfaceAtlas)
return; // TODO: Text rendering to Global Surface Atlas
if (_isDirty)
{
UpdateLayout();
}
Matrix world;
renderContext.View.GetWorldMatrix(_transform, world);
GEOMETRY_DRAW_STATE_EVENT_BEGIN(_drawState, world);
const DrawPass drawModes = (DrawPass)(DrawModes & renderContext.View.Pass & (int32)renderContext.View.GetShadowsDrawPassMask(ShadowsMode));
const DrawPass drawModes = (DrawPass)(DrawModes & renderContext.View.Pass & (uint32)renderContext.View.GetShadowsDrawPassMask(ShadowsMode));
if (_vb0.Data.Count() > 0 && drawModes != DrawPass::None)
{
#if USE_EDITOR
@@ -394,6 +392,8 @@ void TextRender::Draw(RenderContext& renderContext)
// Submit draw calls
for (const auto& e : _drawChunks)
{
if ((drawModes & e.Material->GetDrawModes()) == 0)
continue;
drawCall.Draw.IndicesCount = e.IndicesCount;
drawCall.Draw.StartIndex = e.StartIndex;
drawCall.Material = e.Material;