Fixes for building editor for Linux

This commit is contained in:
mafiesto4
2020-12-23 21:54:21 +01:00
parent 429b71b63f
commit e4936547e3
11 changed files with 331 additions and 309 deletions

View File

@@ -8,13 +8,313 @@
#include "Engine/Graphics/RenderTargetPool.h"
#define STEPS_SLEEP_TIME 20
#define RUN_STEP(handler) handler(); if (checkBuildCancelled()) goto BUILDING_END; Platform::Sleep(STEPS_SLEEP_TIME)
#define RUN_STEP(handler) handler(); if (checkBuildCancelled()) return true; Platform::Sleep(STEPS_SLEEP_TIME)
bool ShadowsOfMordor::Builder::doWorkInner(DateTime buildStart)
{
#if HEMISPHERES_BAKE_STATE_SAVE
_lastStateSaveTime = DateTime::Now();
_firstStateSave = true;
// Try to load the state that was cached during hemispheres rendering (restore rendering in case of GPU driver crash)
if (loadState())
{
reportProgress(BuildProgressStep::RenderHemispheres, 0.0f);
const int32 firstScene = _workerActiveSceneIndex;
{
// Wait for lightmaps to be fully loaded
for (_workerActiveSceneIndex = 0; _workerActiveSceneIndex < _scenes.Count(); _workerActiveSceneIndex++)
{
if (_scenes[_workerActiveSceneIndex]->WaitForLightmaps())
{
LOG(Error, "Failed to load lightmap textures.");
_wasBuildCalled = false;
_isActive = false;
return true;
}
if (checkBuildCancelled())
return true;
}
// Continue the hemispheres rendering for the last scene from the cached position
{
_workerActiveSceneIndex = firstScene;
if (runStage(RenderHemispheres, false))
return true;
// Fill black holes with blurred data to prevent artifacts on the edges
_workerStagePosition0 = 0;
if (runStage(PostprocessLightmaps))
return true;
// Wait for GPU commands to sync
if (waitForJobDataSync())
return true;
// Update lightmaps textures
_scenes[_workerActiveSceneIndex]->UpdateLightmaps();
}
for (_workerActiveSceneIndex = firstScene + 1; _workerActiveSceneIndex < _scenes.Count(); _workerActiveSceneIndex++)
{
// Skip scenes without any lightmaps
if (_scenes[_workerActiveSceneIndex]->Lightmaps.IsEmpty())
continue;
// Clear hemispheres target
_workerStagePosition0 = 0;
if (runStage(ClearLightmapData))
return true;
// Render all registered Hemispheres rendering
_workerStagePosition0 = 0;
if (runStage(RenderHemispheres))
return true;
// Fill black holes with blurred data to prevent artifacts on the edges
_workerStagePosition0 = 0;
if (runStage(PostprocessLightmaps))
return true;
// Wait for GPU commands to sync
if (waitForJobDataSync())
return true;
// Update lightmaps textures
_scenes[_workerActiveSceneIndex]->UpdateLightmaps();
}
}
for (int32 bounce = _giBounceRunningIndex + 1; bounce < _bounceCount; bounce++)
{
_giBounceRunningIndex = bounce;
// Wait for lightmaps to be fully loaded
for (_workerActiveSceneIndex = 0; _workerActiveSceneIndex < _scenes.Count(); _workerActiveSceneIndex++)
{
if (_scenes[_workerActiveSceneIndex]->WaitForLightmaps())
{
LOG(Error, "Failed to load lightmap textures.");
_wasBuildCalled = false;
_isActive = false;
return true;
}
if (checkBuildCancelled())
return true;
}
// Render bounce for every scene separately
for (_workerActiveSceneIndex = firstScene; _workerActiveSceneIndex < _scenes.Count(); _workerActiveSceneIndex++)
{
// Skip scenes without any lightmaps
if (_scenes[_workerActiveSceneIndex]->Lightmaps.IsEmpty())
continue;
// Clear hemispheres target
_workerStagePosition0 = 0;
if (runStage(ClearLightmapData))
return true;
// Render all registered Hemispheres rendering
_workerStagePosition0 = 0;
if (runStage(RenderHemispheres))
return true;
// Fill black holes with blurred data to prevent artifacts on the edges
_workerStagePosition0 = 0;
if (runStage(PostprocessLightmaps))
return true;
// Wait for GPU commands to sync
if (waitForJobDataSync())
return true;
// Update lightmaps textures
_scenes[_workerActiveSceneIndex]->UpdateLightmaps();
}
}
reportProgress(BuildProgressStep::RenderHemispheres, 1.0f);
return true;
}
#endif
// Compute the final weight for integration
{
float weightSum = 0.0f;
for (uint32 y = 0; y < HEMISPHERES_RESOLUTION; y++)
{
const float v = (float(y) / float(HEMISPHERES_RESOLUTION)) * 2.0f - 1.0f;
for (uint32 x = 0; x < HEMISPHERES_RESOLUTION; x++)
{
const float u = (float(x) / float(HEMISPHERES_RESOLUTION)) * 2.0f - 1.0f;
const float t = 1.0f + u * u + v * v;
const float weight = 4.0f / (Math::Sqrt(t) * t);
weightSum += weight;
}
}
weightSum *= 6;
_hemisphereTexelsTotalWeight = (4.0f * PI) / weightSum;
}
// Initialize the lightmaps and pack entries to the charts
for (_workerActiveSceneIndex = 0; _workerActiveSceneIndex < _scenes.Count(); _workerActiveSceneIndex++)
{
RUN_STEP(cacheEntries);
RUN_STEP(generateCharts);
RUN_STEP(packCharts);
RUN_STEP(updateLightmaps);
RUN_STEP(updateEntries);
}
// TODO: if settings require wait for asset dependencies to all materials and models be loaded (maybe only for higher quality profiles)
// Generate hemispheres cache and prepare for baking
for (_workerActiveSceneIndex = 0; _workerActiveSceneIndex < _scenes.Count(); _workerActiveSceneIndex++)
{
// Wait for lightmaps to be fully loaded
if (_scenes[_workerActiveSceneIndex]->WaitForLightmaps())
{
LOG(Error, "Failed to load lightmap textures.");
_wasBuildCalled = false;
_isActive = false;
return true;
}
ASSERT(_cachePositions == nullptr && _cacheNormals == nullptr);
const int32 atlasSize = (int32)_scenes[_workerActiveSceneIndex]->GetSettings().AtlasSize;
auto tempDesc = GPUTextureDescription::New2D(atlasSize, atlasSize, HemispheresFormatToPixelFormat[CACHE_POSITIONS_FORMAT]);
_cachePositions = RenderTargetPool::Get(tempDesc);
tempDesc.Format = HemispheresFormatToPixelFormat[CACHE_NORMALS_FORMAT];
_cacheNormals = RenderTargetPool::Get(tempDesc);
if (_cachePositions == nullptr || _cacheNormals == nullptr)
return true;
generateHemispheres();
RenderTargetPool::Release(_cachePositions);
_cachePositions = nullptr;
RenderTargetPool::Release(_cacheNormals);
_cacheNormals = nullptr;
if (checkBuildCancelled())
return true;
Platform::Sleep(STEPS_SLEEP_TIME);
}
// Prepare before actual baking
int32 hemispheresCount = 0;
int32 mergedHemispheresCount = 0;
int32 bounceCount = 0;
int32 lightmapsCount = 0;
int32 entriesCount = 0;
for (int32 sceneIndex = 0; sceneIndex < _scenes.Count(); sceneIndex++)
{
auto& scene = *_scenes[sceneIndex];
hemispheresCount += scene.HemispheresCount;
mergedHemispheresCount += scene.MergedHemispheresCount;
lightmapsCount += scene.Lightmaps.Count();
entriesCount += scene.Entries.Count();
bounceCount = Math::Max(bounceCount, scene.GetSettings().BounceCount);
// Cleanup unused data to reduce memory usage
scene.Entries.Resize(0);
scene.Charts.Resize(0);
for (auto& lightmap : scene.Lightmaps)
lightmap.Entries.Resize(0);
}
_bounceCount = bounceCount;
LOG(Info, "Rendering {0} hemispheres in {1} bounce(s) (merged: {2})", hemispheresCount, bounceCount, mergedHemispheresCount);
if (bounceCount <= 0 || hemispheresCount <= 0)
{
LOG(Warning, "No data to render");
return true;
}
// For each bounce
for (int32 bounce = 0; bounce < _bounceCount; bounce++)
{
_giBounceRunningIndex = bounce;
// Wait for lightmaps to be fully loaded
for (_workerActiveSceneIndex = 0; _workerActiveSceneIndex < _scenes.Count(); _workerActiveSceneIndex++)
{
if (_scenes[_workerActiveSceneIndex]->WaitForLightmaps())
{
LOG(Error, "Failed to load lightmap textures.");
_wasBuildCalled = false;
_isActive = false;
return true;
}
if (checkBuildCancelled())
return true;
}
// Render bounce for every scene separately
for (_workerActiveSceneIndex = 0; _workerActiveSceneIndex < _scenes.Count(); _workerActiveSceneIndex++)
{
// Skip scenes without any lightmaps
if (_scenes[_workerActiveSceneIndex]->Lightmaps.IsEmpty())
continue;
// Clear hemispheres target
_workerStagePosition0 = 0;
if (runStage(ClearLightmapData))
return true;
// Render all registered Hemispheres rendering
_workerStagePosition0 = 0;
if (runStage(RenderHemispheres))
return true;
// Fill black holes with blurred data to prevent artifacts on the edges
_workerStagePosition0 = 0;
if (runStage(PostprocessLightmaps))
return true;
// Wait for GPU commands to sync
if (waitForJobDataSync())
return true;
// Update lightmaps textures
_scenes[_workerActiveSceneIndex]->UpdateLightmaps();
}
}
reportProgress(BuildProgressStep::RenderHemispheres, 1.0f);
#if DEBUG_EXPORT_HEMISPHERES_PREVIEW
for (int32 sceneIndex = 0; sceneIndex < _scenes.Count(); sceneIndex++)
downloadDebugHemisphereAtlases(_scenes[sceneIndex]);
#endif
// References:
// "Optimization of numerical calculations execution time in multiprocessor systems" - Wojciech Figat
// https://knarkowicz.wordpress.com/2014/07/20/lightmapping-in-anomaly-2-mobile/
// http://the-witness.net/news/2010/09/hemicube-rendering-and-integration/
// http://the-witness.net/news/2010/03/graphics-tech-texture-parameterization/
// http://the-witness.net/news/2010/03/graphics-tech-lighting-comparison/
// Some ideas:
// - render hemispheres to atlas or sth and batch integration and downscalling for multiply texels
// - use conservative rasterization for dx12 instead of blur or MSAA for all platforms
// - use hemisphere depth buffer to compute AO
// End
const int32 hemispheresRenderedCount = hemispheresCount * bounceCount;
DateTime buildEnd = DateTime::NowUTC();
LOG(Info, "Building lightmap finished! Time: {0}s, Lightmaps: {1}, Entries: {2}, Hemicubes rendered: {3}",
static_cast<int32>((buildEnd - buildStart).GetTotalSeconds()),
lightmapsCount,
entriesCount,
hemispheresRenderedCount);
return false;
}
int32 ShadowsOfMordor::Builder::doWork()
{
// Start
bool buildFailed = true;
DateTime buildEnd, buildStart = DateTime::NowUTC();
DateTime buildStart = DateTime::NowUTC();
_lastStep = BuildProgressStep::CacheEntries;
_lastStepStart = buildStart;
_hemispheresPerJob = HEMISPHERES_PER_JOB_MIN;
@@ -84,309 +384,14 @@ int32 ShadowsOfMordor::Builder::doWork()
}
}
// Run
IsBakingLightmaps = true;
#if HEMISPHERES_BAKE_STATE_SAVE
_lastStateSaveTime = DateTime::Now();
_firstStateSave = true;
// Try to load the state that was cached during hemispheres rendering (restore rendering in case of GPU driver crash)
if (loadState())
buildFailed = doWorkInner(buildStart);
if (buildFailed && !checkBuildCancelled())
{
reportProgress(BuildProgressStep::RenderHemispheres, 0.0f);
const int32 firstScene = _workerActiveSceneIndex;
{
// Wait for lightmaps to be fully loaded
for (_workerActiveSceneIndex = 0; _workerActiveSceneIndex < _scenes.Count(); _workerActiveSceneIndex++)
{
if (_scenes[_workerActiveSceneIndex]->WaitForLightmaps())
{
LOG(Error, "Failed to load lightmap textures.");
_wasBuildCalled = false;
_isActive = false;
OnBuildFinished(buildFailed);
return 0;
}
if (checkBuildCancelled())
goto BUILDING_END;
}
// Continue the hemispheres rendering for the last scene from the cached position
{
_workerActiveSceneIndex = firstScene;
if (runStage(RenderHemispheres, false))
goto BUILDING_END;
// Fill black holes with blurred data to prevent artifacts on the edges
_workerStagePosition0 = 0;
if (runStage(PostprocessLightmaps))
goto BUILDING_END;
// Wait for GPU commands to sync
if (waitForJobDataSync())
goto BUILDING_END;
// Update lightmaps textures
_scenes[_workerActiveSceneIndex]->UpdateLightmaps();
}
for (_workerActiveSceneIndex = firstScene + 1; _workerActiveSceneIndex < _scenes.Count(); _workerActiveSceneIndex++)
{
// Skip scenes without any lightmaps
if (_scenes[_workerActiveSceneIndex]->Lightmaps.IsEmpty())
continue;
// Clear hemispheres target
_workerStagePosition0 = 0;
if (runStage(ClearLightmapData))
goto BUILDING_END;
// Render all registered Hemispheres rendering
_workerStagePosition0 = 0;
if (runStage(RenderHemispheres))
goto BUILDING_END;
// Fill black holes with blurred data to prevent artifacts on the edges
_workerStagePosition0 = 0;
if (runStage(PostprocessLightmaps))
goto BUILDING_END;
// Wait for GPU commands to sync
if (waitForJobDataSync())
goto BUILDING_END;
// Update lightmaps textures
_scenes[_workerActiveSceneIndex]->UpdateLightmaps();
}
}
for (int32 bounce = _giBounceRunningIndex + 1; bounce < _bounceCount; bounce++)
{
_giBounceRunningIndex = bounce;
// Wait for lightmaps to be fully loaded
for (_workerActiveSceneIndex = 0; _workerActiveSceneIndex < _scenes.Count(); _workerActiveSceneIndex++)
{
if (_scenes[_workerActiveSceneIndex]->WaitForLightmaps())
{
LOG(Error, "Failed to load lightmap textures.");
_wasBuildCalled = false;
_isActive = false;
OnBuildFinished(buildFailed);
return 0;
}
if (checkBuildCancelled())
goto BUILDING_END;
}
// Render bounce for every scene separately
for (_workerActiveSceneIndex = firstScene; _workerActiveSceneIndex < _scenes.Count(); _workerActiveSceneIndex++)
{
// Skip scenes without any lightmaps
if (_scenes[_workerActiveSceneIndex]->Lightmaps.IsEmpty())
continue;
// Clear hemispheres target
_workerStagePosition0 = 0;
if (runStage(ClearLightmapData))
goto BUILDING_END;
// Render all registered Hemispheres rendering
_workerStagePosition0 = 0;
if (runStage(RenderHemispheres))
goto BUILDING_END;
// Fill black holes with blurred data to prevent artifacts on the edges
_workerStagePosition0 = 0;
if (runStage(PostprocessLightmaps))
goto BUILDING_END;
// Wait for GPU commands to sync
if (waitForJobDataSync())
goto BUILDING_END;
// Update lightmaps textures
_scenes[_workerActiveSceneIndex]->UpdateLightmaps();
}
}
reportProgress(BuildProgressStep::RenderHemispheres, 1.0f);
goto BUILDING_END;
OnBuildFinished(buildFailed);
return 0;
}
#endif
// Compute the final weight for integration
{
float weightSum = 0.0f;
for (uint32 y = 0; y < HEMISPHERES_RESOLUTION; y++)
{
const float v = (float(y) / float(HEMISPHERES_RESOLUTION)) * 2.0f - 1.0f;
for (uint32 x = 0; x < HEMISPHERES_RESOLUTION; x++)
{
const float u = (float(x) / float(HEMISPHERES_RESOLUTION)) * 2.0f - 1.0f;
const float t = 1.0f + u * u + v * v;
const float weight = 4.0f / (Math::Sqrt(t) * t);
weightSum += weight;
}
}
weightSum *= 6;
_hemisphereTexelsTotalWeight = (4.0f * PI) / weightSum;
}
// Initialize the lightmaps and pack entries to the charts
for (_workerActiveSceneIndex = 0; _workerActiveSceneIndex < _scenes.Count(); _workerActiveSceneIndex++)
{
RUN_STEP(cacheEntries);
RUN_STEP(generateCharts);
RUN_STEP(packCharts);
RUN_STEP(updateLightmaps);
RUN_STEP(updateEntries);
}
// TODO: if settings require wait for asset dependencies to all materials and models be loaded (maybe only for higher quality profiles)
// Generate hemispheres cache and prepare for baking
for (_workerActiveSceneIndex = 0; _workerActiveSceneIndex < _scenes.Count(); _workerActiveSceneIndex++)
{
// Wait for lightmaps to be fully loaded
if (_scenes[_workerActiveSceneIndex]->WaitForLightmaps())
{
LOG(Error, "Failed to load lightmap textures.");
_wasBuildCalled = false;
_isActive = false;
OnBuildFinished(buildFailed);
return 0;
}
ASSERT(_cachePositions == nullptr && _cacheNormals == nullptr);
const int32 atlasSize = (int32)_scenes[_workerActiveSceneIndex]->GetSettings().AtlasSize;
auto tempDesc = GPUTextureDescription::New2D(atlasSize, atlasSize, HemispheresFormatToPixelFormat[CACHE_POSITIONS_FORMAT]);
_cachePositions = RenderTargetPool::Get(tempDesc);
tempDesc.Format = HemispheresFormatToPixelFormat[CACHE_NORMALS_FORMAT];
_cacheNormals = RenderTargetPool::Get(tempDesc);
if (_cachePositions == nullptr || _cacheNormals == nullptr)
goto BUILDING_END;
generateHemispheres();
RenderTargetPool::Release(_cachePositions);
_cachePositions = nullptr;
RenderTargetPool::Release(_cacheNormals);
_cacheNormals = nullptr;
if (checkBuildCancelled())
goto BUILDING_END;
Platform::Sleep(STEPS_SLEEP_TIME);
}
// Prepare before actual baking
int32 hemispheresCount = 0;
int32 mergedHemispheresCount = 0;
int32 bounceCount = 0;
int32 lightmapsCount = 0;
int32 entriesCount = 0;
for (int32 sceneIndex = 0; sceneIndex < _scenes.Count(); sceneIndex++)
{
auto& scene = *_scenes[sceneIndex];
hemispheresCount += scene.HemispheresCount;
mergedHemispheresCount += scene.MergedHemispheresCount;
lightmapsCount += scene.Lightmaps.Count();
entriesCount += scene.Entries.Count();
bounceCount = Math::Max(bounceCount, scene.GetSettings().BounceCount);
// Cleanup unused data to reduce memory usage
scene.Entries.Resize(0);
scene.Charts.Resize(0);
for (auto& lightmap : scene.Lightmaps)
lightmap.Entries.Resize(0);
}
_bounceCount = bounceCount;
LOG(Info, "Rendering {0} hemispheres in {1} bounce(s) (merged: {2})", hemispheresCount, bounceCount, mergedHemispheresCount);
if (bounceCount <= 0 || hemispheresCount <= 0)
{
LOG(Warning, "No data to render");
goto BUILDING_END;
}
// For each bounce
for (int32 bounce = 0; bounce < _bounceCount; bounce++)
{
_giBounceRunningIndex = bounce;
// Wait for lightmaps to be fully loaded
for (_workerActiveSceneIndex = 0; _workerActiveSceneIndex < _scenes.Count(); _workerActiveSceneIndex++)
{
if (_scenes[_workerActiveSceneIndex]->WaitForLightmaps())
{
LOG(Error, "Failed to load lightmap textures.");
_wasBuildCalled = false;
_isActive = false;
OnBuildFinished(buildFailed);
return 0;
}
if (checkBuildCancelled())
goto BUILDING_END;
}
// Render bounce for every scene separately
for (_workerActiveSceneIndex = 0; _workerActiveSceneIndex < _scenes.Count(); _workerActiveSceneIndex++)
{
// Skip scenes without any lightmaps
if (_scenes[_workerActiveSceneIndex]->Lightmaps.IsEmpty())
continue;
// Clear hemispheres target
_workerStagePosition0 = 0;
if (runStage(ClearLightmapData))
goto BUILDING_END;
// Render all registered Hemispheres rendering
_workerStagePosition0 = 0;
if (runStage(RenderHemispheres))
goto BUILDING_END;
// Fill black holes with blurred data to prevent artifacts on the edges
_workerStagePosition0 = 0;
if (runStage(PostprocessLightmaps))
goto BUILDING_END;
// Wait for GPU commands to sync
if (waitForJobDataSync())
goto BUILDING_END;
// Update lightmaps textures
_scenes[_workerActiveSceneIndex]->UpdateLightmaps();
}
}
reportProgress(BuildProgressStep::RenderHemispheres, 1.0f);
#if DEBUG_EXPORT_HEMISPHERES_PREVIEW
for (int32 sceneIndex = 0; sceneIndex < _scenes.Count(); sceneIndex++)
downloadDebugHemisphereAtlases(_scenes[sceneIndex]);
#endif
// References:
// "Optimization of numerical calculations execution time in multiprocessor systems" - Wojciech Figat
// https://knarkowicz.wordpress.com/2014/07/20/lightmapping-in-anomaly-2-mobile/
// http://the-witness.net/news/2010/09/hemicube-rendering-and-integration/
// http://the-witness.net/news/2010/03/graphics-tech-texture-parameterization/
// http://the-witness.net/news/2010/03/graphics-tech-lighting-comparison/
// Some ideas:
// - render hemispheres to atlas or sth and batch integration and downscalling for multiply texels
// - use conservative rasterization for dx12 instead of blur or MSAA for all platforms
// - use hemisphere depth buffer to compute AO
// End
const int32 hemispheresRenderedCount = hemispheresCount * bounceCount;
buildEnd = DateTime::NowUTC();
LOG(Info, "Building lightmap finished! Time: {0}s, Lightmaps: {1}, Entries: {2}, Hemicubes rendered: {3}",
static_cast<int32>((buildEnd - buildStart).GetTotalSeconds()),
lightmapsCount,
entriesCount,
hemispheresRenderedCount);
buildFailed = false;
BUILDING_END:
// Cleanup cached data
reportProgress(BuildProgressStep::Cleanup, 0.0f);