Rework RenderPass allocations
Instead of managing a GrowingSlice<> more or less shared between several RenderPass, RenderPass now works directly with an Arena which owns the commands. We do this so RenderPass can be copiable, which is useful for creating a RenderPass from a "template" RenderPass (usually empty, but with some scene parameters set). RenderPass is now a lot more self contained. Execution of the commands is now done with a RenderPass::Execcutor which can be obtained from a RenderPass. This change led to some code clean-up as well. We also had to improve Arena<> to be able to take a custom Area.
This commit is contained in:
committed by
Mathias Agopian
parent
ca3b841974
commit
46a88c7ed8
@@ -312,10 +312,10 @@ void OpenGLDriver::setRasterStateSlow(RasterState rs) noexcept {
|
||||
// -- less than or equal to 208 bytes
|
||||
|
||||
|
||||
OpenGLDriver::HandleAllocator::HandleAllocator(const utils::HeapArea& area)
|
||||
OpenGLDriver::HandleAllocator::HandleAllocator(const utils::AreaPolicy::HeapArea& area)
|
||||
{
|
||||
// TODO: we probably need a better way to set the size of these pools
|
||||
const size_t unit = area.getSize() / 32;
|
||||
const size_t unit = area.size() / 32;
|
||||
const size_t offsetPool1 = unit;
|
||||
const size_t offsetPool2 = 16 * unit;
|
||||
char* const p = (char*)area.begin();
|
||||
|
||||
@@ -253,7 +253,7 @@ private:
|
||||
utils::PoolAllocator<208, 32> mPool2;
|
||||
public:
|
||||
static constexpr size_t MIN_ALIGNMENT_SHIFT = 4;
|
||||
explicit HandleAllocator(const utils::HeapArea& area);
|
||||
explicit HandleAllocator(const utils::AreaPolicy::HeapArea& area);
|
||||
|
||||
// this is in fact always called with a constexpr size argument
|
||||
inline void* alloc(size_t size, size_t alignment, size_t extra) noexcept {
|
||||
|
||||
@@ -299,7 +299,8 @@ void PostProcessManager::commitAndRender(FrameGraphResources::RenderPassInfo con
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
FrameGraphId<FrameGraphTexture> PostProcessManager::structure(FrameGraph& fg,
|
||||
const RenderPass& pass, uint32_t width, uint32_t height, float scale) noexcept {
|
||||
RenderPass const& pass, uint32_t width, uint32_t height, float scale) noexcept {
|
||||
|
||||
|
||||
// structure pass -- automatically culled if not used, currently used by:
|
||||
// - ssao
|
||||
@@ -332,7 +333,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::structure(FrameGraph& fg,
|
||||
.clearFlags = TargetBufferFlags::DEPTH
|
||||
});
|
||||
},
|
||||
[=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) {
|
||||
[=](FrameGraphResources const& resources,
|
||||
auto const& data, DriverApi& driver) mutable {
|
||||
auto out = resources.getRenderPassInfo();
|
||||
pass.execute(resources.getPassName(), out.target, out.params);
|
||||
});
|
||||
@@ -380,8 +382,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::structure(FrameGraph& fg,
|
||||
return depth;
|
||||
}
|
||||
|
||||
FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
|
||||
FrameGraph& fg, RenderPass& pass,
|
||||
FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(FrameGraph& fg,
|
||||
filament::Viewport const& svp, const CameraInfo& cameraInfo,
|
||||
View::AmbientOcclusionOptions options) noexcept {
|
||||
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
#include <fg2/FrameGraphId.h>
|
||||
#include <fg2/FrameGraphResources.h>
|
||||
|
||||
#include <filament/View.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/PipelineState.h>
|
||||
|
||||
#include <filament/View.h>
|
||||
|
||||
#include <utils/CString.h>
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
@@ -69,8 +69,7 @@ public:
|
||||
|
||||
// SSAO
|
||||
FrameGraphId<FrameGraphTexture> screenSpaceAmbientOcclusion(FrameGraph& fg,
|
||||
RenderPass& pass, filament::Viewport const& svp,
|
||||
CameraInfo const& cameraInfo,
|
||||
filament::Viewport const& svp, const CameraInfo& cameraInfo,
|
||||
View::AmbientOcclusionOptions options) noexcept;
|
||||
|
||||
// Used in refraction pass
|
||||
|
||||
@@ -40,16 +40,32 @@ namespace filament {
|
||||
using namespace backend;
|
||||
|
||||
RenderPass::RenderPass(FEngine& engine,
|
||||
GrowingSlice<RenderPass::Command> commands) noexcept
|
||||
: mEngine(engine), mCommands(commands),
|
||||
RenderPass::Arena& arena) noexcept
|
||||
: mEngine(engine), mCommandArena(arena),
|
||||
mCustomCommands(engine.getPerRenderPassAllocator()) {
|
||||
mCustomCommands.reserve(8); // preallocate allocate a reasonable number of custom commands
|
||||
}
|
||||
|
||||
RenderPass::RenderPass(RenderPass const& rhs) = default;
|
||||
|
||||
RenderPass::~RenderPass() noexcept = default;
|
||||
|
||||
RenderPass::Command* RenderPass::append(size_t count) noexcept {
|
||||
Command* const curr = mCommandArena.alloc<Command>(count);
|
||||
assert_invariant(mCommandBegin == nullptr || curr == mCommandEnd);
|
||||
if (mCommandBegin == nullptr) {
|
||||
mCommandBegin = mCommandEnd = curr;
|
||||
}
|
||||
mCommandEnd += count;
|
||||
return curr;
|
||||
}
|
||||
|
||||
void RenderPass::resize(size_t count) noexcept {
|
||||
if (mCommandBegin) {
|
||||
mCommandEnd = mCommandBegin + count;
|
||||
mCommandArena.rewind(mCommandEnd);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderPass::setGeometry(FScene::RenderableSoa const& soa, Range<uint32_t> vr,
|
||||
backend::Handle<backend::HwUniformBuffer> uboHandle) noexcept {
|
||||
mRenderableSoa = &soa;
|
||||
@@ -57,55 +73,42 @@ void RenderPass::setGeometry(FScene::RenderableSoa const& soa, Range<uint32_t> v
|
||||
mUboHandle = uboHandle;
|
||||
}
|
||||
|
||||
void RenderPass::setCamera(const CameraInfo& camera) noexcept {
|
||||
mCamera = camera;
|
||||
}
|
||||
|
||||
void RenderPass::setRenderFlags(RenderPass::RenderFlags flags) noexcept {
|
||||
mFlags = flags;
|
||||
}
|
||||
|
||||
void RenderPass::overridePolygonOffset(backend::PolygonOffset* polygonOffset) noexcept {
|
||||
if ((mPolygonOffsetOverride = (polygonOffset != nullptr))) {
|
||||
mPolygonOffset = *polygonOffset;
|
||||
}
|
||||
}
|
||||
|
||||
RenderPass::Command* RenderPass::newCommandBuffer() noexcept {
|
||||
GrowingSlice<Command>& commands = mCommands;
|
||||
commands = GrowingSlice<Command>(commands.end(), commands.capacity() - commands.size());
|
||||
return commands.begin();
|
||||
}
|
||||
|
||||
RenderPass::Command* RenderPass::appendCommands(CommandTypeFlags const commandTypeFlags) noexcept {
|
||||
void RenderPass::appendCommands(CommandTypeFlags const commandTypeFlags) noexcept {
|
||||
SYSTRACE_CONTEXT();
|
||||
|
||||
assert_invariant(mRenderableSoa);
|
||||
|
||||
utils::Range<uint32_t> vr = mVisibleRenderables;
|
||||
// trace the number of visible renderables
|
||||
SYSTRACE_VALUE32("visibleRenderables", vr.size());
|
||||
if (UTILS_UNLIKELY(vr.empty())) {
|
||||
return;
|
||||
}
|
||||
|
||||
FEngine& engine = mEngine;
|
||||
JobSystem& js = engine.getJobSystem();
|
||||
GrowingSlice<Command>& commands = mCommands;
|
||||
const RenderFlags renderFlags = mFlags;
|
||||
const FScene::VisibleMaskType visibilityMask = mVisibilityMask;
|
||||
CameraInfo const& camera = mCamera;
|
||||
utils::Range<uint32_t> vr = mVisibleRenderables;
|
||||
if (UTILS_UNLIKELY(vr.empty())) {
|
||||
return commands.end();
|
||||
}
|
||||
assert_invariant(mRenderableSoa);
|
||||
|
||||
// trace the number of visible renderables
|
||||
SYSTRACE_VALUE32("visibleRenderables", vr.size());
|
||||
|
||||
// up-to-date summed primitive counts needed for generateCommands()
|
||||
FScene::RenderableSoa const& soa = *mRenderableSoa;
|
||||
updateSummedPrimitiveCounts(const_cast<FScene::RenderableSoa&>(soa), vr);
|
||||
|
||||
// compute how much maximum storage we need for this pass
|
||||
uint32_t growBy = FScene::getPrimitiveCount(soa, vr.last);
|
||||
uint32_t commandCount = FScene::getPrimitiveCount(soa, vr.last);
|
||||
// double the color pass for transparent objects that need to render twice
|
||||
const bool colorPass = bool(commandTypeFlags & CommandTypeFlags::COLOR);
|
||||
const bool depthPass = bool(commandTypeFlags & CommandTypeFlags::DEPTH);
|
||||
growBy *= uint32_t(colorPass * 2 + depthPass);
|
||||
Command* const curr = commands.grow(growBy);
|
||||
commandCount *= uint32_t(colorPass * 2 + depthPass);
|
||||
commandCount += 1; // for the sentinel
|
||||
Command* const curr = append(commandCount);
|
||||
|
||||
// we extract camera position/forward outside of the loop, because these are not cheap.
|
||||
const float3 cameraPosition(camera.getPosition());
|
||||
@@ -129,14 +132,10 @@ RenderPass::Command* RenderPass::appendCommands(CommandTypeFlags const commandTy
|
||||
// always add an "eof" command
|
||||
// "eof" command. these commands are guaranteed to be sorted last in the
|
||||
// command buffer.
|
||||
commands.grow(1)->key = uint64_t(Pass::SENTINEL);
|
||||
|
||||
mCommandsHighWatermark = std::max(mCommandsHighWatermark, size_t(commands.size()));
|
||||
|
||||
return commands.end();
|
||||
curr[commandCount - 1].key = uint64_t(Pass::SENTINEL);
|
||||
}
|
||||
|
||||
RenderPass::Command* RenderPass::appendCustomCommand(Pass pass, CustomCommand custom, uint32_t order,
|
||||
void RenderPass::appendCustomCommand(Pass pass, CustomCommand custom, uint32_t order,
|
||||
std::function<void()> command) {
|
||||
|
||||
assert((uint64_t(order) << CUSTOM_ORDER_SHIFT) <= CUSTOM_ORDER_MASK);
|
||||
@@ -149,103 +148,22 @@ RenderPass::Command* RenderPass::appendCustomCommand(Pass pass, CustomCommand cu
|
||||
cmd |= uint64_t(order) << CUSTOM_ORDER_SHIFT;
|
||||
cmd |= uint64_t(index);
|
||||
|
||||
Command* const curr = mCommands.grow(1);
|
||||
Command* const curr = append(1);
|
||||
curr->key = cmd;
|
||||
return curr + 1;
|
||||
}
|
||||
|
||||
RenderPass::Command* RenderPass::sortCommands() noexcept {
|
||||
void RenderPass::sortCommands() noexcept {
|
||||
SYSTRACE_NAME("sort and trim commands");
|
||||
|
||||
GrowingSlice<Command>& commands = mCommands;
|
||||
|
||||
std::sort(commands.begin(), commands.end());
|
||||
std::sort(mCommandBegin, mCommandEnd);
|
||||
|
||||
// find the last command
|
||||
Command const* const last = std::partition_point(commands.begin(), commands.end(),
|
||||
Command const* const last = std::partition_point(mCommandBegin, mCommandEnd,
|
||||
[](Command const& c) {
|
||||
return c.key != uint64_t(Pass::SENTINEL);
|
||||
});
|
||||
|
||||
commands.resize(uint32_t(last - commands.begin()));
|
||||
|
||||
return commands.end();
|
||||
}
|
||||
|
||||
void RenderPass::execute(const char* name,
|
||||
backend::Handle<backend::HwRenderTarget> renderTarget,
|
||||
backend::RenderPassParams params) const noexcept {
|
||||
FEngine& engine = mEngine;
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
driver.beginRenderPass(renderTarget, params);
|
||||
executeCommands(name);
|
||||
driver.endRenderPass();
|
||||
}
|
||||
|
||||
void RenderPass::executeCommands(const char* name) const noexcept {
|
||||
// this is a good time to flush the CommandStream, because we're about to potentially
|
||||
// output a lot of commands. This guarantees here that we have at least
|
||||
// FILAMENT_MIN_COMMAND_BUFFERS_SIZE_IN_MB bytes (1MiB by default).
|
||||
FEngine& engine = mEngine;
|
||||
engine.flush();
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
RenderPass::recordDriverCommands(driver, mCommands.begin(), mCommands.end());
|
||||
}
|
||||
|
||||
UTILS_NOINLINE // no need to be inlined
|
||||
void RenderPass::recordDriverCommands(FEngine::DriverApi& driver, const Command* first,
|
||||
const Command* last) const noexcept {
|
||||
SYSTRACE_CALL();
|
||||
|
||||
if (first != last) {
|
||||
SYSTRACE_VALUE32("commandCount", last - first);
|
||||
|
||||
PolygonOffset dummyPolyOffset;
|
||||
PipelineState pipeline{ .polygonOffset = mPolygonOffset };
|
||||
PolygonOffset* const pPipelinePolygonOffset =
|
||||
mPolygonOffsetOverride ? &dummyPolyOffset : &pipeline.polygonOffset;
|
||||
|
||||
Handle<HwUniformBuffer> uboHandle = mUboHandle;
|
||||
FMaterialInstance const* UTILS_RESTRICT mi = nullptr;
|
||||
FMaterial const* UTILS_RESTRICT ma = nullptr;
|
||||
auto const& customCommands = mCustomCommands;
|
||||
|
||||
first--;
|
||||
while (++first != last) {
|
||||
/*
|
||||
* Be careful when changing code below, this is the hot inner-loop
|
||||
*/
|
||||
|
||||
if (UTILS_UNLIKELY((first->key & CUSTOM_MASK) != uint64_t(CustomCommand::PASS))) {
|
||||
uint32_t index = (first->key & CUSTOM_INDEX_MASK) >> CUSTOM_INDEX_SHIFT;
|
||||
customCommands[index]();
|
||||
continue;
|
||||
}
|
||||
|
||||
// per-renderable uniform
|
||||
const PrimitiveInfo info = first->primitive;
|
||||
pipeline.rasterState = info.rasterState;
|
||||
if (UTILS_UNLIKELY(mi != info.mi)) {
|
||||
// this is always taken the first time
|
||||
mi = info.mi;
|
||||
ma = mi->getMaterial();
|
||||
pipeline.scissor = mi->getScissor();
|
||||
*pPipelinePolygonOffset = mi->getPolygonOffset();
|
||||
mi->use(driver);
|
||||
}
|
||||
|
||||
pipeline.program = ma->getProgram(info.materialVariant.key);
|
||||
size_t offset = info.index * sizeof(PerRenderableUib);
|
||||
driver.bindUniformBufferRange(BindingPoints::PER_RENDERABLE,
|
||||
uboHandle, offset, sizeof(PerRenderableUib));
|
||||
if (UTILS_UNLIKELY(info.perRenderableBones)) {
|
||||
driver.bindUniformBuffer(BindingPoints::PER_RENDERABLE_BONES,
|
||||
info.perRenderableBones);
|
||||
}
|
||||
driver.draw(pipeline, info.primitiveHandle);
|
||||
}
|
||||
mCustomCommands.clear();
|
||||
}
|
||||
resize(uint32_t(last - mCommandBegin));
|
||||
}
|
||||
|
||||
/* static */
|
||||
@@ -582,4 +500,82 @@ void RenderPass::updateSummedPrimitiveCounts(
|
||||
summedPrimitiveCount[vr.last] = count;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void RenderPass::Executor::execute(const char* name,
|
||||
backend::Handle<backend::HwRenderTarget> renderTarget,
|
||||
backend::RenderPassParams params) const noexcept {
|
||||
FEngine& engine = mEngine;
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
driver.beginRenderPass(renderTarget, params);
|
||||
executeCommands(name);
|
||||
driver.endRenderPass();
|
||||
}
|
||||
|
||||
void RenderPass::Executor::executeCommands(const char* name) const noexcept {
|
||||
// this is a good time to flush the CommandStream, because we're about to potentially
|
||||
// output a lot of commands. This guarantees here that we have at least
|
||||
// FILAMENT_MIN_COMMAND_BUFFERS_SIZE_IN_MB bytes (1MiB by default).
|
||||
FEngine& engine = mEngine;
|
||||
engine.flush();
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
recordDriverCommands(driver, mBegin, mEnd);
|
||||
}
|
||||
|
||||
UTILS_NOINLINE // no need to be inlined
|
||||
void RenderPass::Executor::recordDriverCommands(backend::DriverApi& driver,
|
||||
const Command* first, const Command* last) const noexcept {
|
||||
SYSTRACE_CALL();
|
||||
|
||||
if (first != last) {
|
||||
SYSTRACE_VALUE32("commandCount", last - first);
|
||||
|
||||
PolygonOffset dummyPolyOffset;
|
||||
PipelineState pipeline{ .polygonOffset = mPolygonOffset };
|
||||
PolygonOffset* const pPipelinePolygonOffset =
|
||||
mPolygonOffsetOverride ? &dummyPolyOffset : &pipeline.polygonOffset;
|
||||
|
||||
Handle<HwUniformBuffer> uboHandle = mUboHandle;
|
||||
FMaterialInstance const* UTILS_RESTRICT mi = nullptr;
|
||||
FMaterial const* UTILS_RESTRICT ma = nullptr;
|
||||
auto const& customCommands = mCustomCommands;
|
||||
|
||||
first--;
|
||||
while (++first != last) {
|
||||
/*
|
||||
* Be careful when changing code below, this is the hot inner-loop
|
||||
*/
|
||||
|
||||
if (UTILS_UNLIKELY((first->key & CUSTOM_MASK) != uint64_t(CustomCommand::PASS))) {
|
||||
uint32_t index = (first->key & CUSTOM_INDEX_MASK) >> CUSTOM_INDEX_SHIFT;
|
||||
customCommands[index]();
|
||||
continue;
|
||||
}
|
||||
|
||||
// per-renderable uniform
|
||||
const PrimitiveInfo info = first->primitive;
|
||||
pipeline.rasterState = info.rasterState;
|
||||
if (UTILS_UNLIKELY(mi != info.mi)) {
|
||||
// this is always taken the first time
|
||||
mi = info.mi;
|
||||
ma = mi->getMaterial();
|
||||
pipeline.scissor = mi->getScissor();
|
||||
*pPipelinePolygonOffset = mi->getPolygonOffset();
|
||||
mi->use(driver);
|
||||
}
|
||||
|
||||
pipeline.program = ma->getProgram(info.materialVariant.key);
|
||||
size_t offset = info.index * sizeof(PerRenderableUib);
|
||||
driver.bindUniformBufferRange(BindingPoints::PER_RENDERABLE,
|
||||
uboHandle, offset, sizeof(PerRenderableUib));
|
||||
if (UTILS_UNLIKELY(info.perRenderableBones)) {
|
||||
driver.bindUniformBuffer(BindingPoints::PER_RENDERABLE_BONES,
|
||||
info.perRenderableBones);
|
||||
}
|
||||
driver.draw(pipeline, info.primitiveHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -17,30 +17,91 @@
|
||||
#ifndef TNT_UTILS_RENDERPASS_H
|
||||
#define TNT_UTILS_RENDERPASS_H
|
||||
|
||||
#include <filament/Viewport.h>
|
||||
|
||||
#include "details/Allocators.h"
|
||||
#include "details/Camera.h"
|
||||
#include "details/Material.h"
|
||||
#include "details/Scene.h"
|
||||
|
||||
#include "private/backend/DriverApiForward.h"
|
||||
|
||||
#include <private/filament/Variant.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include <utils/architecture.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Slice.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Allocator.h>
|
||||
#include <utils/Range.h>
|
||||
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
|
||||
namespace utils {
|
||||
class JobSystem;
|
||||
}
|
||||
#include <vector>
|
||||
|
||||
namespace filament {
|
||||
|
||||
class FMaterialInstance;
|
||||
|
||||
class RenderPass {
|
||||
public:
|
||||
/*
|
||||
* Command key encoding
|
||||
* --------------------
|
||||
*
|
||||
* a = alpha masking
|
||||
* ppp = priority
|
||||
* t = two-pass transparency ordering
|
||||
* 0 = reserved, must be zero
|
||||
*
|
||||
* DEPTH command
|
||||
* | 6 | 2| 2|1| 3 | 2| 16 | 32 |
|
||||
* +------+--+--+-+---+--+----------------+--------------------------------+
|
||||
* |000000|01|00|0|ppp|00|0000000000000000| distanceBits |
|
||||
* +------+--+--+-+---+-------------------+--------------------------------+
|
||||
* | correctness | optimizations (truncation allowed) |
|
||||
*
|
||||
*
|
||||
* COLOR command
|
||||
* | 6 | 2| 2|1| 3 | 2| 6 | 10 | 32 |
|
||||
* +------+--+--+-+---+--+------+----------+--------------------------------+
|
||||
* |000001|01|00|a|ppp|00|000000| Z-bucket | material-id |
|
||||
* |000010|01|00|a|ppp|00|000000| Z-bucket | material-id | refraction
|
||||
* +------+--+--+-+---+--+------+----------+--------------------------------+
|
||||
* | correctness | optimizations (truncation allowed) |
|
||||
*
|
||||
*
|
||||
* BLENDED command
|
||||
* | 6 | 2| 2|1| 3 | 2| 32 | 15 |1|
|
||||
* +------+--+--+-+---+--+--------------------------------+---------------+-+
|
||||
* |000011|01|00|0|ppp|00| ~distanceBits | blendOrder |t|
|
||||
* +------+--+--+-+---+--+--------------------------------+---------------+-+
|
||||
* | correctness |
|
||||
*
|
||||
*
|
||||
* pre-CUSTOM command
|
||||
* | 6 | 2| 2| 22 | 32 |
|
||||
* +------+--+--+----------------------+--------------------------------+
|
||||
* | pass |00|00| order | custom command index |
|
||||
* +------+--+--+----------------------+--------------------------------+
|
||||
* | correctness |
|
||||
*
|
||||
*
|
||||
* post-CUSTOM command
|
||||
* | 6 | 2| 2| 22 | 32 |
|
||||
* +------+--+--+----------------------+--------------------------------+
|
||||
* | pass |11|00| order | custom command index |
|
||||
* +------+--+--+----------------------+--------------------------------+
|
||||
* | correctness |
|
||||
*
|
||||
*
|
||||
* SENTINEL command
|
||||
* | 64 |
|
||||
* +--------.--------.--------.--------.--------.--------.--------.--------+
|
||||
* |11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111|
|
||||
* +-----------------------------------------------------------------------+
|
||||
*/
|
||||
using CommandKey = uint64_t;
|
||||
|
||||
static constexpr uint64_t DISTANCE_BITS_MASK = 0xFFFFFFFFllu;
|
||||
static constexpr unsigned DISTANCE_BITS_SHIFT = 0;
|
||||
|
||||
@@ -119,74 +180,16 @@ public:
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Command key encoding
|
||||
// --------------------
|
||||
//
|
||||
// a = alpha masking
|
||||
// ppp = priority
|
||||
// t = two-pass transparency ordering
|
||||
// 0 = reserved, must be zero
|
||||
//
|
||||
// DEPTH command
|
||||
// | 6 | 2| 2|1| 3 | 2| 16 | 32 |
|
||||
// +------+--+--+-+---+--+----------------+--------------------------------+
|
||||
// |000000|01|00|0|ppp|00|0000000000000000| distanceBits |
|
||||
// +------+--+--+-+---+-------------------+--------------------------------+
|
||||
// | correctness | optimizations (truncation allowed) |
|
||||
//
|
||||
//
|
||||
// COLOR command
|
||||
// | 6 | 2| 2|1| 3 | 2| 6 | 10 | 32 |
|
||||
// +------+--+--+-+---+--+------+----------+--------------------------------+
|
||||
// |000001|01|00|a|ppp|00|000000| Z-bucket | material-id |
|
||||
// |000010|01|00|a|ppp|00|000000| Z-bucket | material-id | refraction
|
||||
// +------+--+--+-+---+--+------+----------+--------------------------------+
|
||||
// | correctness | optimizations (truncation allowed) |
|
||||
//
|
||||
//
|
||||
// BLENDED command
|
||||
// | 6 | 2| 2|1| 3 | 2| 32 | 15 |1|
|
||||
// +------+--+--+-+---+--+--------------------------------+---------------+-+
|
||||
// |000011|01|00|0|ppp|00| ~distanceBits | blendOrder |t|
|
||||
// +------+--+--+-+---+--+--------------------------------+---------------+-+
|
||||
// | correctness |
|
||||
//
|
||||
//
|
||||
// pre-CUSTOM command
|
||||
// | 6 | 2| 2| 22 | 32 |
|
||||
// +------+--+--+----------------------+--------------------------------+
|
||||
// | pass |00|00| order | custom command index |
|
||||
// +------+--+--+----------------------+--------------------------------+
|
||||
// | correctness |
|
||||
//
|
||||
//
|
||||
// post-CUSTOM command
|
||||
// | 6 | 2| 2| 22 | 32 |
|
||||
// +------+--+--+----------------------+--------------------------------+
|
||||
// | pass |11|00| order | custom command index |
|
||||
// +------+--+--+----------------------+--------------------------------+
|
||||
// | correctness |
|
||||
//
|
||||
//
|
||||
// SENTINEL command
|
||||
// | 64 |
|
||||
// +--------.--------.--------.--------.--------.--------.--------.--------+
|
||||
// |11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111|
|
||||
// +-----------------------------------------------------------------------+
|
||||
//
|
||||
using CommandKey = uint64_t;
|
||||
|
||||
|
||||
// The sorting material key is 32 bits and encoded as:
|
||||
//
|
||||
// | 12 | 8 | 12 |
|
||||
// +------------+--------+------------+
|
||||
// | material |variant | instance |
|
||||
// +------------+--------+------------+
|
||||
//
|
||||
// The variant is inserted while building the commands, because we don't know it before that
|
||||
//
|
||||
/*
|
||||
* The sorting material key is 32 bits and encoded as:
|
||||
*
|
||||
* | 12 | 8 | 12 |
|
||||
* +------------+--------+------------+
|
||||
* | material |variant | instance |
|
||||
* +------------+--------+------------+
|
||||
*
|
||||
* The variant is inserted while building the commands, because we don't know it before that
|
||||
*/
|
||||
static CommandKey makeMaterialSortingKey(uint32_t materialId, uint32_t instanceId) noexcept {
|
||||
CommandKey key = ((materialId << MATERIAL_ID_SHIFT) & MATERIAL_ID_MASK) |
|
||||
((instanceId << MATERIAL_INSTANCE_ID_SHIFT) & MATERIAL_INSTANCE_ID_MASK);
|
||||
@@ -199,15 +202,9 @@ public:
|
||||
return uint64_t(value) << shift;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static CommandKey makeFieldTruncate(T value, uint64_t mask, unsigned shift) noexcept {
|
||||
return (uint64_t(value) << shift) & mask;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
static CommandKey select(T boolish) noexcept {
|
||||
return boolish ? -1llu : 0llu;
|
||||
return boolish ? std::numeric_limits<uint64_t>::max() : uint64_t(0);
|
||||
}
|
||||
|
||||
struct PrimitiveInfo { // 24 bytes
|
||||
@@ -219,7 +216,6 @@ public:
|
||||
Variant materialVariant; // 1 byte
|
||||
uint8_t reserved = {}; // 1 byte
|
||||
};
|
||||
|
||||
static_assert(sizeof(PrimitiveInfo) == sizeof(void*) + 16);
|
||||
|
||||
struct alignas(8) Command { // 32 bytes
|
||||
@@ -227,15 +223,13 @@ public:
|
||||
PrimitiveInfo primitive; // 24 bytes
|
||||
bool operator < (Command const& rhs) const noexcept { return key < rhs.key; }
|
||||
// placement new declared as "throw" to avoid the compiler's null-check
|
||||
inline void* operator new (std::size_t size, void* ptr) {
|
||||
inline void* operator new (std::size_t, void* ptr) {
|
||||
assert_invariant(ptr);
|
||||
return ptr;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(Command) == 32);
|
||||
|
||||
static_assert(std::is_trivially_destructible<Command>::value,
|
||||
static_assert(std::is_trivially_destructible_v<Command>,
|
||||
"Command isn't trivially destructible");
|
||||
|
||||
using RenderFlags = uint8_t;
|
||||
@@ -246,62 +240,118 @@ public:
|
||||
static constexpr RenderFlags HAS_FOG = 0x10;
|
||||
static constexpr RenderFlags HAS_VSM = 0x20;
|
||||
|
||||
// Arena used for commands
|
||||
using Arena = utils::Arena<
|
||||
utils::LinearAllocator,
|
||||
utils::LockingPolicy::NoLock,
|
||||
utils::TrackingPolicy::HighWatermark,
|
||||
utils::AreaPolicy::StaticArea>;
|
||||
|
||||
RenderPass(FEngine& engine, utils::GrowingSlice<Command> commands) noexcept;
|
||||
/*
|
||||
* Create a RenderPass.
|
||||
* The Arena is used to allocate commands which are then owned by the Arena.
|
||||
*/
|
||||
RenderPass(FEngine& engine, Arena& arena) noexcept;
|
||||
|
||||
// Copy the RenderPass as is. This can be used to create a RenderPass from a "template"
|
||||
// by copying from an "empty" RenderPass.
|
||||
RenderPass(RenderPass const& rhs);
|
||||
|
||||
// allocated commands ARE NOT freed, they're owned by the Arena
|
||||
~RenderPass() noexcept;
|
||||
|
||||
// if non-null, overrides the material's polygon offset
|
||||
void overridePolygonOffset(backend::PolygonOffset* polygonOffset) noexcept;
|
||||
|
||||
// specifies the geometry to generate commands for
|
||||
void setGeometry(FScene::RenderableSoa const& soa, utils::Range<uint32_t> vr,
|
||||
backend::Handle<backend::HwUniformBuffer> uboHandle) noexcept;
|
||||
void setCamera(const CameraInfo& camera) noexcept;
|
||||
void setRenderFlags(RenderFlags flags) noexcept;
|
||||
|
||||
// specifies camera information (e.g. used for sorting commands)
|
||||
void setCamera(const CameraInfo& camera) noexcept { mCamera = camera; }
|
||||
|
||||
// flags controling how commands are generated
|
||||
void setRenderFlags(RenderFlags flags) noexcept { mFlags = flags; }
|
||||
|
||||
// Sets the visibility mask, which is AND-ed against each Renderable's VISIBLE_MASK to determine
|
||||
// if the renderable is visible for this pass.
|
||||
// Defaults to all 1's, which means all renderables in this render pass will be rendered.
|
||||
void setVisibilityMask(FScene::VisibleMaskType mask) noexcept { mVisibilityMask = mask; }
|
||||
|
||||
// Resets the visibility mask to the default value of all 1's.
|
||||
void clearVisibilityMask() noexcept {
|
||||
mVisibilityMask = std::numeric_limits<FScene::VisibleMaskType>::max();
|
||||
}
|
||||
Command const* begin() const noexcept { return mCommandBegin; }
|
||||
Command const* end() const noexcept { return mCommandEnd; }
|
||||
|
||||
Command* begin() noexcept { return mCommands.begin(); }
|
||||
Command* end() noexcept { return mCommands.end(); }
|
||||
// This is the main function of this class, this appends commands to the pass using
|
||||
// the current camera, geometry and flags set. This can be called multiple times if needed.
|
||||
void appendCommands(CommandTypeFlags commandTypeFlags) noexcept;
|
||||
|
||||
Command const* begin() const noexcept { return mCommands.begin(); }
|
||||
Command const* end() const noexcept { return mCommands.end(); }
|
||||
|
||||
Command* newCommandBuffer() noexcept;
|
||||
|
||||
// returns mCommands.end()
|
||||
Command* appendCommands(CommandTypeFlags commandTypeFlags) noexcept;
|
||||
|
||||
// returns mCommands.end()
|
||||
Command* appendCustomCommand(Pass pass, CustomCommand custom, uint32_t order,
|
||||
// Appends a custom command.
|
||||
void appendCustomCommand(Pass pass, CustomCommand custom, uint32_t order,
|
||||
std::function<void()> command);
|
||||
|
||||
// sorts commands, then trims sentinels and returns
|
||||
// the new mCommands.end()
|
||||
Command* sortCommands() noexcept;
|
||||
// sorts commands, then trims sentinels
|
||||
void sortCommands() noexcept;
|
||||
|
||||
// Helper to execute all the commands generated by this RenderPass
|
||||
void execute(const char* name,
|
||||
backend::Handle<backend::HwRenderTarget> renderTarget,
|
||||
backend::RenderPassParams params) const noexcept;
|
||||
backend::RenderPassParams params) const noexcept {
|
||||
getExecutor().execute(name, renderTarget, params);
|
||||
}
|
||||
|
||||
void executeCommands(const char* name) const noexcept;
|
||||
/*
|
||||
* Executor holds the range of commands to execute for a given pass
|
||||
*/
|
||||
class Executor {
|
||||
using CustomCommandFn = std::function<void()>;
|
||||
using CustomCommandVector = std::vector<CustomCommandFn,
|
||||
utils::STLAllocator<CustomCommandFn, LinearAllocatorArena>>;
|
||||
|
||||
utils::GrowingSlice<Command>& getCommands() { return mCommands; }
|
||||
utils::Slice<Command> const& getCommands() const { return mCommands; }
|
||||
friend class RenderPass;
|
||||
FEngine& mEngine;
|
||||
Command const* mBegin;
|
||||
Command const* mEnd;
|
||||
const CustomCommandVector mCustomCommands;
|
||||
const backend::Handle<backend::HwUniformBuffer> mUboHandle;
|
||||
const backend::PolygonOffset mPolygonOffset;
|
||||
const bool mPolygonOffsetOverride;
|
||||
|
||||
size_t getCommandsHighWatermark() const noexcept {
|
||||
return mCommandsHighWatermark * sizeof(Command);
|
||||
Executor(RenderPass const* pass, Command const* b, Command const* e) noexcept
|
||||
: mEngine(pass->mEngine), mBegin(b), mEnd(e),
|
||||
mCustomCommands(pass->mCustomCommands), mUboHandle(pass->mUboHandle),
|
||||
mPolygonOffset(pass->mPolygonOffset),
|
||||
mPolygonOffsetOverride(pass->mPolygonOffsetOverride) {
|
||||
assert_invariant(b >= pass->begin());
|
||||
assert_invariant(e <= pass->end());
|
||||
}
|
||||
|
||||
void recordDriverCommands(backend::DriverApi& driver, const Command* first,
|
||||
const Command* last) const noexcept;
|
||||
|
||||
public:
|
||||
void execute(const char* name,
|
||||
backend::Handle<backend::HwRenderTarget> renderTarget,
|
||||
backend::RenderPassParams params) const noexcept;
|
||||
|
||||
void executeCommands(const char* name) const noexcept;
|
||||
};
|
||||
|
||||
// returns a new executor for this pass
|
||||
Executor getExecutor() const {
|
||||
return { this, mCommandBegin, mCommandEnd };
|
||||
}
|
||||
|
||||
// returns a new executor for this pass with a custom range
|
||||
Executor getExecutor(Command const* b, Command const* e) const {
|
||||
return { this, b, e };
|
||||
}
|
||||
|
||||
private:
|
||||
friend class FRenderer;
|
||||
|
||||
Command* append(size_t count) noexcept;
|
||||
void resize(size_t count) noexcept;
|
||||
|
||||
// on 64-bits systems, we process batches of 256 (64 bytes) cache-lines, or 512 (32 bytes) commands
|
||||
// on 32-bits systems, we process batches of 512 (32 bytes) cache-lines, or 512 (32 bytes) commands
|
||||
static constexpr size_t JOBS_PARALLEL_FOR_COMMANDS_COUNT = 512;
|
||||
@@ -324,9 +374,6 @@ private:
|
||||
static void setupColorCommand(Command& cmdDraw,
|
||||
FMaterialInstance const* mi, bool inverseFrontFaces) noexcept;
|
||||
|
||||
void recordDriverCommands(FEngine::DriverApi& driver, const Command* first,
|
||||
const Command* last) const noexcept;
|
||||
|
||||
static void updateSummedPrimitiveCounts(
|
||||
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> vr) noexcept;
|
||||
|
||||
@@ -337,30 +384,41 @@ private:
|
||||
// a reference to the Engine, mostly to get to things like JobSystem
|
||||
FEngine& mEngine;
|
||||
|
||||
utils::GrowingSlice<Command> mCommands;
|
||||
// Arena where all Commands are allocated. The Arena owns the commands.
|
||||
Arena& mCommandArena;
|
||||
|
||||
// Pointer to the first command
|
||||
Command* mCommandBegin = nullptr;
|
||||
|
||||
// Pointer to one past the last command
|
||||
Command* mCommandEnd = nullptr;
|
||||
|
||||
// the SOA containing the renderables we're interested in
|
||||
FScene::RenderableSoa const* mRenderableSoa = nullptr;
|
||||
// and the range of visible renderables in the SOA above
|
||||
|
||||
// The range of visible renderables in the SOA above
|
||||
utils::Range<uint32_t> mVisibleRenderables{};
|
||||
|
||||
// the UBO containing the data for the renderables
|
||||
backend::Handle<backend::HwUniformBuffer> mUboHandle;
|
||||
|
||||
// info about the camera
|
||||
CameraInfo mCamera;
|
||||
|
||||
// info about the scene features (e.g.: has shadows, lighting, etc...)
|
||||
RenderFlags mFlags{};
|
||||
|
||||
// Additional visibility mask
|
||||
FScene::VisibleMaskType mVisibilityMask = std::numeric_limits<FScene::VisibleMaskType>::max();
|
||||
|
||||
// whether to override the polygon offset setting
|
||||
bool mPolygonOffsetOverride = false;
|
||||
|
||||
// value of the override
|
||||
backend::PolygonOffset mPolygonOffset{};
|
||||
|
||||
// a vector for our custom commands
|
||||
mutable CustomCommandVector mCustomCommands;
|
||||
|
||||
// high watermark for debugging
|
||||
size_t mCommandsHighWatermark = 0;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -271,12 +271,13 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
|
||||
|
||||
FScene& scene = *view.getScene();
|
||||
|
||||
const size_t commandsSize = FEngine::CONFIG_PER_FRAME_COMMANDS_SIZE;
|
||||
const size_t commandsCount = commandsSize / sizeof(Command);
|
||||
GrowingSlice<Command> commands(
|
||||
arena.allocate<Command>(commandsCount, CACHELINE_SIZE), commandsCount);
|
||||
// Allocate some space for our commands in the per-frame Arena, and use that space as
|
||||
// an Arena for commands. All this space is released when we exit this method.
|
||||
void* const arenaBegin = arena.allocate(FEngine::CONFIG_PER_FRAME_COMMANDS_SIZE, CACHELINE_SIZE);
|
||||
void* const arenaEnd = pointermath::add(arenaBegin, FEngine::CONFIG_PER_FRAME_COMMANDS_SIZE);
|
||||
RenderPass::Arena commandArena("Command Arena", { arenaBegin, arenaEnd });
|
||||
|
||||
RenderPass pass(engine, commands);
|
||||
RenderPass pass(engine, commandArena);
|
||||
RenderPass::RenderFlags renderFlags = 0;
|
||||
if (view.hasShadowing()) renderFlags |= RenderPass::HAS_SHADOWING;
|
||||
if (view.hasDirectionalLight()) renderFlags |= RenderPass::HAS_DIRECTIONAL_LIGHT;
|
||||
@@ -390,10 +391,15 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
|
||||
|
||||
CameraInfo cameraInfo = view.getCameraInfo();
|
||||
|
||||
// updatePrimitivesLod must be run before appendCommands and once for each set
|
||||
// of RenderPass::setCamera / RenderPass::setGeometry calls.
|
||||
view.updatePrimitivesLod(engine, cameraInfo,
|
||||
scene.getRenderableData(), view.getVisibleRenderables());
|
||||
|
||||
pass.setCamera(cameraInfo);
|
||||
pass.setGeometry(scene.getRenderableData(), view.getVisibleRenderables(), scene.getRenderableUBO());
|
||||
view.updatePrimitivesLod(engine, cameraInfo, scene.getRenderableData(), view.getVisibleRenderables());
|
||||
|
||||
// view set-ups that need to happen before rendering
|
||||
fg.addTrivialSideEffectPass("Prepare View Uniforms", [svp, &view] (DriverApi& driver) {
|
||||
CameraInfo cameraInfo = view.getCameraInfo();
|
||||
view.prepareCamera(cameraInfo);
|
||||
@@ -406,13 +412,13 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
|
||||
// Currently it consists of a simple depth pass.
|
||||
// This is normally used by SSAO and contact-shadows
|
||||
|
||||
// TODO: this should be a FrameGraph pass to participate to automatic culling
|
||||
pass.newCommandBuffer();
|
||||
pass.appendCommands(RenderPass::CommandTypeFlags::SSAO);
|
||||
pass.sortCommands();
|
||||
// TODO: ideally this should be a FrameGraph pass to participate to automatic culling
|
||||
RenderPass structurePass(pass);
|
||||
structurePass.appendCommands(RenderPass::CommandTypeFlags::SSAO);
|
||||
structurePass.sortCommands();
|
||||
|
||||
// TODO: the scaling should depends on all passes that need the structure pass
|
||||
ppm.structure(fg, pass, svp.width, svp.height, aoOptions.resolution);
|
||||
ppm.structure(fg, structurePass, svp.width, svp.height, aoOptions.resolution);
|
||||
|
||||
// Apply the TAA jitter to everything after the structure pass, starting with the color pass.
|
||||
if (taaOptions.enabled) {
|
||||
@@ -435,14 +441,13 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
|
||||
|
||||
if (aoOptions.enabled) {
|
||||
// we could rely on FrameGraph culling, but this creates unnecessary CPU work
|
||||
ppm.screenSpaceAmbientOcclusion(fg, pass, svp, cameraInfo, aoOptions);
|
||||
ppm.screenSpaceAmbientOcclusion(fg, svp, cameraInfo, aoOptions);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// Color passes
|
||||
|
||||
// TODO: ideally this should be a FrameGraph pass to participate to automatic culling
|
||||
pass.newCommandBuffer();
|
||||
pass.appendCommands(RenderPass::COLOR);
|
||||
pass.sortCommands();
|
||||
|
||||
@@ -478,7 +483,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
|
||||
|
||||
// the color pass itself + color-grading as subpass if needed
|
||||
FrameGraphId<FrameGraphTexture> colorPassOutput = colorPass(fg, "Color Pass",
|
||||
desc, config, colorGradingConfigForColor, pass, view);
|
||||
desc, config, colorGradingConfigForColor, pass.getExecutor(), view);
|
||||
|
||||
// the color pass + refraction + color-grading as subpass if needed
|
||||
// this cancels the colorPass() call above if refraction is active.
|
||||
@@ -584,7 +589,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) {
|
||||
// save the current history entry and destroy the oldest entry
|
||||
view.commitFrameHistory(engine);
|
||||
|
||||
recordHighWatermark(pass.getCommandsHighWatermark());
|
||||
//recordHighWatermark(pass.getCommandsHighWatermark());
|
||||
}
|
||||
|
||||
FrameGraphId<FrameGraphTexture> FRenderer::refractionPass(FrameGraph& fg,
|
||||
@@ -613,10 +618,7 @@ FrameGraphId<FrameGraphTexture> FRenderer::refractionPass(FrameGraph& fg,
|
||||
blackboard.remove("color");
|
||||
blackboard.remove("depth");
|
||||
|
||||
RenderPass opaquePass(pass);
|
||||
opaquePass.getCommands().set(
|
||||
const_cast<Command*>(pass.begin()),
|
||||
const_cast<Command*>(refraction));
|
||||
const RenderPass::Executor opaquePass{ pass.getExecutor(pass.begin(), refraction) };
|
||||
|
||||
FrameGraphTexture::Descriptor desc = {
|
||||
.width = config.svp.width,
|
||||
@@ -688,10 +690,7 @@ FrameGraphId<FrameGraphTexture> FRenderer::refractionPass(FrameGraph& fg,
|
||||
// ^^^ the actual refraction pass ends above ^^^
|
||||
|
||||
// set-up the refraction pass
|
||||
RenderPass translucentPass(pass);
|
||||
translucentPass.getCommands().set(
|
||||
const_cast<Command*>(refraction),
|
||||
const_cast<Command*>(pass.end()));
|
||||
const RenderPass::Executor translucentPass{ pass.getExecutor(refraction, pass.end()) };
|
||||
|
||||
config.refractionLodOffset = refractionLodOffset;
|
||||
config.clearFlags = TargetBufferFlags::NONE;
|
||||
@@ -714,7 +713,7 @@ FrameGraphId<FrameGraphTexture> FRenderer::refractionPass(FrameGraph& fg,
|
||||
FrameGraphId<FrameGraphTexture> FRenderer::colorPass(FrameGraph& fg, const char* name,
|
||||
FrameGraphTexture::Descriptor const& colorBufferDesc,
|
||||
ColorPassConfig const& config, PostProcessManager::ColorGradingConfig colorGradingConfig,
|
||||
RenderPass const& pass, FView const& view) const noexcept {
|
||||
RenderPass::Executor const& passExecutor, FView const& view) const noexcept {
|
||||
|
||||
struct ColorPassData {
|
||||
FrameGraphId<FrameGraphTexture> shadows;
|
||||
@@ -850,16 +849,15 @@ FrameGraphId<FrameGraphTexture> FRenderer::colorPass(FrameGraph& fg, const char*
|
||||
|
||||
if (colorGradingConfig.asSubpass) {
|
||||
out.params.subpassMask = 1;
|
||||
// TODO: we should implement this with a RenderPass command
|
||||
driver.beginRenderPass(out.target, out.params);
|
||||
pass.executeCommands(resources.getPassName());
|
||||
passExecutor.executeCommands(resources.getPassName());
|
||||
ppm.colorGradingSubpass(driver, colorGradingConfig);
|
||||
driver.endRenderPass();
|
||||
} else {
|
||||
driver.beginRenderPass(out.target, out.params);
|
||||
pass.executeCommands(resources.getPassName());
|
||||
passExecutor.execute(resources.getPassName(), out.target, out.params);
|
||||
}
|
||||
|
||||
driver.endRenderPass();
|
||||
|
||||
// color pass is typically heavy and we don't have much CPU work left after
|
||||
// this point, so flushing now allows us to start the GPU earlier and reduce
|
||||
// latency, without creating bubbles.
|
||||
|
||||
@@ -69,24 +69,23 @@ ShadowMap::~ShadowMap() {
|
||||
engine.getEntityManager().destroy(sizeof(entities) / sizeof(Entity), entities);
|
||||
}
|
||||
|
||||
void ShadowMap::render(DriverApi& driver, FView::Range const& range, RenderPass& pass,
|
||||
FView& view) noexcept {
|
||||
void ShadowMap::render(DriverApi& driver, FView::Range const& range,
|
||||
RenderPass* const pass, FView& view) noexcept {
|
||||
FEngine& engine = mEngine;
|
||||
|
||||
filament::CameraInfo cameraInfo(getCamera());
|
||||
|
||||
FScene& scene = *view.getScene();
|
||||
FScene::RenderableSoa& renderableData = scene.getRenderableData();
|
||||
|
||||
FCamera const& camera = getCamera();
|
||||
filament::CameraInfo cameraInfo(camera);
|
||||
|
||||
pass.setCamera(cameraInfo);
|
||||
pass.setGeometry(scene.getRenderableData(), range, scene.getRenderableUBO());
|
||||
|
||||
pass->setCamera(cameraInfo);
|
||||
pass->setGeometry(renderableData, range, scene.getRenderableUBO());
|
||||
// updatePrimitivesLod must be run before appendCommands.
|
||||
view.updatePrimitivesLod(engine, cameraInfo, scene.getRenderableData(), range);
|
||||
view.updatePrimitivesLod(engine, cameraInfo, renderableData, range);
|
||||
|
||||
pass.newCommandBuffer();
|
||||
pass.appendCommands(RenderPass::SHADOW);
|
||||
pass.sortCommands();
|
||||
pass->overridePolygonOffset(&mPolygonOffset);
|
||||
pass->appendCommands(RenderPass::SHADOW);
|
||||
pass->sortCommands();
|
||||
}
|
||||
|
||||
mat4f ShadowMap::getLightViewMatrix(float3 position, float3 direction) noexcept {
|
||||
|
||||
@@ -77,7 +77,7 @@ void ShadowMapManager::addSpotShadowMap(size_t lightIndex) noexcept {
|
||||
}
|
||||
|
||||
void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, FView& view,
|
||||
backend::DriverApi& driver, RenderPass& pass) noexcept {
|
||||
backend::DriverApi& driver, RenderPass const& pass) noexcept {
|
||||
|
||||
constexpr size_t MAX_SHADOW_LAYERS =
|
||||
CONFIG_MAX_SHADOW_CASCADES + CONFIG_MAX_SHADOW_CASTING_SPOTS;
|
||||
@@ -88,37 +88,45 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, FView& view,
|
||||
const TextureRequirements textureRequirements = mTextureRequirements;
|
||||
assert_invariant(textureRequirements.layers <= MAX_SHADOW_LAYERS);
|
||||
|
||||
using ShadowPass = std::pair<const ShadowMapEntry*, RenderPass>;
|
||||
auto passes = utils::FixedCapacityVector<ShadowPass>::with_capacity(MAX_SHADOW_LAYERS);
|
||||
struct ShadowPass {
|
||||
ShadowPass(ShadowMapEntry const* shadowmap, RenderPass const& pass) noexcept
|
||||
: shadowmap(shadowmap), pass(pass) {
|
||||
}
|
||||
ShadowMapEntry const* shadowmap;
|
||||
RenderPass pass;
|
||||
};
|
||||
|
||||
auto passList = utils::FixedCapacityVector<ShadowPass>::with_capacity(MAX_SHADOW_LAYERS);
|
||||
|
||||
// These loops fill render passes with appropriate rendering commands for each shadow map.
|
||||
// The actual render pass execution is deferred to the frame graph.
|
||||
// Directional, cascaded shadowmaps
|
||||
for (const auto& map : mCascadeShadowMaps) {
|
||||
if (!map.hasVisibleShadows()) {
|
||||
auto& range = view.getVisibleDirectionalShadowCasters();
|
||||
if (!map.hasVisibleShadows() || range.empty()) {
|
||||
continue;
|
||||
}
|
||||
map.getShadowMap().render(driver, view.getVisibleDirectionalShadowCasters(), pass, view);
|
||||
passes.emplace_back(&map, pass);
|
||||
auto& entry = passList.emplace_back(&map, pass);
|
||||
map.getShadowMap().render(driver, range, &entry.pass, view);
|
||||
}
|
||||
|
||||
// Spotlight shadowmaps
|
||||
for (size_t i = 0, c = mSpotShadowMaps.size(); i < c; i++) {
|
||||
const auto& map = mSpotShadowMaps[i];
|
||||
if (!map.hasVisibleShadows()) {
|
||||
auto& range = view.getVisibleSpotShadowCasters();
|
||||
if (!map.hasVisibleShadows() || range.empty()) {
|
||||
continue;
|
||||
}
|
||||
pass.setVisibilityMask(VISIBLE_SPOT_SHADOW_RENDERABLE_N(i));
|
||||
map.getShadowMap().render(driver, view.getVisibleSpotShadowCasters(), pass, view);
|
||||
pass.clearVisibilityMask();
|
||||
passes.emplace_back(&map, pass);
|
||||
auto& entry = passList.emplace_back(&map, pass);
|
||||
entry.pass.setVisibilityMask(VISIBLE_SPOT_SHADOW_RENDERABLE_N(i));
|
||||
map.getShadowMap().render(driver, range, &entry.pass, view);
|
||||
}
|
||||
|
||||
const float vsmMoment2 = std::numeric_limits<math::half>::max();
|
||||
const float vsmMoment1 = std::sqrt(vsmMoment2);
|
||||
const float4 vsmClearColor{ vsmMoment1, vsmMoment2, 0.0f, 0.0f };
|
||||
|
||||
assert_invariant(passes.size() <= textureRequirements.layers);
|
||||
assert_invariant(passList.size() <= textureRequirements.layers);
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -151,13 +159,12 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, FView& view,
|
||||
|
||||
auto& ppm = engine.getPostProcessManager();
|
||||
|
||||
for (auto& entry : passes) {
|
||||
auto* map = entry.first;
|
||||
auto& pass = entry.second;
|
||||
for (auto& entry : passList) {
|
||||
if (!entry.shadowmap->hasVisibleShadows()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!map->hasVisibleShadows()) continue;
|
||||
|
||||
ShadowLayout const& layout = map->getLayout();
|
||||
ShadowLayout const& layout = entry.shadowmap->getLayout();
|
||||
const auto layer = layout.layer;
|
||||
const auto* options = layout.options;
|
||||
|
||||
@@ -230,14 +237,14 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, FView& view,
|
||||
// finally create the shadowmap render target -- one per layer.
|
||||
data.shadowRt = builder.declareRenderPass("Shadow RT", renderTargetDesc);
|
||||
},
|
||||
[=, passes = std::move(passes), &view](FrameGraphResources const& resources,
|
||||
auto const& data, DriverApi& driver) mutable {
|
||||
[=, executor = entry.pass.getExecutor(), &view](FrameGraphResources const& resources,
|
||||
auto const& data, DriverApi& driver) {
|
||||
|
||||
const auto& options = layout.options;
|
||||
const bool blur = view.hasVsm() && options->vsm.blurWidth > 0.0f;
|
||||
|
||||
// TODO: camera is already set inside 'pass', we could get it from there
|
||||
FCamera const& camera = map->getShadowMap().getCamera();
|
||||
FCamera const& camera = entry.shadowmap->getShadowMap().getCamera();
|
||||
filament::CameraInfo cameraInfo(camera);
|
||||
view.prepareCamera(cameraInfo);
|
||||
|
||||
@@ -268,10 +275,7 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, FView& view,
|
||||
|
||||
rt.params.viewport = viewport;
|
||||
|
||||
auto polygonOffset = map->getShadowMap().getPolygonOffset();
|
||||
pass.overridePolygonOffset(&polygonOffset);
|
||||
|
||||
pass.execute("Shadow Pass", rt.target, rt.params);
|
||||
executor.execute("Shadow Pass", rt.target, rt.params);
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -863,7 +863,7 @@ void FView::updatePrimitivesLod(FEngine& engine, const CameraInfo&,
|
||||
}
|
||||
|
||||
void FView::renderShadowMaps(FrameGraph& fg, FEngine& engine, FEngine::DriverApi& driver,
|
||||
RenderPass& pass) noexcept {
|
||||
RenderPass const& pass) noexcept {
|
||||
mShadowMapManager.render(fg, engine, *this, driver, pass);
|
||||
}
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ private:
|
||||
FrameGraphTexture::Descriptor const& colorBufferDesc,
|
||||
ColorPassConfig const& config,
|
||||
PostProcessManager::ColorGradingConfig colorGradingConfig,
|
||||
RenderPass const& pass, FView const& view) const noexcept;
|
||||
RenderPass::Executor const& passExecutor, FView const& view) const noexcept;
|
||||
|
||||
FrameGraphId<FrameGraphTexture> refractionPass(FrameGraph& fg,
|
||||
ColorPassConfig config,
|
||||
|
||||
@@ -95,8 +95,8 @@ public:
|
||||
filament::CameraInfo const& camera,
|
||||
const ShadowMapInfo& shadowMapInfo, const SceneInfo& cascadeParams) noexcept;
|
||||
|
||||
void render(backend::DriverApi& driver, utils::Range<uint32_t> const& range, RenderPass& pass,
|
||||
FView& view) noexcept;
|
||||
void render(backend::DriverApi& driver, utils::Range<uint32_t> const& range,
|
||||
RenderPass* pass, FView& view) noexcept;
|
||||
|
||||
// Do we have visible shadows. Valid after calling update().
|
||||
bool hasVisibleShadows() const noexcept { return mHasVisibleShadows; }
|
||||
|
||||
@@ -75,7 +75,7 @@ public:
|
||||
|
||||
// Renders all of the shadow maps.
|
||||
void render(FrameGraph& fg, FEngine& engine, FView& view, backend::DriverApi& driver,
|
||||
RenderPass& pass) noexcept;
|
||||
RenderPass const& pass) noexcept;
|
||||
|
||||
const ShadowMap* getCascadeShadowMap(size_t c) const noexcept {
|
||||
assert_invariant(c < mCascadeShadowMapCache.size());
|
||||
|
||||
@@ -183,7 +183,7 @@ public:
|
||||
bool hasVsm() const noexcept { return mShadowType == ShadowType::VSM; }
|
||||
|
||||
void renderShadowMaps(FrameGraph& fg, FEngine& engine, FEngine::DriverApi& driver,
|
||||
RenderPass& pass) noexcept;
|
||||
RenderPass const& pass) noexcept;
|
||||
|
||||
void updatePrimitivesLod(
|
||||
FEngine& engine, const CameraInfo& camera,
|
||||
|
||||
@@ -58,7 +58,7 @@ class FrameGraphPass : public FrameGraphPassBase {
|
||||
friend class FrameGraph;
|
||||
|
||||
// allow our allocators to instantiate us
|
||||
template<typename, typename, typename>
|
||||
template<typename, typename, typename, typename>
|
||||
friend class utils::Arena;
|
||||
|
||||
explicit FrameGraphPass(Execute&& execute) noexcept
|
||||
|
||||
@@ -385,6 +385,39 @@ using ThreadSafeObjectPoolAllocator = PoolAllocator<sizeof(T),
|
||||
// Areas
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
namespace AreaPolicy {
|
||||
|
||||
class StaticArea {
|
||||
public:
|
||||
StaticArea() noexcept = default;
|
||||
|
||||
StaticArea(void* b, void* e) noexcept
|
||||
: mBegin(b), mEnd(e) {
|
||||
}
|
||||
|
||||
~StaticArea() noexcept = default;
|
||||
|
||||
StaticArea(const StaticArea& rhs) = default;
|
||||
StaticArea& operator=(const StaticArea& rhs) = default;
|
||||
StaticArea(StaticArea&& rhs) noexcept = default;
|
||||
StaticArea& operator=(StaticArea&& rhs) noexcept = default;
|
||||
|
||||
void* data() const noexcept { return mBegin; }
|
||||
void* begin() const noexcept { return mBegin; }
|
||||
void* end() const noexcept { return mEnd; }
|
||||
size_t size() const noexcept { return uintptr_t(mEnd) - uintptr_t(mBegin); }
|
||||
|
||||
friend void swap(StaticArea& lhs, StaticArea& rhs) noexcept {
|
||||
using std::swap;
|
||||
swap(lhs.mBegin, rhs.mBegin);
|
||||
swap(lhs.mEnd, rhs.mEnd);
|
||||
}
|
||||
|
||||
private:
|
||||
void* mBegin = nullptr;
|
||||
void* mEnd = nullptr;
|
||||
};
|
||||
|
||||
class HeapArea {
|
||||
public:
|
||||
HeapArea() noexcept = default;
|
||||
@@ -410,7 +443,7 @@ public:
|
||||
void* data() const noexcept { return mBegin; }
|
||||
void* begin() const noexcept { return mBegin; }
|
||||
void* end() const noexcept { return mEnd; }
|
||||
size_t getSize() const noexcept { return uintptr_t(mEnd) - uintptr_t(mBegin); }
|
||||
size_t size() const noexcept { return uintptr_t(mEnd) - uintptr_t(mBegin); }
|
||||
|
||||
friend void swap(HeapArea& lhs, HeapArea& rhs) noexcept {
|
||||
using std::swap;
|
||||
@@ -423,6 +456,7 @@ private:
|
||||
void* mEnd = nullptr;
|
||||
};
|
||||
|
||||
} // namespace AreaPolicy
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Policies
|
||||
@@ -524,7 +558,8 @@ struct DebugAndHighWatermark : protected HighWatermark, protected Debug {
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename AllocatorPolicy, typename LockingPolicy,
|
||||
typename TrackingPolicy = TrackingPolicy::Untracked>
|
||||
typename TrackingPolicy = TrackingPolicy::Untracked,
|
||||
typename AreaPolicy = AreaPolicy::HeapArea>
|
||||
class Arena {
|
||||
public:
|
||||
|
||||
@@ -536,7 +571,15 @@ public:
|
||||
: mArenaName(name),
|
||||
mArea(size),
|
||||
mAllocator(mArea, std::forward<ARGS>(args) ... ),
|
||||
mListener(name, mArea.data(), size) {
|
||||
mListener(name, mArea.data(), mArea.size()) {
|
||||
}
|
||||
|
||||
template<typename ... ARGS>
|
||||
Arena(const char* name, AreaPolicy&& area, ARGS&& ... args)
|
||||
: mArenaName(name),
|
||||
mArea(std::forward<AreaPolicy>(area)),
|
||||
mAllocator(mArea, std::forward<ARGS>(args) ... ),
|
||||
mListener(name, mArea.data(), mArea.size()) {
|
||||
}
|
||||
|
||||
// allocate memory from arena with given size and alignment
|
||||
@@ -616,8 +659,8 @@ public:
|
||||
TrackingPolicy& getListener() noexcept { return mListener; }
|
||||
TrackingPolicy const& getListener() const noexcept { return mListener; }
|
||||
|
||||
HeapArea& getArea() noexcept { return mArea; }
|
||||
HeapArea const& getArea() const noexcept { return mArea; }
|
||||
AreaPolicy& getArea() noexcept { return mArea; }
|
||||
AreaPolicy const& getArea() const noexcept { return mArea; }
|
||||
|
||||
void setListener(TrackingPolicy listener) noexcept {
|
||||
std::swap(mListener, listener);
|
||||
@@ -644,7 +687,7 @@ public:
|
||||
|
||||
private:
|
||||
char const* mArenaName = nullptr;
|
||||
HeapArea mArea; // We might want to make that a template parameter too eventually.
|
||||
AreaPolicy mArea;
|
||||
// note: we should use something like compressed_pair for the members below
|
||||
AllocatorPolicy mAllocator;
|
||||
LockingPolicy mLock;
|
||||
|
||||
Reference in New Issue
Block a user