Files
NazaraEngine/include/Nazara/Renderer/RenderResources.inl
Lynix 4cc8b78f5f Renderer/RenderResource: Half the number of virtual calls required on cleanup
Since most of the time objects are directly pushed for release instead of callbacks, it makes sense to optimize this case by not storing the object in a callback and removing the call to the virtual Release method. The virtual destructor does the job here.
2023-11-21 23:58:59 +01:00

136 lines
3.2 KiB
C++

// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Renderer module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <NazaraUtils/Algorithm.hpp>
#include <NazaraUtils/MemoryHelper.hpp>
#include <Nazara/Renderer/Debug.hpp>
namespace Nz
{
inline RenderResources::RenderResources(RenderDevice& renderDevice) :
m_renderDevice(renderDevice)
{
}
inline void RenderResources::FlushReleaseQueue()
{
for (ReleasableCallback* callback : m_callbackQueue)
callback->Release();
m_callbackQueue.clear();
for (Releasable* releasable : m_releaseQueue)
PlacementDestroy(releasable);
m_releaseQueue.clear();
for (auto& memoryblock : m_releaseMemoryPool)
memoryblock.clear();
}
inline RenderDevice& RenderResources::GetRenderDevice()
{
return m_renderDevice;
}
template<typename T>
void RenderResources::PushForRelease(T&& value)
{
static_assert(std::is_rvalue_reference_v<decltype(value)>);
using ReleaseData = ReleasableData<std::remove_cv_t<std::remove_reference_t<T>>>;
ReleaseData* releasable = Allocate<ReleaseData>();
PlacementNew(releasable, std::forward<T>(value));
m_releaseQueue.push_back(releasable);
}
template<typename F>
void RenderResources::PushReleaseCallback(F&& callback)
{
using ReleaseFunctor = ReleasableLambda<std::remove_cv_t<std::remove_reference_t<F>>>;
ReleaseFunctor* releasable = Allocate<ReleaseFunctor>();
PlacementNew(releasable, std::forward<F>(callback));
m_releaseQueue.push_back(releasable);
m_callbackQueue.push_back(releasable);
}
template<typename T>
T* RenderResources::Allocate()
{
return static_cast<T*>(Allocate(sizeof(T), alignof(T)));
}
void* RenderResources::Allocate(std::size_t size, std::size_t alignment)
{
// Try to minimize lost space
struct
{
Block* block = nullptr;
UInt64 alignedOffset = 0;
UInt64 lostSpace = 0;
} bestBlock;
for (Block& block : m_releaseMemoryPool)
{
std::size_t freeOffset = block.size();
UInt64 alignedOffset = Align(freeOffset, alignment);
if (alignedOffset + size > block.capacity())
continue; //< Not enough space
UInt64 lostSpace = alignedOffset - freeOffset;
if (!bestBlock.block || lostSpace < bestBlock.lostSpace)
{
bestBlock.block = &block;
bestBlock.alignedOffset = alignedOffset;
bestBlock.lostSpace = lostSpace;
}
}
// No block found, allocate a new one
if (!bestBlock.block)
{
Block newBlock;
newBlock.reserve(BlockSize);
bestBlock.block = &m_releaseMemoryPool.emplace_back(std::move(newBlock));
bestBlock.alignedOffset = 0;
bestBlock.lostSpace = 0;
}
Block& targetBlock = *bestBlock.block;
targetBlock.resize(bestBlock.alignedOffset + size);
return &targetBlock[bestBlock.alignedOffset];
}
template<typename T>
RenderResources::ReleasableData<T>::ReleasableData(T&& data) :
m_data(std::move(data))
{
}
template<typename T>
template<typename U>
RenderResources::ReleasableLambda<T>::ReleasableLambda(U&& lambda) :
m_lambda(std::forward<U>(lambda))
{
}
template<typename T>
void RenderResources::ReleasableLambda<T>::Release()
{
m_lambda();
}
}
#include <Nazara/Renderer/DebugOff.hpp>
#include "RenderResources.hpp"