pooled shared ptr (#1316)

This commit is contained in:
Turánszki János
2025-11-22 17:12:26 +01:00
committed by GitHub
parent 060164c1f1
commit 7a5ea2f64f
19 changed files with 422 additions and 107 deletions
+3 -2
View File
@@ -554,7 +554,7 @@ int main(int argc, char* argv[])
{
if (shaderdump_enabled)
{
auto vec = std::make_shared<std::vector<uint8_t>>();
auto vec = wi::allocator::make_shared<std::vector<uint8_t>>();
if (wi::helper::FileRead(shaderbinaryfilename, *vec))
{
@@ -566,7 +566,8 @@ int main(int argc, char* argv[])
std::cout << "up-to-date: " << shaderbinaryfilename << std::endl;
locker.unlock();
}
else {
else
{
locker.lock();
std::cerr << "ERROR reading binary shader: " << shaderbinaryfilename << std::endl;
locker.unlock();
+282 -2
View File
@@ -1,6 +1,7 @@
#pragma once
#include "CommonInclude.h"
#include "wiVector.h"
#include "wiSpinLock.h"
#include "Utility/offsetAllocator.hpp"
@@ -182,7 +183,7 @@ namespace wi::allocator
{
Reset();
}
void operator=(const Allocation& other)
Allocation& operator=(const Allocation& other)
{
Reset();
allocator = other.allocator;
@@ -192,8 +193,9 @@ namespace wi::allocator
{
internal_state->refcount.fetch_add(1);
}
return *this;
}
void operator=(Allocation&& other) noexcept
Allocation& operator=(Allocation&& other) noexcept
{
Reset();
allocator = std::move(other.allocator);
@@ -202,6 +204,7 @@ namespace wi::allocator
other.allocator = nullptr;
other.internal_state = nullptr;
other.byte_offset = ~0ull;
return *this;
}
void Reset()
{
@@ -253,4 +256,281 @@ namespace wi::allocator
return allocator->allocator.storageReport().totalFreeSpace == page_count;
}
};
// Interface for allocating pooled shared_ptr
struct SharedBlockAllocator
{
virtual void init_refcount(void* ptr) = 0;
virtual uint32_t get_refcount(void* ptr) = 0;
virtual uint32_t inc_refcount(void* ptr) = 0;
virtual uint32_t dec_refcount(void* ptr) = 0;
virtual uint32_t get_refcount_weak(void* ptr) = 0;
virtual uint32_t inc_refcount_weak(void* ptr) = 0;
virtual uint32_t dec_refcount_weak(void* ptr) = 0;
};
// The per-type block allocators can be indexed with bottom 8 bits of the shared_ptr's handle:
inline SharedBlockAllocator* block_allocators[256] = {};
inline std::atomic<uint8_t> next_allocator_id{ 0 };
inline uint8_t register_shared_block_allocator(SharedBlockAllocator* allocator)
{
uint8_t id = next_allocator_id.fetch_add(1);
assert(id < arraysize(block_allocators));
block_allocators[id] = allocator;
return id;
}
inline uint8_t get_shared_block_allocator_count() { return next_allocator_id.load(); }
// Shared ptr using a block allocation strategy, refcounted, thread-safe, reduced size using single uint64_t handle
// This makes it easy to swap-out std::shared_ptr, but not feature complete, only has minimal feature set
// Use this if you require many object of the same type, their memory allocation will be pooled
// If you require just a single object, it will be better to use std::shared_ptr instead
template<typename T>
struct shared_ptr
{
uint64_t handle = 0;
constexpr bool IsValid() const { return handle != 0; }
constexpr T* get_ptr() const { return (T*)(handle & (~0ull << 8ull)); }
constexpr SharedBlockAllocator* get_allocator() const { return block_allocators[handle & 0xFF]; }
constexpr T* operator->() const { return get_ptr(); }
constexpr operator T* () const { return get_ptr(); }
constexpr T* get() const { return get_ptr(); }
template<typename U>
operator shared_ptr<U>& () const { return *(shared_ptr<U>*)this; }
shared_ptr() = default;
shared_ptr(const shared_ptr& other) { copy(other); }
shared_ptr(shared_ptr&& other) noexcept { move(other); }
~shared_ptr() noexcept { reset(); }
shared_ptr& operator=(const shared_ptr& other) { copy(other); return *this; }
shared_ptr& operator=(shared_ptr&& other) noexcept { move(other); return *this; }
void reset() noexcept
{
if (IsValid())
{
get_allocator()->dec_refcount(get_ptr());
}
handle = 0;
}
void copy(const shared_ptr& other)
{
reset();
handle = other.handle;
if (IsValid())
{
get_allocator()->inc_refcount(get_ptr());
}
}
void move(shared_ptr& other) noexcept
{
if (this == &other)
return;
reset();
handle = other.handle;
other.handle = 0;
}
uint32_t use_count() const { return IsValid() ? get_allocator()->get_refcount(get_ptr()) : 0; }
};
// Similar to std::weak_ptr but works with the shared block allocator, and reduced feature set
template<typename T>
struct weak_ptr
{
uint64_t handle = 0;
constexpr bool IsValid() const { return handle != 0; }
constexpr T* get_ptr() const { return (T*)(handle & (~0ull << 8ull)); }
constexpr SharedBlockAllocator* get_allocator() const { return block_allocators[handle & 0xFF]; }
template<typename U>
operator weak_ptr<U>& () const { return *(weak_ptr<U>*)this; }
weak_ptr() = default;
weak_ptr(const weak_ptr& other) { copy(other); }
weak_ptr(weak_ptr&& other) noexcept { move(other); }
~weak_ptr() noexcept { reset(); }
weak_ptr& operator=(const weak_ptr& other) { copy(other); return *this; }
weak_ptr& operator=(weak_ptr&& other) noexcept { move(other); return *this; }
weak_ptr(const shared_ptr<T>& other)
{
reset();
handle = other.handle;
if (IsValid())
{
get_allocator()->inc_refcount_weak(get_ptr());
}
}
shared_ptr<T> lock()
{
if (!IsValid())
return {};
SharedBlockAllocator* alloc = get_allocator();
T* ptr = get_ptr();
uint32_t old_strong = alloc->inc_refcount(ptr);
if (old_strong == 0)
{
alloc->dec_refcount(ptr); // undo refcount
return {};
}
shared_ptr<T> ret;
ret.handle = handle;
return ret; // Already incremented refcount
}
void reset() noexcept
{
if (IsValid())
{
get_allocator()->dec_refcount_weak(get_ptr());
}
handle = 0;
}
void copy(const weak_ptr& other)
{
reset();
handle = other.handle;
if (IsValid())
{
get_allocator()->inc_refcount_weak(get_ptr());
}
}
void move(weak_ptr& other) noexcept
{
if (this == &other)
return;
reset();
handle = other.handle;
other.handle = 0;
}
uint32_t use_count() const { return get_allocator()->get_refcount(get_ptr()); }
bool expired() const noexcept
{
return !IsValid() || use_count() == 0;
}
};
// Implementation of a thread-safe refcounted block allocator
template<typename T, size_t block_size = 256>
struct SharedBlockAllocatorImpl final : public SharedBlockAllocator
{
const uint8_t allocator_id = register_shared_block_allocator(this);
struct alignas(std::max(size_t(256), alignof(T))) RawStruct // 256 alignment is used at least because I use bottom 8 bits of pointer as allocator id
{
uint8_t data[sizeof(T)];
std::atomic<uint32_t> refcount;
std::atomic<uint32_t> refcount_weak;
};
static_assert(offsetof(RawStruct, data) == 0); // we assume that data is located at 0 when casting ptr to T*, this avoids having to do a function call that would return T* like the refcounts
struct Block
{
std::unique_ptr<RawStruct[]> mem;
};
wi::vector<Block> blocks;
wi::vector<RawStruct*> free_list;
//std::mutex locker;
wi::SpinLock locker;
template<typename... ARG>
inline shared_ptr<T> allocate(ARG&&... args)
{
locker.lock();
if (free_list.empty())
{
Block& block = blocks.emplace_back();
block.mem.reset(new RawStruct[block_size]);
RawStruct* ptr = block.mem.get();
free_list.reserve(block_size);
for (size_t i = 0; i < block_size; ++i)
{
free_list.push_back(ptr + i);
}
}
RawStruct* ptr = free_list.back();
assert((uint64_t)ptr == ((uint64_t)ptr & (~0ull << 8ull))); // The pointer lower 8 bits must be 0, it will be used as allocator index
free_list.pop_back();
locker.unlock();
// Construction can be outside of lock, this structure wasn't shared yet:
new (ptr) T(std::forward<ARG>(args)...);
init_refcount(ptr);
shared_ptr<T> allocation;
allocation.handle = uint64_t(ptr) | uint64_t(allocator_id);
return allocation;
}
void reclaim(void* ptr)
{
std::scoped_lock lck(locker);
free_list.push_back((RawStruct*)ptr);
}
void init_refcount(void* ptr) override
{
static_cast<RawStruct*>(ptr)->refcount.store(1, std::memory_order_relaxed);
static_cast<RawStruct*>(ptr)->refcount_weak.store(1, std::memory_order_relaxed);
}
uint32_t get_refcount(void* ptr) override
{
return static_cast<RawStruct*>(ptr)->refcount.load(std::memory_order_acquire);
}
uint32_t inc_refcount(void* ptr) override
{
return static_cast<RawStruct*>(ptr)->refcount.fetch_add(1, std::memory_order_relaxed);
}
uint32_t dec_refcount(void* ptr) override
{
uint32_t old = static_cast<RawStruct*>(ptr)->refcount.fetch_sub(1, std::memory_order_acq_rel);
if (old == 1)
{
static_cast<T*>(ptr)->~T();
dec_refcount_weak(ptr);
}
return old;
}
uint32_t get_refcount_weak(void* ptr) override
{
return static_cast<RawStruct*>(ptr)->refcount_weak.load(std::memory_order_acquire);
}
uint32_t inc_refcount_weak(void* ptr) override
{
return static_cast<RawStruct*>(ptr)->refcount_weak.fetch_add(1, std::memory_order_relaxed);
}
uint32_t dec_refcount_weak(void* ptr) override
{
uint32_t old = static_cast<RawStruct*>(ptr)->refcount_weak.fetch_sub(1, std::memory_order_acq_rel);
if (old == 1)
{
reclaim(ptr);
}
return old;
}
};
// The allocators are global intentionally, this avoids runtime construction, guard check
template<typename T, size_t block_size = 256>
inline static SharedBlockAllocatorImpl<T, block_size>* shared_block_allocator = new SharedBlockAllocatorImpl<T, block_size>; // only destroyed after program exit, never earlier
// Create a new shared pooled object:
template<typename T, size_t block_size = 256, typename... ARG>
inline shared_ptr<T> make_shared(ARG&&... args)
{
return shared_block_allocator<T, block_size>->allocate(std::forward<ARG>(args)...);
}
}
+8 -8
View File
@@ -222,7 +222,7 @@ namespace wi::audio
struct SoundInstanceInternal : public IXAudio2VoiceCallback
{
std::shared_ptr<AudioInternal> audio;
std::shared_ptr<SoundInternal> soundinternal;
wi::allocator::shared_ptr<SoundInternal> soundinternal;
IXAudio2SourceVoice* sourceVoice = nullptr;
XAUDIO2_VOICE_DETAILS voiceDetails = {};
wi::vector<float> outputMatrix;
@@ -352,7 +352,7 @@ namespace wi::audio
{
if (audio_internal == nullptr || !audio_internal->IsValid())
return false;
std::shared_ptr<SoundInternal> soundinternal = std::make_shared<SoundInternal>();
auto soundinternal = wi::allocator::make_shared<SoundInternal>();
soundinternal->audio = audio_internal;
sound->internal_state = soundinternal;
@@ -429,8 +429,8 @@ namespace wi::audio
if (sound == nullptr || !sound->IsValid())
return false;
HRESULT hr;
const auto& soundinternal = std::static_pointer_cast<SoundInternal>(sound->internal_state);
std::shared_ptr<SoundInstanceInternal> instanceinternal = std::make_shared<SoundInstanceInternal>();
auto soundinternal = wi::allocator::shared_ptr<SoundInternal>(sound->internal_state);
auto instanceinternal = wi::allocator::make_shared<SoundInstanceInternal>();
instance->internal_state = instanceinternal;
instanceinternal->audio = audio_internal;
@@ -893,7 +893,7 @@ namespace wi::audio
};
struct SoundInstanceInternal{
std::shared_ptr<AudioInternal> audio;
std::shared_ptr<SoundInternal> soundinternal;
wi::allocator::shared_ptr<SoundInternal> soundinternal;
FAudioSourceVoice* sourceVoice = nullptr;
FAudioVoiceDetails voiceDetails = {};
wi::vector<float> outputMatrix;
@@ -979,7 +979,7 @@ namespace wi::audio
{
if (audio_internal == nullptr || !audio_internal->IsValid())
return false;
std::shared_ptr<SoundInternal> soundinternal = std::make_shared<SoundInternal>();
auto soundinternal = wi::allocator::make_shared<SoundInternal>();
soundinternal->audio = audio_internal;
sound->internal_state = soundinternal;
@@ -1056,8 +1056,8 @@ namespace wi::audio
if (sound == nullptr || !sound->IsValid())
return false;
uint32_t res;
const auto& soundinternal = std::static_pointer_cast<SoundInternal>(sound->internal_state);
std::shared_ptr<SoundInstanceInternal> instanceinternal = std::make_shared<SoundInstanceInternal>();
auto soundinternal = wi::allocator::shared_ptr<SoundInternal>(sound->internal_state);
auto instanceinternal = wi::allocator::make_shared<SoundInstanceInternal>();
instance->internal_state = instanceinternal;
instanceinternal->audio = audio_internal;
+5 -4
View File
@@ -1,6 +1,7 @@
#pragma once
#include "CommonInclude.h"
#include "wiMath.h"
#include "wiAllocator.h"
#include <memory>
#include <string>
@@ -28,14 +29,14 @@ namespace wi::audio
// Use SoundInstance for playback the sound data
struct Sound
{
std::shared_ptr<void> internal_state;
inline bool IsValid() const { return internal_state.get() != nullptr; }
wi::allocator::shared_ptr<void> internal_state;
constexpr bool IsValid() const { return internal_state.IsValid(); }
};
// SoundInstance can be used to play back a Sound with specified effects
struct SoundInstance
{
std::shared_ptr<void> internal_state;
inline bool IsValid() const { return internal_state.get() != nullptr; }
wi::allocator::shared_ptr<void> internal_state;
constexpr bool IsValid() const { return internal_state.IsValid(); }
// You can specify these params before creating the sound instance:
// The sound instance will need to be recreated for changes to take effect
+20 -19
View File
@@ -1,6 +1,7 @@
#pragma once
#include "CommonInclude.h"
#include "wiVector.h"
#include "wiAllocator.h"
#include <cassert>
#include <memory>
@@ -840,8 +841,8 @@ namespace wi::graphics
struct Sampler
{
std::shared_ptr<void> internal_state;
inline bool IsValid() const { return internal_state != nullptr; }
wi::allocator::shared_ptr<void> internal_state;
constexpr bool IsValid() const { return internal_state.IsValid(); }
SamplerDesc desc;
@@ -850,22 +851,22 @@ namespace wi::graphics
struct Shader
{
std::shared_ptr<void> internal_state;
inline bool IsValid() const { return internal_state != nullptr; }
wi::allocator::shared_ptr<void> internal_state;
constexpr bool IsValid() const { return internal_state.IsValid(); }
ShaderStage stage = ShaderStage::Count;
};
struct GPUResource
{
std::shared_ptr<void> internal_state;
inline bool IsValid() const { return internal_state != nullptr; }
wi::allocator::shared_ptr<void> internal_state;
constexpr bool IsValid() const { return internal_state.IsValid(); }
// These are only valid if the resource was created with CPU access (USAGE::UPLOAD or USAGE::READBACK)
void* mapped_data = nullptr; // for buffers, it is a pointer to the buffer data; for textures, it is a pointer to texture data with linear tiling;
size_t mapped_size = 0; // for buffers, it is the full buffer size; for textures it is the full texture size including all subresources;
uint32_t sparse_page_size = 0ull; // specifies the required alignment of backing allocation for sparse tile pool
uint32_t sparse_page_size = 0; // specifies the required alignment of backing allocation for sparse tile pool
enum class Type : uint8_t
{
@@ -887,9 +888,9 @@ namespace wi::graphics
// Dynamic allocation and destruction of this object is not allowed because virtual table is not used
static void* operator new (size_t) = delete;
static void* operator new[](size_t) = delete;
static void* operator new[] (size_t) = delete;
static void operator delete (void*) = delete;
static void operator delete[](void*) = delete;
static void operator delete[] (void*) = delete;
static void* operator new (size_t, void*) = delete;
static void* operator new[](size_t, void*) = delete;
};
@@ -924,8 +925,8 @@ namespace wi::graphics
struct VideoDecoder
{
std::shared_ptr<void> internal_state;
inline bool IsValid() const { return internal_state != nullptr; }
wi::allocator::shared_ptr<void> internal_state;
constexpr bool IsValid() const { return internal_state.IsValid(); }
VideoDesc desc;
constexpr const VideoDesc& GetDesc() const { return desc; }
@@ -1159,8 +1160,8 @@ namespace wi::graphics
struct GPUQueryHeap
{
std::shared_ptr<void> internal_state;
inline bool IsValid() const { return internal_state != nullptr; }
wi::allocator::shared_ptr<void> internal_state;
constexpr bool IsValid() const { return internal_state.IsValid(); }
GPUQueryHeapDesc desc;
@@ -1169,8 +1170,8 @@ namespace wi::graphics
struct PipelineState
{
std::shared_ptr<void> internal_state;
inline bool IsValid() const { return internal_state != nullptr; }
wi::allocator::shared_ptr<void> internal_state;
constexpr bool IsValid() const { return internal_state.IsValid(); }
PipelineStateDesc desc;
@@ -1179,8 +1180,8 @@ namespace wi::graphics
struct SwapChain
{
std::shared_ptr<void> internal_state;
inline bool IsValid() const { return internal_state != nullptr; }
wi::allocator::shared_ptr<void> internal_state;
constexpr bool IsValid() const { return internal_state.IsValid(); }
SwapChainDesc desc;
@@ -1329,8 +1330,8 @@ namespace wi::graphics
};
struct RaytracingPipelineState
{
std::shared_ptr<void> internal_state;
inline bool IsValid() const { return internal_state != nullptr; }
wi::allocator::shared_ptr<void> internal_state;
constexpr bool IsValid() const { return internal_state.IsValid(); }
RaytracingPipelineStateDesc desc;
+12 -16
View File
@@ -1438,7 +1438,7 @@ namespace dx12_internal
ComPtr<ID3D12VersionedRootSignatureDeserializer> rootsig_deserializer;
const D3D12_VERSIONED_ROOT_SIGNATURE_DESC* rootsig_desc = nullptr;
std::shared_ptr<void> rootsig_desc_lifetime_extender;
wi::allocator::shared_ptr<void> rootsig_desc_lifetime_extender;
RootSignatureOptimizer rootsig_optimizer;
struct PSO_STREAM
@@ -3046,11 +3046,7 @@ std::mutex queue_locker;
bool GraphicsDevice_DX12::CreateSwapChain(const SwapChainDesc* desc, wi::platform::window_type window, SwapChain* swapchain) const
{
auto internal_state = std::static_pointer_cast<SwapChain_DX12>(swapchain->internal_state);
if (swapchain->internal_state == nullptr)
{
internal_state = std::make_shared<SwapChain_DX12>();
}
auto internal_state = swapchain->IsValid() ? wi::allocator::shared_ptr<SwapChain_DX12>(swapchain->internal_state) : wi::allocator::make_shared<SwapChain_DX12>();
internal_state->allocationhandler = allocationhandler;
swapchain->internal_state = internal_state;
swapchain->desc = *desc;
@@ -3264,7 +3260,7 @@ std::mutex queue_locker;
}
bool GraphicsDevice_DX12::CreateBuffer2(const GPUBufferDesc* desc, const std::function<void(void*)>& init_callback, GPUBuffer* buffer, const GPUResource* alias, uint64_t alias_offset) const
{
auto internal_state = std::make_shared<Resource_DX12>();
auto internal_state = wi::allocator::make_shared<Resource_DX12>();
internal_state->allocationhandler = allocationhandler;
buffer->internal_state = internal_state;
buffer->type = GPUResource::Type::BUFFER;
@@ -3495,7 +3491,7 @@ std::mutex queue_locker;
}
bool GraphicsDevice_DX12::CreateTexture(const TextureDesc* desc, const SubresourceData* initial_data, Texture* texture, const GPUResource* alias, uint64_t alias_offset) const
{
auto internal_state = std::make_shared<Texture_DX12>();
auto internal_state = wi::allocator::make_shared<Texture_DX12>();
internal_state->allocationhandler = allocationhandler;
texture->internal_state = internal_state;
texture->type = GPUResource::Type::TEXTURE;
@@ -3914,7 +3910,7 @@ std::mutex queue_locker;
}
bool GraphicsDevice_DX12::CreateShader(ShaderStage stage, const void* shadercode, size_t shadercode_size, Shader* shader) const
{
auto internal_state = std::make_shared<PipelineState_DX12>();
auto internal_state = wi::allocator::make_shared<PipelineState_DX12>();
internal_state->allocationhandler = allocationhandler;
shader->internal_state = internal_state;
@@ -3974,7 +3970,7 @@ std::mutex queue_locker;
}
bool GraphicsDevice_DX12::CreateSampler(const SamplerDesc* desc, Sampler* sampler) const
{
auto internal_state = std::make_shared<Sampler_DX12>();
auto internal_state = wi::allocator::make_shared<Sampler_DX12>();
internal_state->allocationhandler = allocationhandler;
sampler->internal_state = internal_state;
@@ -4020,7 +4016,7 @@ std::mutex queue_locker;
}
bool GraphicsDevice_DX12::CreateQueryHeap(const GPUQueryHeapDesc* desc, GPUQueryHeap* queryheap) const
{
auto internal_state = std::make_shared<QueryHeap_DX12>();
auto internal_state = wi::allocator::make_shared<QueryHeap_DX12>();
internal_state->allocationhandler = allocationhandler;
queryheap->internal_state = internal_state;
queryheap->desc = *desc;
@@ -4046,7 +4042,7 @@ std::mutex queue_locker;
}
bool GraphicsDevice_DX12::CreatePipelineState(const PipelineStateDesc* desc, PipelineState* pso, const RenderPassInfo* renderpass_info) const
{
auto internal_state = std::make_shared<PipelineState_DX12>();
auto internal_state = wi::allocator::make_shared<PipelineState_DX12>();
internal_state->allocationhandler = allocationhandler;
pso->internal_state = internal_state;
@@ -4286,7 +4282,7 @@ std::mutex queue_locker;
}
bool GraphicsDevice_DX12::CreateRaytracingAccelerationStructure(const RaytracingAccelerationStructureDesc* desc, RaytracingAccelerationStructure* bvh) const
{
auto internal_state = std::make_shared<BVH_DX12>();
auto internal_state = wi::allocator::make_shared<BVH_DX12>();
internal_state->allocationhandler = allocationhandler;
bvh->internal_state = internal_state;
bvh->type = GPUResource::Type::RAYTRACING_ACCELERATION_STRUCTURE;
@@ -4423,7 +4419,7 @@ std::mutex queue_locker;
}
bool GraphicsDevice_DX12::CreateRaytracingPipelineState(const RaytracingPipelineStateDesc* desc, RaytracingPipelineState* rtpso) const
{
auto internal_state = std::make_shared<RTPipelineState_DX12>();
auto internal_state = wi::allocator::make_shared<RTPipelineState_DX12>();
internal_state->allocationhandler = allocationhandler;
rtpso->internal_state = internal_state;
rtpso->desc = *desc;
@@ -4605,7 +4601,7 @@ std::mutex queue_locker;
video_decoder->support |= VideoDecoderSupportFlags::DPB_INDIVIDUAL_TEXTURES_SUPPORTED;
}
auto internal_state = std::make_shared<VideoDecoder_DX12>();
auto internal_state = wi::allocator::make_shared<VideoDecoder_DX12>();
internal_state->allocationhandler = allocationhandler;
video_decoder->internal_state = internal_state;
video_decoder->desc = *desc;
@@ -5790,7 +5786,7 @@ std::mutex queue_locker;
{
auto swapchain_internal = to_internal(swapchain);
auto internal_state = std::make_shared<Texture_DX12>();
auto internal_state = wi::allocator::make_shared<Texture_DX12>();
internal_state->allocationhandler = allocationhandler;
internal_state->resource = swapchain_internal->backBuffers[swapchain_internal->GetBufferIndex()];
+14 -17
View File
@@ -2163,13 +2163,14 @@ using namespace vulkan_internal;
// Blending:
uint32_t numBlendAttachments = 0;
VkPipelineColorBlendAttachmentState colorBlendAttachments[8] = {};
static BlendState::RenderTargetBlendState default_blend;
for (size_t i = 0; i < commandlist.renderpass_info.rt_count; ++i)
{
size_t attachmentIndex = 0;
if (pso->desc.bs->independent_blend_enable)
if (pso->desc.bs != nullptr && pso->desc.bs->independent_blend_enable)
attachmentIndex = i;
const auto& desc = pso->desc.bs->render_target[attachmentIndex];
const auto& desc = pso->desc.bs == nullptr ? default_blend : pso->desc.bs->render_target[attachmentIndex];
VkPipelineColorBlendAttachmentState& attachment = colorBlendAttachments[numBlendAttachments];
numBlendAttachments++;
@@ -3755,11 +3756,7 @@ using namespace vulkan_internal;
bool GraphicsDevice_Vulkan::CreateSwapChain(const SwapChainDesc* desc, wi::platform::window_type window, SwapChain* swapchain) const
{
auto internal_state = std::static_pointer_cast<SwapChain_Vulkan>(swapchain->internal_state);
if (swapchain->internal_state == nullptr)
{
internal_state = std::make_shared<SwapChain_Vulkan>();
}
auto internal_state = swapchain->IsValid() ? wi::allocator::shared_ptr<SwapChain_Vulkan>(swapchain->internal_state) : wi::allocator::make_shared<SwapChain_Vulkan>();
internal_state->allocationhandler = allocationhandler;
internal_state->desc = *desc;
swapchain->internal_state = internal_state;
@@ -3818,7 +3815,7 @@ using namespace vulkan_internal;
}
bool GraphicsDevice_Vulkan::CreateBuffer2(const GPUBufferDesc* desc, const std::function<void(void*)>& init_callback, GPUBuffer* buffer, const GPUResource* alias, uint64_t alias_offset) const
{
auto internal_state = std::make_shared<Buffer_Vulkan>();
auto internal_state = wi::allocator::make_shared<Buffer_Vulkan>();
internal_state->allocationhandler = allocationhandler;
buffer->internal_state = internal_state;
buffer->type = GPUResource::Type::BUFFER;
@@ -4161,7 +4158,7 @@ using namespace vulkan_internal;
alias_offset = 0;
#endif // PLATFORM_LINUX
auto internal_state = std::make_shared<Texture_Vulkan>();
auto internal_state = wi::allocator::make_shared<Texture_Vulkan>();
internal_state->allocationhandler = allocationhandler;
internal_state->defaultLayout = _ConvertImageLayout(desc->layout);
texture->internal_state = internal_state;
@@ -4763,7 +4760,7 @@ using namespace vulkan_internal;
}
bool GraphicsDevice_Vulkan::CreateShader(ShaderStage stage, const void* shadercode, size_t shadercode_size, Shader* shader) const
{
auto internal_state = std::make_shared<Shader_Vulkan>();
auto internal_state = wi::allocator::make_shared<Shader_Vulkan>();
internal_state->allocationhandler = allocationhandler;
shader->internal_state = internal_state;
shader->stage = stage;
@@ -5073,7 +5070,7 @@ using namespace vulkan_internal;
}
bool GraphicsDevice_Vulkan::CreateSampler(const SamplerDesc* desc, Sampler* sampler) const
{
auto internal_state = std::make_shared<Sampler_Vulkan>();
auto internal_state = wi::allocator::make_shared<Sampler_Vulkan>();
internal_state->allocationhandler = allocationhandler;
sampler->internal_state = internal_state;
sampler->desc = *desc;
@@ -5311,7 +5308,7 @@ using namespace vulkan_internal;
}
bool GraphicsDevice_Vulkan::CreateQueryHeap(const GPUQueryHeapDesc* desc, GPUQueryHeap* queryheap) const
{
auto internal_state = std::make_shared<QueryHeap_Vulkan>();
auto internal_state = wi::allocator::make_shared<QueryHeap_Vulkan>();
internal_state->allocationhandler = allocationhandler;
queryheap->internal_state = internal_state;
queryheap->desc = *desc;
@@ -5337,7 +5334,7 @@ using namespace vulkan_internal;
}
bool GraphicsDevice_Vulkan::CreatePipelineState(const PipelineStateDesc* desc, PipelineState* pso, const RenderPassInfo* renderpass_info) const
{
auto internal_state = std::make_shared<PipelineState_Vulkan>();
auto internal_state = wi::allocator::make_shared<PipelineState_Vulkan>();
internal_state->allocationhandler = allocationhandler;
pso->internal_state = internal_state;
pso->desc = *desc;
@@ -5967,7 +5964,7 @@ using namespace vulkan_internal;
}
bool GraphicsDevice_Vulkan::CreateRaytracingAccelerationStructure(const RaytracingAccelerationStructureDesc* desc, RaytracingAccelerationStructure* bvh) const
{
auto internal_state = std::make_shared<BVH_Vulkan>();
auto internal_state = wi::allocator::make_shared<BVH_Vulkan>();
internal_state->allocationhandler = allocationhandler;
bvh->internal_state = internal_state;
bvh->type = GPUResource::Type::RAYTRACING_ACCELERATION_STRUCTURE;
@@ -6145,7 +6142,7 @@ using namespace vulkan_internal;
}
bool GraphicsDevice_Vulkan::CreateRaytracingPipelineState(const RaytracingPipelineStateDesc* desc, RaytracingPipelineState* rtpso) const
{
auto internal_state = std::make_shared<RTPipelineState_Vulkan>();
auto internal_state = wi::allocator::make_shared<RTPipelineState_Vulkan>();
internal_state->allocationhandler = allocationhandler;
rtpso->internal_state = internal_state;
rtpso->desc = *desc;
@@ -6242,7 +6239,7 @@ using namespace vulkan_internal;
}
bool GraphicsDevice_Vulkan::CreateVideoDecoder(const VideoDesc* desc, VideoDecoder* video_decoder) const
{
auto internal_state = std::make_shared<VideoDecoder_Vulkan>();
auto internal_state = wi::allocator::make_shared<VideoDecoder_Vulkan>();
internal_state->allocationhandler = allocationhandler;
video_decoder->internal_state = internal_state;
video_decoder->desc = *desc;
@@ -7537,7 +7534,7 @@ using namespace vulkan_internal;
{
auto swapchain_internal = to_internal(swapchain);
auto internal_state = std::make_shared<Texture_Vulkan>();
auto internal_state = wi::allocator::make_shared<Texture_Vulkan>();
internal_state->resource = swapchain_internal->swapChainImages[swapchain_internal->swapChainImageIndex];
Texture result;
+4
View File
@@ -138,6 +138,10 @@ namespace wi::initializer
wilog("\nNo embedded shaders found, shaders will be compiled at runtime if needed.\n\tShader source path: %s\n\tShader binary path: %s", wi::renderer::GetShaderSourcePath().c_str(), wi::renderer::GetShaderPath().c_str());
}
#ifdef _DEBUG
wilog("\nNumber of shared block allocated types: %d", (int)wi::allocator::get_shared_block_allocator_count());
#endif // _DEBUG
wi::backlog::post("");
wi::jobsystem::Initialize();
+1 -1
View File
@@ -50,7 +50,7 @@ namespace wi
// This is an identifier of RenderPath subtype that is used for lua binding.
static constexpr const char* script_check_identifier = relative_path(__FILE__);
virtual const char* GetScriptBindingID() const { return script_check_identifier; }
const char* GetScriptBindingID() const override { return script_check_identifier; }
};
}
+1 -1
View File
@@ -106,7 +106,7 @@ namespace wi
// This is an identifier of RenderPath subtype that is used for lua binding.
static constexpr const char* script_check_identifier = relative_path(__FILE__);
virtual const char* GetScriptBindingID() const { return script_check_identifier; }
const char* GetScriptBindingID() const override { return script_check_identifier; }
};
}
+1 -1
View File
@@ -369,7 +369,7 @@ namespace wi
// This is an identifier of RenderPath subtype that is used for lua binding.
static constexpr const char* script_check_identifier = relative_path(__FILE__);
virtual const char* GetScriptBindingID() const { return script_check_identifier; }
const char* GetScriptBindingID() const override { return script_check_identifier; }
};
}
+1 -1
View File
@@ -47,7 +47,7 @@ namespace wi
// This is an identifier of RenderPath subtype that is used for lua binding.
static constexpr const char* script_check_identifier = relative_path(__FILE__);
virtual const char* GetScriptBindingID() const { return script_check_identifier; }
const char* GetScriptBindingID() const override { return script_check_identifier; }
};
}
+47 -22
View File
@@ -15,6 +15,14 @@
using namespace wi::graphics;
//#define RESOURCE_LOGGING
#ifdef RESOURCE_LOGGING
#define resource_log(str,...) wilog(str, ## __VA_ARGS__)
#else
#define resource_log(str,...)
#endif // RESOURCE_LOGGING
namespace wi
{
struct StreamingTexture
@@ -112,7 +120,7 @@ namespace wi
{
if (internal_state == nullptr)
{
internal_state = std::make_shared<ResourceInternal>();
internal_state = wi::allocator::make_shared<ResourceInternal>();
}
ResourceInternal* resourceinternal = (ResourceInternal*)internal_state.get();
resourceinternal->filedata = data;
@@ -121,7 +129,7 @@ namespace wi
{
if (internal_state == nullptr)
{
internal_state = std::make_shared<ResourceInternal>();
internal_state = wi::allocator::make_shared<ResourceInternal>();
}
ResourceInternal* resourceinternal = (ResourceInternal*)internal_state.get();
resourceinternal->filedata = data;
@@ -130,7 +138,7 @@ namespace wi
{
if (internal_state == nullptr)
{
internal_state = std::make_shared<ResourceInternal>();
internal_state = wi::allocator::make_shared<ResourceInternal>();
}
ResourceInternal* resourceinternal = (ResourceInternal*)internal_state.get();
resourceinternal->texture = texture;
@@ -140,7 +148,7 @@ namespace wi
{
if (internal_state == nullptr)
{
internal_state = std::make_shared<ResourceInternal>();
internal_state = wi::allocator::make_shared<ResourceInternal>();
}
ResourceInternal* resourceinternal = (ResourceInternal*)internal_state.get();
resourceinternal->tile_pool = tile_pool;
@@ -151,7 +159,7 @@ namespace wi
{
if (internal_state == nullptr)
{
internal_state = std::make_shared<ResourceInternal>();
internal_state = wi::allocator::make_shared<ResourceInternal>();
}
ResourceInternal* resourceinternal = (ResourceInternal*)internal_state.get();
resourceinternal->sound = sound;
@@ -160,7 +168,7 @@ namespace wi
{
if (internal_state == nullptr)
{
internal_state = std::make_shared<ResourceInternal>();
internal_state = wi::allocator::make_shared<ResourceInternal>();
}
ResourceInternal* resourceinternal = (ResourceInternal*)internal_state.get();
resourceinternal->script = script;
@@ -169,7 +177,7 @@ namespace wi
{
if (internal_state == nullptr)
{
internal_state = std::make_shared<ResourceInternal>();
internal_state = wi::allocator::make_shared<ResourceInternal>();
}
ResourceInternal* resourceinternal = (ResourceInternal*)internal_state.get();
resourceinternal->video = video;
@@ -179,7 +187,7 @@ namespace wi
{
if (internal_state == nullptr)
{
internal_state = std::make_shared<ResourceInternal>();
internal_state = wi::allocator::make_shared<ResourceInternal>();
}
ResourceInternal* resourceinternal = (ResourceInternal*)internal_state.get();
resourceinternal->timestamp = 0;
@@ -189,7 +197,7 @@ namespace wi
{
if (internal_state == nullptr)
{
internal_state = std::make_shared<ResourceInternal>();
internal_state = wi::allocator::make_shared<ResourceInternal>();
}
ResourceInternal* resourceinternal = (ResourceInternal*)internal_state.get();
resourceinternal->streaming_resolution.fetch_or(resolution);
@@ -198,7 +206,7 @@ namespace wi
namespace resourcemanager
{
static std::mutex locker;
static std::unordered_map<std::string, std::weak_ptr<ResourceInternal>> resources;
static wi::unordered_map<std::string, wi::allocator::weak_ptr<ResourceInternal>> resources;
static Mode mode = Mode::NO_EMBEDDING;
void SetMode(Mode param)
@@ -907,8 +915,8 @@ namespace wi
)
{
locker.lock();
std::weak_ptr<ResourceInternal>& weak_resource = resources[name];
std::shared_ptr<ResourceInternal> resource = weak_resource.lock();
wi::allocator::weak_ptr<ResourceInternal>& weak_resource = resources[name];
wi::allocator::shared_ptr<ResourceInternal> resource = weak_resource.lock();
uint64_t timestamp = 0;
if(!container_filename.empty())
@@ -922,7 +930,7 @@ namespace wi
if (resource == nullptr || resource->timestamp < timestamp)
{
resource = std::make_shared<ResourceInternal>();
resource = wi::allocator::make_shared<ResourceInternal>();
resources[name] = resource;
resource->filename = name;
@@ -957,6 +965,7 @@ namespace wi
}
else
{
resource_log("\tResource reused: %s", name.c_str());
Resource retVal;
retVal.internal_state = resource;
locker.unlock();
@@ -989,6 +998,7 @@ namespace wi
}
else
{
resource_log("\tResource loading: %s", name.c_str());
success = LoadResourceDirectly(name, flags, filedata, filesize, resource.get());
}
@@ -1027,10 +1037,10 @@ namespace wi
}
wi::jobsystem::context streaming_ctx;
wi::vector<std::shared_ptr<ResourceInternal>> streaming_texture_jobs;
wi::vector<wi::allocator::shared_ptr<ResourceInternal>> streaming_texture_jobs;
struct StreamingTextureReplace
{
std::shared_ptr<ResourceInternal> resource;
wi::allocator::shared_ptr<ResourceInternal> resource;
Texture texture;
int srgb_subresource = -1;
};
@@ -1069,8 +1079,8 @@ namespace wi
return; // Streaming is not that important, we can abandon it if some resource loading is holding the lock
for (auto& x : resources)
{
std::weak_ptr<ResourceInternal>& weak_resource = x.second;
std::shared_ptr<ResourceInternal> resource = weak_resource.lock();
wi::allocator::weak_ptr<ResourceInternal>& weak_resource = x.second;
wi::allocator::shared_ptr<ResourceInternal> resource = weak_resource.lock();
if (resource != nullptr && resource->texture.IsValid() && has_flag(resource->flags, Flags::STREAMING))
{
const TextureDesc& desc = resource->texture.desc;
@@ -1123,16 +1133,31 @@ namespace wi
streaming_texture_jobs.clear();
// Gather the streaming jobs:
static wi::vector<const std::string*> removals; // string ptr to avoid string copies, or string constructions from char*
// Gather the streaming jobs, unload lost resources:
for (auto& x : resources)
{
std::weak_ptr<ResourceInternal>& weak_resource = x.second;
std::shared_ptr<ResourceInternal> resource = weak_resource.lock();
if (x.second.expired())
{
removals.push_back(&x.first);
continue;
}
wi::allocator::weak_ptr<ResourceInternal>& weak_resource = x.second;
wi::allocator::shared_ptr<ResourceInternal> resource = weak_resource.lock();
if (resource != nullptr && resource->texture.IsValid() && resource->streaming_texture.mip_count > 1)
{
streaming_texture_jobs.push_back(resource);
}
}
for (auto& x : removals)
{
resource_log("\tResource lost: %s", x->c_str());
resources.erase(*x);
}
removals.clear();
locker.unlock();
if (streaming_texture_jobs.empty())
@@ -1400,7 +1425,7 @@ namespace wi
auto it = resources.find(name);
if (it == resources.end())
continue;
std::shared_ptr<ResourceInternal> resource = it->second.lock();
wi::allocator::shared_ptr<ResourceInternal> resource = it->second.lock();
if (resource != nullptr)
{
serializable_count++;
@@ -1414,7 +1439,7 @@ namespace wi
auto it = resources.find(name);
if (it == resources.end())
continue;
std::shared_ptr<ResourceInternal> resource = it->second.lock();
wi::allocator::shared_ptr<ResourceInternal> resource = it->second.lock();
if (resource != nullptr)
{
+2 -1
View File
@@ -7,6 +7,7 @@
#include "wiVector.h"
#include "wiVideo.h"
#include "wiUnorderedSet.h"
#include "wiAllocator.h"
#include <memory>
#include <string>
@@ -17,7 +18,7 @@ namespace wi
// It can be loaded from file or memory using wi::resourcemanager::Load()
struct Resource
{
std::shared_ptr<void> internal_state;
wi::allocator::shared_ptr<void> internal_state;
inline bool IsValid() const { return internal_state.get() != nullptr; }
const wi::vector<uint8_t>& GetFileData() const;
+2 -2
View File
@@ -549,7 +549,7 @@ namespace wi::shadercompiler
output.shadersize = pShader->GetBufferSize();
// keep the blob alive == keep shader pointer valid!
auto internal_state = std::make_shared<ComPtr<IDxcBlob>>();
auto internal_state = wi::allocator::make_shared<ComPtr<IDxcBlob>>();
*internal_state = pShader;
output.internal_state = internal_state;
}
@@ -724,7 +724,7 @@ namespace wi::shadercompiler
output.shadersize = code->GetBufferSize();
// keep the blob alive == keep shader pointer valid!
auto internal_state = std::make_shared<ComPtr<ID3D10Blob>>();
auto internal_state = wi::allocator::make_shared<ComPtr<ID3D10Blob>>();
*internal_state = code;
output.internal_state = internal_state;
}
+3 -2
View File
@@ -1,6 +1,7 @@
#pragma once
#include "wiGraphics.h"
#include "wiVector.h"
#include "wiAllocator.h"
#include <string>
@@ -28,8 +29,8 @@ namespace wi::shadercompiler
};
struct CompilerOutput
{
std::shared_ptr<void> internal_state;
inline bool IsValid() const { return internal_state.get() != nullptr; }
wi::allocator::shared_ptr<void> internal_state;
constexpr bool IsValid() const { return internal_state.IsValid(); }
const uint8_t* shaderdata = nullptr;
size_t shadersize = 0;
+1 -1
View File
@@ -1638,7 +1638,7 @@ namespace wi::terrain
if (chunk_data.vt == nullptr)
{
chunk_data.vt = std::make_shared<VirtualTexture>();
chunk_data.vt = wi::allocator::make_shared<VirtualTexture>();
}
VirtualTexture& vt = *chunk_data.vt;
+7 -7
View File
@@ -108,7 +108,7 @@ namespace wi::terrain
void init(uint32_t resolution);
void reset();
};
wi::unordered_map<uint32_t, wi::vector<std::shared_ptr<Residency>>> free_residencies; // per resolution residencies
wi::unordered_map<uint32_t, wi::vector<wi::allocator::shared_ptr<Residency>>> free_residencies; // per resolution residencies
bool allocate_tile(Tile& tile)
{
@@ -142,20 +142,20 @@ namespace wi::terrain
return ~0ull;
return physical_tiles[tile.x + tile.y * physical_tile_count_x].free_frames;
}
std::shared_ptr<Residency> allocate_residency(uint32_t resolution)
wi::allocator::shared_ptr<Residency> allocate_residency(uint32_t resolution)
{
if (free_residencies[resolution].empty())
{
std::shared_ptr<Residency> residency = std::make_shared<Residency>();
wi::allocator::shared_ptr<Residency> residency = wi::allocator::make_shared<Residency>();
residency->init(resolution);
free_residencies[resolution].push_back(residency);
}
std::shared_ptr<Residency> residency = free_residencies[resolution].back();
wi::allocator::shared_ptr<Residency> residency = free_residencies[resolution].back();
free_residencies[resolution].pop_back();
residency->reset();
return residency;
}
void free_residency(std::shared_ptr<Residency>& residency)
void free_residency(wi::allocator::shared_ptr<Residency>& residency)
{
if (residency == nullptr)
return;
@@ -170,7 +170,7 @@ namespace wi::terrain
struct VirtualTexture
{
std::shared_ptr<VirtualTextureAtlas::Residency> residency;
wi::allocator::shared_ptr<VirtualTextureAtlas::Residency> residency;
wi::vector<VirtualTextureAtlas::Tile> tiles;
uint32_t lod_count = 0;
uint32_t resolution = 0;
@@ -231,7 +231,7 @@ namespace wi::terrain
XMFLOAT3 position = XMFLOAT3(0, 0, 0);
bool visible = true;
bool invalidated = false;
std::shared_ptr<VirtualTexture> vt;
wi::allocator::shared_ptr<VirtualTexture> vt;
wi::vector<uint16_t> heightmap_data;
wi::graphics::Texture heightmap;
+8
View File
@@ -12,5 +12,13 @@
</ArrayItems>
</Expand>
</Type>
<Type Name="wi::allocator::shared_ptr&lt;*&gt;">
<DisplayString>{{ handle={handle} }}</DisplayString>
<Expand>
<Item Name="[ptr]">reinterpret_cast&lt;$T1*&gt;(handle &amp; (~0ull &lt;&lt; 8ull))</Item>
<Item Name="[allocator]">block_allocators[handle &amp; 0xFF]</Item>
</Expand>
</Type>
</AutoVisualizer>