Add Material Complexity debug view model

This commit is contained in:
2021-10-07 09:28:49 +02:00
parent c3fe366be8
commit 98ca7c1726
19 changed files with 430 additions and 27 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
View File
@@ -1398,6 +1398,7 @@ namespace FlaxEditor.Viewport
new ViewModeOptions(ViewMode.VertexColors, "Vertex Colors"),
new ViewModeOptions(ViewMode.PhysicsColliders, "Physics Colliders"),
new ViewModeOptions(ViewMode.LODPreview, "LOD Preview"),
new ViewModeOptions(ViewMode.MaterialComplexity, "Material Complexity"),
};
private void WidgetCamSpeedShowHide(Control cm)
+5
View File
@@ -818,6 +818,11 @@ API_ENUM() enum class ViewMode
/// Draw Level Of Detail number as colors to debug LOD switches.
/// </summary>
LODPreview = 21,
/// <summary>
/// Draw material shaders complexity to visualize performance of pixels rendering.
/// </summary>
MaterialComplexity = 22,
};
/// <summary>
+17
View File
@@ -56,6 +56,23 @@ bool GPUPipelineState::Init(const Description& desc)
CHECK_STAGE(PS);
#undef CHECK_STAGE
#if USE_EDITOR
// Estimate somehow performance cost of this pipeline state for the content profiling
const int32 textureLookupCost = 20;
const int32 tessCost = 300;
Complexity = Utilities::CountBits(_meta.UsedSRsMask) * textureLookupCost;
if (desc.PS)
Complexity += desc.PS->GetBindings().InstructionsCount;
if (desc.HS || desc.DS)
Complexity += tessCost;
if (desc.DepthWriteEnable)
Complexity += 5;
if (desc.DepthTestEnable)
Complexity += 5;
if (desc.BlendMode.BlendEnable)
Complexity += 20;
#endif
return false;
}
@@ -120,6 +120,9 @@ public:
/// </summary>
Description DebugDesc;
#endif
#if USE_EDITOR
int32 Complexity;
#endif
public:
@@ -49,6 +49,9 @@ API_ENUM() enum class MaterialDomain : byte
/// The particle shader used for volumetric effects rendering such as Volumetric Fog.
/// </summary>
VolumeParticle = 7,
API_ENUM(Attributes="HideInEditor")
MAX
};
/// <summary>
+22 -20
View File
@@ -11,19 +11,20 @@
#include "Engine/Graphics/Shaders/GPUConstantBuffer.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Renderer/DrawCall.h"
#include "Engine/Renderer/RenderList.h"
PACK_STRUCT(struct LODPreviewMaterialShaderData {
PACK_STRUCT(struct SingleColorShaderData {
Matrix ViewProjectionMatrix;
Matrix WorldMatrix;
Color Color;
Vector3 WorldInvScale;
Vector3 Dummy0;
float LODDitherFactor;
});
LODPreviewMaterialShader::LODPreviewMaterialShader()
{
_ps = GPUDevice::Instance->CreatePipelineState();
_shader = Content::LoadAsyncInternal<Shader>(TEXT("Shaders/Editor/LODPreview"));
_psModel.CreatePipelineStates();
_shader = Content::LoadAsyncInternal<Shader>(TEXT("Shaders/Editor/SingleColor"));
if (!_shader)
return;
#if COMPILE_WITH_DEV_ENV
@@ -35,7 +36,7 @@ LODPreviewMaterialShader::LODPreviewMaterialShader()
void LODPreviewMaterialShader::OnShaderReloading(Asset* obj)
{
_ps->ReleaseGPU();
_psModel.Release();
}
#endif
@@ -50,6 +51,12 @@ bool LODPreviewMaterialShader::IsReady() const
return _shader && _shader->IsLoaded();
}
bool LODPreviewMaterialShader::CanUseInstancing(InstancingHandler& handler) const
{
handler = { SurfaceDrawCallHandler::GetHash, SurfaceDrawCallHandler::CanBatch, SurfaceDrawCallHandler::WriteDrawCall, };
return true;
}
DrawPass LODPreviewMaterialShader::GetDrawModes() const
{
return DrawPass::GBuffer;
@@ -61,12 +68,14 @@ void LODPreviewMaterialShader::Bind(BindParameters& params)
auto& drawCall = *params.FirstDrawCall;
auto shader = _shader->GetShader();
auto cb = shader->GetCB(0);
if (!_ps->IsValid())
const int32 psIndex = params.DrawCallsCount == 1 ? 0 : 1;
auto ps = _psModel[psIndex];
if (!ps->IsValid())
{
auto psDesc = GPUPipelineState::Description::Default;
psDesc.VS = shader->GetVS("VS");
psDesc.PS = shader->GetPS("PS");
_ps->Init(psDesc);
psDesc.VS = shader->GetVS("VS_Model", psIndex);
psDesc.PS = shader->GetPS("PS_GBuffer");
ps->Init(psDesc);
}
// Find the LOD that produced this draw call
@@ -96,17 +105,11 @@ void LODPreviewMaterialShader::Bind(BindParameters& params)
// Bind
if (cb && cb->GetSize())
{
ASSERT(cb->GetSize() == sizeof(LODPreviewMaterialShaderData));
LODPreviewMaterialShaderData data;
ASSERT_LOW_LAYER(cb->GetSize() == sizeof(SingleColorShaderData));
SingleColorShaderData data;
Matrix::Transpose(params.RenderContext.View.Frustum.GetMatrix(), data.ViewProjectionMatrix);
Matrix::Transpose(drawCall.World, data.WorldMatrix);
const float scaleX = Vector3(drawCall.World.M11, drawCall.World.M12, drawCall.World.M13).Length();
const float scaleY = Vector3(drawCall.World.M21, drawCall.World.M22, drawCall.World.M23).Length();
const float scaleZ = Vector3(drawCall.World.M31, drawCall.World.M32, drawCall.World.M33).Length();
data.WorldInvScale = Vector3(
scaleX > 0.00001f ? 1.0f / scaleX : 0.0f,
scaleY > 0.00001f ? 1.0f / scaleY : 0.0f,
scaleZ > 0.00001f ? 1.0f / scaleZ : 0.0f);
data.LODDitherFactor = drawCall.Surface.LODDitherFactor;
const Color colors[MODEL_MAX_LODS] = {
Color::White,
Color::Red,
@@ -117,11 +120,10 @@ void LODPreviewMaterialShader::Bind(BindParameters& params)
};
ASSERT(lodIndex < MODEL_MAX_LODS);
data.Color = colors[lodIndex];
data.LODDitherFactor = drawCall.Surface.LODDitherFactor;
context->UpdateCB(cb, &data);
context->BindCB(0, cb);
}
context->SetState(_ps);
context->SetState(ps);
}
#endif
+3 -2
View File
@@ -6,8 +6,8 @@
#include "Engine/Content/AssetReference.h"
#include "Engine/Content/Assets/Shader.h"
#include "Engine/Content/Assets/Texture.h"
#include "Engine/Graphics/Materials/IMaterial.h"
#include "Engine/Graphics/GPUPipelineStatePermutations.h"
class GPUPipelineState;
@@ -19,7 +19,7 @@ class LODPreviewMaterialShader : public IMaterial
private:
AssetReference<Shader> _shader;
GPUPipelineState* _ps = nullptr;
GPUPipelineStatePermutations<2> _psModel;
MaterialInfo _info;
public:
@@ -40,6 +40,7 @@ public:
// [IMaterial]
const MaterialInfo& GetInfo() const override;
bool IsReady() const override;
bool CanUseInstancing(InstancingHandler& handler) const override;
DrawPass GetDrawModes() const override;
void Bind(BindParameters& params) override;
};
@@ -0,0 +1,228 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#if USE_EDITOR
#include "MaterialComplexity.h"
#include "Engine/Core/Types/Variant.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/Assets/Model.h"
#include "Engine/Profiler/Profiler.h"
#include "Engine/Level/Actors/Decal.h"
#include "Engine/Graphics/GPUContext.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Graphics/GPUPipelineState.h"
#include "Engine/Graphics/Shaders/GPUShader.h"
#include "Engine/Graphics/Textures/GPUTexture.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/RenderBuffers.h"
#include "Engine/Renderer/DrawCall.h"
#include "Engine/Renderer/RenderList.h"
#include "Engine/Renderer/Lightmaps.h"
// The limit for maximum material complexity (estimated based on shader textures, instructions and GPU stages usage).
#define COMPLEXITY_LIMIT 1700
const MaterialInfo& MaterialComplexityMaterialShader::WrapperShader::GetInfo() const
{
if (MaterialAsset)
return MaterialAsset->GetInfo();
return Info;
}
bool MaterialComplexityMaterialShader::WrapperShader::IsReady() const
{
return MaterialAsset && MaterialAsset->IsReady();
}
bool MaterialComplexityMaterialShader::WrapperShader::CanUseInstancing(InstancingHandler& handler) const
{
return MaterialAsset && MaterialAsset->CanUseInstancing(handler);
}
DrawPass MaterialComplexityMaterialShader::WrapperShader::GetDrawModes() const
{
return MaterialAsset->GetDrawModes();
}
void MaterialComplexityMaterialShader::WrapperShader::Bind(BindParameters& params)
{
auto& drawCall = *params.FirstDrawCall;
// Get original material from the draw call
IMaterial* material = nullptr;
switch (Domain)
{
case MaterialDomain::Surface:
case MaterialDomain::Terrain:
material = *(IMaterial**)&drawCall.Surface.Lightmap;
break;
case MaterialDomain::Particle:
material = *(IMaterial**)&drawCall.ObjectPosition;
break;
case MaterialDomain::Decal:
material = drawCall.Material;
break;
}
// Disable lightmaps
const auto lightmapsEnable = EnableLightmapsUsage;
EnableLightmapsUsage = false;
// Estimate the shader complexity
ASSERT_LOW_LAYER(material && material->IsReady());
material->Bind(params);
GPUPipelineState* materialPs = params.GPUContext->GetState();
const float complexity = (float)Math::Min(materialPs->Complexity, COMPLEXITY_LIMIT) / COMPLEXITY_LIMIT;
// Draw with custom color
const Color color(complexity, complexity, complexity, 1.0f);
MaterialAsset->SetParameterValue(TEXT("Color"), Variant(color));
MaterialAsset->Bind(params);
EnableLightmapsUsage = lightmapsEnable;
}
MaterialComplexityMaterialShader::MaterialComplexityMaterialShader()
{
_shader = Content::LoadAsyncInternal<Shader>(TEXT("Shaders/Editor/MaterialComplexity"));
// Initialize material wrappers table with separate materials for each material domain type and shader configuration
#define INIT_WRAPPER(i, domain, asset) _wrappers[i].Domain = MaterialDomain::domain; _wrappers[i].MaterialAsset = Content::LoadAsyncInternal<Material>(TEXT(asset))
INIT_WRAPPER(0, Surface, "Editor/DebugMaterials/Single Color Surface");
INIT_WRAPPER(1, Surface, "Editor/DebugMaterials/Single Color Surface Additive");
INIT_WRAPPER(2, Terrain, "Editor/DebugMaterials/Single Color Terrain");
INIT_WRAPPER(3, Particle, "Editor/DebugMaterials/Single Color Particle");
INIT_WRAPPER(4, Decal, "Editor/DebugMaterials/Single Color Decal");
// TODO: deformable splines rendering cost for complexity
// TODO: volumetric fog particles rendering cost for complexity
#undef INIT_WRAPPER
}
void MaterialComplexityMaterialShader::DebugOverrideDrawCallsMaterial(RenderContext& renderContext, GPUContext* context, GPUTextureView* lightBuffer)
{
// Cache 'ready' state for wrappers
bool isReady[ARRAY_COUNT(_wrappers) + 1];
for (int32 i = 0; i < ARRAY_COUNT(_wrappers); i++)
isReady[i] = _wrappers[i].IsReady();
isReady[ARRAY_COUNT(_wrappers)] = false;
// Override all draw calls
for (auto& e : renderContext.List->DrawCalls)
DebugOverrideDrawCallsMaterial(e, isReady);
for (auto& e : renderContext.List->BatchedDrawCalls)
DebugOverrideDrawCallsMaterial(e.DrawCall, isReady);
// Initialize background with complexity of the sky (uniform)
if (renderContext.List->Sky)
{
renderContext.List->Sky->ApplySky(context, renderContext, Matrix::Identity);
GPUPipelineState* materialPs = context->GetState();
const float complexity = (float)Math::Min(materialPs->Complexity, COMPLEXITY_LIMIT) / COMPLEXITY_LIMIT;
context->Clear(lightBuffer, Color(complexity, complexity, complexity, 1.0f));
renderContext.List->Sky = nullptr;
}
}
void MaterialComplexityMaterialShader::Draw(RenderContext& renderContext, GPUContext* context, GPUTextureView* lightBuffer)
{
// Draw decals into Light buffer to include them into complexity drawing
auto& decals = renderContext.List->Decals;
auto boxModel = Content::LoadAsyncInternal<Model>(TEXT("Engine/Models/SimpleBox"));
auto& decalsWrapper = _wrappers[4];
if (decals.HasItems() && boxModel && boxModel->CanBeRendered() && decalsWrapper.IsReady())
{
PROFILE_GPU_CPU("Decals");
DrawCall drawCall;
MaterialBase::BindParameters bindParams(context, renderContext, drawCall);
drawCall.WorldDeterminantSign = 1.0f;
context->SetRenderTarget(lightBuffer);
for (int32 i = 0; i < decals.Count(); i++)
{
const auto decal = decals[i];
ASSERT(decal && decal->Material);
decal->GetWorld(&drawCall.World);
drawCall.ObjectPosition = drawCall.World.GetTranslation();
drawCall.Material = decal->Material;
drawCall.PerInstanceRandom = decal->GetPerInstanceRandom();
decalsWrapper.Bind(bindParams);
boxModel->Render(context);
}
context->ResetSR();
}
// Draw transparency into Light buffer to include it into complexity drawing
context->SetRenderTarget(*renderContext.Buffers->DepthBuffer, lightBuffer);
auto& distortionList = renderContext.List->DrawCallsLists[(int32)DrawCallsListType::Distortion];
if (!distortionList.IsEmpty())
{
PROFILE_GPU_CPU("Distortion");
renderContext.View.Pass = DrawPass::Distortion;
renderContext.List->ExecuteDrawCalls(renderContext, distortionList);
}
auto& forwardList = renderContext.List->DrawCallsLists[(int32)DrawCallsListType::Forward];
if (!forwardList.IsEmpty())
{
PROFILE_GPU_CPU("Forward");
renderContext.View.Pass = DrawPass::Forward;
renderContext.List->ExecuteDrawCalls(renderContext, forwardList);
}
// Draw accumulated complexity into colors gradient
context->ResetRenderTarget();
context->SetRenderTarget(renderContext.Task->GetOutputView());
context->SetViewportAndScissors(renderContext.Task->GetOutputViewport());
if (_shader && _shader->IsLoaded())
{
if (!_ps)
{
_ps = GPUDevice::Instance->CreatePipelineState();
auto psDesc = GPUPipelineState::Description::DefaultFullscreenTriangle;
psDesc.PS = _shader->GetShader()->GetPS("PS");
_ps->Init(psDesc);
}
context->BindSR(0, lightBuffer);
context->SetState(_ps);
context->DrawFullscreenTriangle();
return;
}
context->Draw(lightBuffer);
}
void MaterialComplexityMaterialShader::DebugOverrideDrawCallsMaterial(DrawCall& drawCall, const bool isReady[ARRAY_COUNT(_wrappers) + 1])
{
auto domain = drawCall.Material->GetInfo().Domain;
auto wrapperIndex = ARRAY_COUNT(_wrappers);
switch (domain)
{
case MaterialDomain::Surface:
wrapperIndex = drawCall.Material->GetDrawModes() & DrawPass::Forward ? 1 : 0;
break;
case MaterialDomain::Terrain:
wrapperIndex = 2;
break;
case MaterialDomain::Particle:
wrapperIndex = 3;
break;
case MaterialDomain::Decal:
wrapperIndex = 4;
break;
}
if (isReady[wrapperIndex])
{
// Override draw call material and cache original material for later
switch (domain)
{
case MaterialDomain::Surface:
case MaterialDomain::Terrain:
*(void**)&drawCall.Surface.Lightmap = drawCall.Material;
break;
case MaterialDomain::Particle:
case MaterialDomain::Decal:
*(void**)&drawCall.ObjectPosition = drawCall.Material;
break;
}
drawCall.Material = &_wrappers[wrapperIndex];
}
}
#endif
@@ -0,0 +1,49 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#if USE_EDITOR
#include "Engine/Content/AssetReference.h"
#include "Engine/Content/Assets/Material.h"
#include "Engine/Graphics/Materials/IMaterial.h"
#include "Engine/Content/Assets/Shader.h"
class GPUPipelineState;
/// <summary>
/// Rendering material shaders complexity to visualize performance of pixels rendering in editor.
/// </summary>
class MaterialComplexityMaterialShader
{
private:
class WrapperShader : public IMaterial
{
public:
MaterialInfo Info;
MaterialDomain Domain;
AssetReference<Material> MaterialAsset;
const MaterialInfo& GetInfo() const override;
bool IsReady() const override;
bool CanUseInstancing(InstancingHandler& handler) const override;
DrawPass GetDrawModes() const override;
void Bind(BindParameters& params) override;
};
WrapperShader _wrappers[5];
AssetReference<Shader> _shader;
GPUPipelineState* _ps = nullptr;
public:
MaterialComplexityMaterialShader();
void DebugOverrideDrawCallsMaterial(RenderContext& renderContext, GPUContext* context, GPUTextureView* lightBuffer);
void Draw(RenderContext& renderContext, GPUContext* context, GPUTextureView* lightBuffer);
private:
void DebugOverrideDrawCallsMaterial(struct DrawCall& drawCall, const bool isReady[ARRAY_COUNT(_wrappers) + 1]);
};
#endif
+35 -4
View File
@@ -6,6 +6,7 @@
#include "Engine/Renderer/Editor/VertexColors.h"
#include "Engine/Renderer/Editor/LightmapUVsDensity.h"
#include "Engine/Renderer/Editor/LODPreview.h"
#include "Engine/Renderer/Editor/MaterialComplexity.h"
#endif
#include "Engine/Core/Collections/Sorting.h"
#include "Engine/Graphics/GPUDevice.h"
@@ -93,6 +94,7 @@ void GBufferPass::Dispose()
SAFE_DELETE(_lightmapUVsDensity);
SAFE_DELETE(_vertexColors);
SAFE_DELETE(_lodPreview);
SAFE_DELETE(_materialComplexity);
#endif
}
@@ -100,7 +102,12 @@ void GBufferPass::Dispose()
void DebugOverrideDrawCallsMaterial(RenderContext& renderContext, IMaterial* material)
{
if (material->IsReady())
if (!material->IsReady())
return;
IMaterial::InstancingHandler handler;
const bool canUseInstancing = material->CanUseInstancing(handler);
const auto drawModes = material->GetDrawModes();
if (drawModes & DrawPass::GBuffer)
{
auto& drawCallsList = renderContext.List->DrawCallsLists[(int32)DrawCallsListType::GBuffer];
for (int32 i : drawCallsList.Indices)
@@ -111,11 +118,20 @@ void DebugOverrideDrawCallsMaterial(RenderContext& renderContext, IMaterial* mat
drawCall.Material = material;
}
}
IMaterial::InstancingHandler handler;
if (!material->CanUseInstancing(handler))
drawCallsList.CanUseInstancing &= canUseInstancing;
}
if (drawModes & DrawPass::GBuffer)
{
auto& drawCallsList = renderContext.List->DrawCallsLists[(int32)DrawCallsListType::GBufferNoDecals];
for (int32 i : drawCallsList.Indices)
{
drawCallsList.CanUseInstancing = false;
auto& drawCall = renderContext.List->DrawCalls[i];
if (drawCall.Material->IsSurface())
{
drawCall.Material = material;
}
}
drawCallsList.CanUseInstancing &= canUseInstancing;
}
}
@@ -178,6 +194,12 @@ void GBufferPass::Fill(RenderContext& renderContext, GPUTextureView* lightBuffer
_lodPreview = New<LODPreviewMaterialShader>();
DebugOverrideDrawCallsMaterial(renderContext, _lodPreview);
}
else if (renderContext.View.Mode == ViewMode::MaterialComplexity)
{
if (!_materialComplexity)
_materialComplexity = New<MaterialComplexityMaterialShader>();
_materialComplexity->DebugOverrideDrawCallsMaterial(renderContext, context, lightBuffer);
}
if (renderContext.View.Mode == ViewMode::PhysicsColliders)
{
context->ResetRenderTarget();
@@ -258,6 +280,15 @@ void GBufferPass::RenderDebug(RenderContext& renderContext)
context->ResetSR();
}
#if USE_EDITOR
void GBufferPass::DrawMaterialComplexity(RenderContext& renderContext, GPUContext* context, GPUTextureView* lightBuffer)
{
_materialComplexity->Draw(renderContext, context, lightBuffer);
}
#endif
bool GBufferPass::IsDebugView(ViewMode mode)
{
switch (mode)
+5
View File
@@ -19,6 +19,7 @@ private:
class LightmapUVsDensityMaterialShader* _lightmapUVsDensity = nullptr;
class VertexColorsMaterialShader* _vertexColors = nullptr;
class LODPreviewMaterialShader* _lodPreview = nullptr;
class MaterialComplexityMaterialShader* _materialComplexity = nullptr;
#endif
public:
@@ -36,6 +37,10 @@ public:
/// <param name="renderContext">The rendering context.</param>
void RenderDebug(RenderContext& renderContext);
#if USE_EDITOR
void DrawMaterialComplexity(RenderContext& renderContext, GPUContext* context, GPUTextureView* lightBuffer);
#endif
public:
static bool IsDebugView(ViewMode mode);
+8 -1
View File
@@ -326,7 +326,6 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext)
// Check if debug emissive light
if (renderContext.View.Mode == ViewMode::Emissive || renderContext.View.Mode == ViewMode::LightmapUVsDensity)
{
// Render reflections debug view
context->ResetRenderTarget();
context->SetRenderTarget(task->GetOutputView());
context->SetViewportAndScissors(task->GetOutputViewport());
@@ -334,6 +333,14 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext)
RenderTargetPool::Release(lightBuffer);
return;
}
#if USE_EDITOR
if (renderContext.View.Mode == ViewMode::MaterialComplexity)
{
GBufferPass::Instance()->DrawMaterialComplexity(renderContext, context, lightBuffer->View());
RenderTargetPool::Release(lightBuffer);
return;
}
#endif
// Render motion vectors
MotionBlurPass::Instance()->RenderMotionVectors(renderContext);
@@ -0,0 +1,33 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "./Flax/Common.hlsl"
Texture2D Image : register(t0);
META_PS(true, FEATURE_LEVEL_ES2)
float4 PS(Quad_VS2PS input) : SV_Target
{
const float4 colors[4] = {
float4(0, 0.97f, 0.12f, 1),
float4(0.2f, 0.2f, 0.7f, 1),
float4(1, 0, 0, 1),
float4(1, 0.95f, 0.95f, 1)
};
const float weights[4] = {
0.0f,
0.5f,
0.8f,
1.0f
};
float complexity = saturate(Image.Sample(SamplerPointClamp, input.TexCoord).r);
float4 color;
if (complexity < weights[1])
color = lerp(colors[0], colors[1], complexity / weights[1]);
else if (complexity < weights[2])
color = lerp(colors[1], colors[2], (complexity - weights[1]) / (weights[2] - weights[1]));
else
color = lerp(colors[2], colors[3], (complexity - weights[2]) / (weights[3] - weights[2]));
return color;
}