mirror of
https://github.com/seekrs/MacroLibX.git
synced 2026-01-11 14:43:34 +00:00
Merge branch 'better_vk_commands' into indev
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2022/10/08 18:55:57 by maldavid #+# #+# */
|
||||
/* Updated: 2023/12/16 17:10:17 by maldavid ### ########.fr */
|
||||
/* Updated: 2023/12/17 17:35:03 by maldavid ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
@@ -22,6 +22,15 @@ namespace mlx
|
||||
{
|
||||
void Buffer::create(Buffer::kind type, VkDeviceSize size, VkBufferUsageFlags usage, const char* name, const void* data)
|
||||
{
|
||||
CmdResource::setDestroyer([this]()
|
||||
{
|
||||
if(_is_mapped)
|
||||
unmapMem();
|
||||
if(_buffer != VK_NULL_HANDLE)
|
||||
Render_Core::get().getAllocator().destroyBuffer(_allocation, _buffer);
|
||||
_buffer = VK_NULL_HANDLE;
|
||||
});
|
||||
|
||||
_usage = usage;
|
||||
if(type == Buffer::kind::constant || type == Buffer::kind::dynamic_device_local)
|
||||
{
|
||||
@@ -52,11 +61,7 @@ namespace mlx
|
||||
|
||||
void Buffer::destroy() noexcept
|
||||
{
|
||||
if(_is_mapped)
|
||||
unmapMem();
|
||||
if(_buffer != VK_NULL_HANDLE)
|
||||
Render_Core::get().getAllocator().destroyBuffer(_allocation, _buffer);
|
||||
_buffer = VK_NULL_HANDLE;
|
||||
CmdResource::requireDestroy();
|
||||
}
|
||||
|
||||
void Buffer::createBuffer(VkBufferUsageFlags usage, VmaAllocationCreateInfo info, VkDeviceSize size, [[maybe_unused]] const char* name)
|
||||
|
||||
@@ -16,10 +16,11 @@
|
||||
#include <mlx_profile.h>
|
||||
#include <volk.h>
|
||||
#include <renderer/core/render_core.h>
|
||||
#include <renderer/core/cmd_resource.h>
|
||||
|
||||
namespace mlx
|
||||
{
|
||||
class Buffer
|
||||
class Buffer : public CmdResource
|
||||
{
|
||||
public:
|
||||
enum class kind { dynamic, dynamic_device_local, uniform, constant };
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2023/04/02 17:50:52 by maldavid #+# #+# */
|
||||
/* Updated: 2023/04/02 17:51:46 by maldavid ### ########.fr */
|
||||
/* Updated: 2023/12/17 20:10:45 by maldavid ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace mlx
|
||||
{
|
||||
_cmd_pool.init();
|
||||
for(int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
|
||||
_cmd_buffers[i].init(this);
|
||||
_cmd_buffers[i].init(CmdBuffer::kind::long_time, this);
|
||||
}
|
||||
|
||||
void CmdManager::beginRecord(int active_image_index)
|
||||
|
||||
@@ -6,12 +6,13 @@
|
||||
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2023/12/15 19:57:49 by maldavid #+# #+# */
|
||||
/* Updated: 2023/12/16 18:46:26 by maldavid ### ########.fr */
|
||||
/* Updated: 2023/12/17 20:10:25 by maldavid ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include <algorithm>
|
||||
#include <renderer/command/single_time_cmd_manager.h>
|
||||
#include <renderer/command/vk_cmd_buffer.h>
|
||||
#include <renderer/core/render_core.h>
|
||||
|
||||
namespace mlx
|
||||
@@ -24,7 +25,7 @@ namespace mlx
|
||||
for(int i = 0; i < MIN_POOL_SIZE; i++)
|
||||
{
|
||||
_buffers.emplace_back();
|
||||
_buffers.back().init(&_pool);
|
||||
_buffers.back().init(CmdBuffer::kind::single_time, &_pool);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +39,7 @@ namespace mlx
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
_buffers.emplace_back().init(&_pool);
|
||||
_buffers.emplace_back().init(CmdBuffer::kind::single_time, &_pool);
|
||||
return _buffers.back();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2023/12/15 18:25:57 by maldavid #+# #+# */
|
||||
/* Updated: 2023/12/16 18:09:56 by maldavid ### ########.fr */
|
||||
/* Updated: 2023/12/17 17:36:18 by maldavid ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
@@ -16,10 +16,11 @@
|
||||
#include <vector>
|
||||
|
||||
#include <renderer/command/vk_cmd_pool.h>
|
||||
#include <renderer/command/vk_cmd_buffer.h>
|
||||
|
||||
namespace mlx
|
||||
{
|
||||
class CmdBuffer;
|
||||
|
||||
class SingleTimeCmdManager
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -14,16 +14,18 @@
|
||||
#include <renderer/core/render_core.h>
|
||||
#include <renderer/command/cmd_manager.h>
|
||||
#include <renderer/core/vk_semaphore.h>
|
||||
#include <renderer/buffers/vk_buffer.h>
|
||||
|
||||
namespace mlx
|
||||
{
|
||||
void CmdBuffer::init(CmdManager* manager)
|
||||
void CmdBuffer::init(kind type, CmdManager* manager)
|
||||
{
|
||||
init(&manager->getCmdPool());
|
||||
init(type, &manager->getCmdPool());
|
||||
}
|
||||
|
||||
void CmdBuffer::init(CmdPool* pool)
|
||||
void CmdBuffer::init(kind type, CmdPool* pool)
|
||||
{
|
||||
_type = type;
|
||||
_pool = pool;
|
||||
|
||||
VkCommandBufferAllocateInfo allocInfo{};
|
||||
@@ -59,6 +61,18 @@ namespace mlx
|
||||
_state = state::recording;
|
||||
}
|
||||
|
||||
void CmdBuffer::bindVertexBuffer(Buffer& buffer) const noexcept
|
||||
{
|
||||
if(!isRecording())
|
||||
{
|
||||
core::error::report(e_kind::warning, "Vulkan : trying to bind a vertex buffer to a non recording command buffer");
|
||||
return;
|
||||
}
|
||||
VkDeviceSize offset[] = { buffer.getOffset() };
|
||||
vkCmdBindVertexBuffers(_cmd_buffer, 0, 1, &buffer.get(), offset);
|
||||
buffer.recordedInCmdBuffer();
|
||||
}
|
||||
|
||||
void CmdBuffer::endRecord()
|
||||
{
|
||||
if(!isInit())
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
|
||||
namespace mlx
|
||||
{
|
||||
class Buffer;
|
||||
class Image;
|
||||
|
||||
class CmdBuffer
|
||||
{
|
||||
public:
|
||||
@@ -31,9 +34,15 @@ namespace mlx
|
||||
submitted, // buffer has been submitted
|
||||
};
|
||||
|
||||
enum class kind
|
||||
{
|
||||
single_time = 0,
|
||||
long_time
|
||||
};
|
||||
|
||||
public:
|
||||
void init(class CmdManager* manager);
|
||||
void init(class CmdPool* pool);
|
||||
void init(kind type, class CmdManager* manager);
|
||||
void init(kind type, class CmdPool* pool);
|
||||
void destroy() noexcept;
|
||||
|
||||
void beginRecord(VkCommandBufferUsageFlags usage = 0);
|
||||
@@ -43,6 +52,12 @@ namespace mlx
|
||||
inline void reset() noexcept { vkResetCommandBuffer(_cmd_buffer, 0); }
|
||||
void endRecord();
|
||||
|
||||
void bindVertexBuffer(Buffer& buffer) const noexcept;
|
||||
void bindIndexBuffer(Buffer& buffer) const noexcept;
|
||||
void copyBuffer(Buffer& dst, Buffer& src) const noexcept;
|
||||
void copyBufferToImage(Buffer& buffer, Image& image) const noexcept;
|
||||
void copyImagetoBuffer(Image& image, Buffer& buffer) const noexcept;
|
||||
|
||||
inline bool isInit() const noexcept { return _state != state::uninit; }
|
||||
inline bool isReadyToBeUsed() const noexcept { return _state == state::ready; }
|
||||
inline bool isRecording() const noexcept { return _state == state::recording; }
|
||||
@@ -58,6 +73,7 @@ namespace mlx
|
||||
VkCommandBuffer _cmd_buffer = VK_NULL_HANDLE;
|
||||
class CmdPool* _pool = nullptr;
|
||||
state _state = state::uninit;
|
||||
kind _type;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
62
src/renderer/core/cmd_resource.h
git.filemode.normal_file
62
src/renderer/core/cmd_resource.h
git.filemode.normal_file
@@ -0,0 +1,62 @@
|
||||
/* ************************************************************************** */
|
||||
/* */
|
||||
/* ::: :::::::: */
|
||||
/* cmd_resource.h :+: :+: :+: */
|
||||
/* +:+ +:+ +:+ */
|
||||
/* By: maldavid <kbz_8.dev@akel-engine.com> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2023/12/16 20:44:29 by maldavid #+# #+# */
|
||||
/* Updated: 2023/12/17 17:10:03 by maldavid ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
#ifndef __MLX_COMMAND_RESOURCE__
|
||||
#define __MLX_COMMAND_RESOURCE__
|
||||
|
||||
#include <function.h>
|
||||
|
||||
namespace mlx
|
||||
{
|
||||
class CmdResource
|
||||
{
|
||||
friend class SingleTimeCmdManager;
|
||||
public:
|
||||
enum class state
|
||||
{
|
||||
in_cmd_buffer = 0,
|
||||
out_cmd_buffer,
|
||||
};
|
||||
|
||||
public:
|
||||
CmdResource() = default;
|
||||
inline void recordedInCmdBuffer() noexcept { _state = state::in_cmd_buffer; }
|
||||
inline void removedFromCmdBuffer() noexcept
|
||||
{
|
||||
_state = state::out_cmd_buffer;
|
||||
if(_destroy_required && _destroy_required)
|
||||
{
|
||||
_destroyer();
|
||||
_destroy_required = false;
|
||||
}
|
||||
}
|
||||
inline void setDestroyer(func::function<void(void)>&& functor) { _destroyer = functor; }
|
||||
inline void requireDestroy() noexcept
|
||||
{
|
||||
if(_state == state::out_cmd_buffer && _destroyer)
|
||||
_destroyer();
|
||||
else
|
||||
_destroy_required = true;
|
||||
}
|
||||
virtual ~CmdResource() = default;
|
||||
|
||||
private:
|
||||
void realDestroy();
|
||||
|
||||
private:
|
||||
state _state = state::out_cmd_buffer;
|
||||
func::function<void(void)> _destroyer;
|
||||
bool _destroy_required = false;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -38,7 +38,7 @@ namespace mlx
|
||||
void setPixel(int x, int y, uint32_t color) noexcept;
|
||||
int getPixel(int x, int y) noexcept;
|
||||
|
||||
inline void setDescriptor(DescriptorSet set) noexcept { _set = std::move(set); }
|
||||
inline void setDescriptor(DescriptorSet&& set) noexcept { _set = set; }
|
||||
inline VkDescriptorSet getSet() noexcept { return _set.isInit() ? _set.get() : VK_NULL_HANDLE; }
|
||||
inline void updateSet(int binding) noexcept { _set.writeDescriptor(binding, *this); _has_been_updated = true; }
|
||||
inline bool hasBeenUpdated() const noexcept { return _has_been_updated; }
|
||||
|
||||
@@ -138,6 +138,16 @@ namespace mlx
|
||||
|
||||
void Image::create(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, const char* name, bool dedicated_memory)
|
||||
{
|
||||
CmdResource::setDestroyer([this]()
|
||||
{
|
||||
this->destroySampler();
|
||||
this->destroyImageView();
|
||||
|
||||
if(_image != VK_NULL_HANDLE)
|
||||
Render_Core::get().getAllocator().destroyImage(_allocation, _image);
|
||||
_image = VK_NULL_HANDLE;
|
||||
});
|
||||
|
||||
_width = width;
|
||||
_height = height;
|
||||
_format = format;
|
||||
@@ -332,12 +342,7 @@ namespace mlx
|
||||
|
||||
void Image::destroy() noexcept
|
||||
{
|
||||
destroySampler();
|
||||
destroyImageView();
|
||||
|
||||
if(_image != VK_NULL_HANDLE)
|
||||
Render_Core::get().getAllocator().destroyImage(_allocation, _image);
|
||||
_image = VK_NULL_HANDLE;
|
||||
CmdResource::requireDestroy();
|
||||
}
|
||||
|
||||
uint32_t formatSize(VkFormat format)
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include <vma.h>
|
||||
#include <renderer/core/cmd_resource.h>
|
||||
#include <renderer/command/vk_cmd_buffer.h>
|
||||
#include <renderer/command/vk_cmd_pool.h>
|
||||
|
||||
@@ -25,7 +26,7 @@ namespace mlx
|
||||
{
|
||||
uint32_t formatSize(VkFormat format);
|
||||
|
||||
class Image
|
||||
class Image : public CmdResource
|
||||
{
|
||||
friend class SwapChain;
|
||||
|
||||
|
||||
630
third_party/function.h
vendored
git.filemode.normal_file
630
third_party/function.h
vendored
git.filemode.normal_file
@@ -0,0 +1,630 @@
|
||||
/*
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org/>
|
||||
*/
|
||||
// despite that it would be nice if you give credit to Malte Skarupke
|
||||
|
||||
|
||||
#pragma once
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
#include <exception>
|
||||
#include <typeinfo>
|
||||
#include <memory>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define FUNC_NOEXCEPT
|
||||
#define FUNC_TEMPLATE_NOEXCEPT(FUNCTOR, ALLOCATOR)
|
||||
#define FUNC_CONSTEXPR const
|
||||
#else
|
||||
#define FUNC_NOEXCEPT noexcept
|
||||
#define FUNC_TEMPLATE_NOEXCEPT(FUNCTOR, ALLOCATOR) noexcept(detail::is_inplace_allocated<FUNCTOR, ALLOCATOR>::value)
|
||||
#define FUNC_CONSTEXPR constexpr
|
||||
#endif
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
#endif
|
||||
|
||||
#define FUNC_MOVE(value) static_cast<typename std::remove_reference<decltype(value)>::type &&>(value)
|
||||
#define FUNC_FORWARD(type, value) static_cast<type &&>(value)
|
||||
|
||||
namespace func
|
||||
{
|
||||
#ifndef FUNC_NO_EXCEPTIONS
|
||||
struct bad_function_call : std::exception
|
||||
{
|
||||
const char * what() const FUNC_NOEXCEPT override
|
||||
{
|
||||
return "Bad function call";
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
template<typename>
|
||||
struct force_function_heap_allocation
|
||||
: std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template<typename>
|
||||
class function;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
struct manager_storage_type;
|
||||
struct function_manager;
|
||||
struct functor_padding
|
||||
{
|
||||
protected:
|
||||
size_t padding_first;
|
||||
size_t padding_second;
|
||||
};
|
||||
|
||||
struct empty_struct
|
||||
{
|
||||
};
|
||||
|
||||
# ifndef FUNC_NO_EXCEPTIONS
|
||||
template<typename Result, typename... Arguments>
|
||||
Result empty_call(const functor_padding &, Arguments...)
|
||||
{
|
||||
throw bad_function_call();
|
||||
}
|
||||
# endif
|
||||
|
||||
template<typename T, typename Allocator>
|
||||
struct is_inplace_allocated
|
||||
{
|
||||
static const bool value
|
||||
// so that it fits
|
||||
= sizeof(T) <= sizeof(functor_padding)
|
||||
// so that it will be aligned
|
||||
&& std::alignment_of<functor_padding>::value % std::alignment_of<T>::value == 0
|
||||
// so that we can offer noexcept move
|
||||
&& std::is_nothrow_move_constructible<T>::value
|
||||
// so that the user can override it
|
||||
&& !force_function_heap_allocation<T>::value;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
T to_functor(T && func)
|
||||
{
|
||||
return FUNC_FORWARD(T, func);
|
||||
}
|
||||
template<typename Result, typename Class, typename... Arguments>
|
||||
auto to_functor(Result (Class::*func)(Arguments...)) -> decltype(std::mem_fn(func))
|
||||
{
|
||||
return std::mem_fn(func);
|
||||
}
|
||||
template<typename Result, typename Class, typename... Arguments>
|
||||
auto to_functor(Result (Class::*func)(Arguments...) const) -> decltype(std::mem_fn(func))
|
||||
{
|
||||
return std::mem_fn(func);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct functor_type
|
||||
{
|
||||
typedef decltype(to_functor(std::declval<T>())) type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
bool is_null(const T &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
template<typename Result, typename... Arguments>
|
||||
bool is_null(Result (* const & function_pointer)(Arguments...))
|
||||
{
|
||||
return function_pointer == nullptr;
|
||||
}
|
||||
template<typename Result, typename Class, typename... Arguments>
|
||||
bool is_null(Result (Class::* const & function_pointer)(Arguments...))
|
||||
{
|
||||
return function_pointer == nullptr;
|
||||
}
|
||||
template<typename Result, typename Class, typename... Arguments>
|
||||
bool is_null(Result (Class::* const & function_pointer)(Arguments...) const)
|
||||
{
|
||||
return function_pointer == nullptr;
|
||||
}
|
||||
|
||||
template<typename, typename>
|
||||
struct is_valid_function_argument
|
||||
{
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template<typename Result, typename... Arguments>
|
||||
struct is_valid_function_argument<function<Result (Arguments...)>, Result (Arguments...)>
|
||||
{
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template<typename T, typename Result, typename... Arguments>
|
||||
struct is_valid_function_argument<T, Result (Arguments...)>
|
||||
{
|
||||
# ifdef _MSC_VER
|
||||
// as of january 2013 visual studio doesn't support the SFINAE below
|
||||
static const bool value = true;
|
||||
# else
|
||||
template<typename U>
|
||||
static decltype(to_functor(std::declval<U>())(std::declval<Arguments>()...)) check(U *);
|
||||
template<typename>
|
||||
static empty_struct check(...);
|
||||
|
||||
static const bool value = std::is_convertible<decltype(check<T>(nullptr)), Result>::value;
|
||||
# endif
|
||||
};
|
||||
|
||||
typedef const function_manager * manager_type;
|
||||
|
||||
struct manager_storage_type
|
||||
{
|
||||
template<typename Allocator>
|
||||
Allocator & get_allocator() FUNC_NOEXCEPT
|
||||
{
|
||||
return reinterpret_cast<Allocator &>(manager);
|
||||
}
|
||||
template<typename Allocator>
|
||||
const Allocator & get_allocator() const FUNC_NOEXCEPT
|
||||
{
|
||||
return reinterpret_cast<const Allocator &>(manager);
|
||||
}
|
||||
|
||||
functor_padding functor;
|
||||
manager_type manager;
|
||||
};
|
||||
|
||||
template<typename T, typename Allocator, typename Enable = void>
|
||||
struct function_manager_inplace_specialization
|
||||
{
|
||||
template<typename Result, typename... Arguments>
|
||||
static Result call(const functor_padding & storage, Arguments... arguments)
|
||||
{
|
||||
// do not call get_functor_ref because I want this function to be fast
|
||||
// in debug when nothing gets inlined
|
||||
return const_cast<T &>(reinterpret_cast<const T &>(storage))(FUNC_FORWARD(Arguments, arguments)...);
|
||||
}
|
||||
|
||||
static void store_functor(manager_storage_type & storage, T to_store)
|
||||
{
|
||||
new (&get_functor_ref(storage)) T(FUNC_FORWARD(T, to_store));
|
||||
}
|
||||
static void move_functor(manager_storage_type & lhs, manager_storage_type && rhs) FUNC_NOEXCEPT
|
||||
{
|
||||
new (&get_functor_ref(lhs)) T(FUNC_MOVE(get_functor_ref(rhs)));
|
||||
}
|
||||
static void destroy_functor(Allocator &, manager_storage_type & storage) FUNC_NOEXCEPT
|
||||
{
|
||||
get_functor_ref(storage).~T();
|
||||
}
|
||||
static T & get_functor_ref(const manager_storage_type & storage) FUNC_NOEXCEPT
|
||||
{
|
||||
return const_cast<T &>(reinterpret_cast<const T &>(storage.functor));
|
||||
}
|
||||
};
|
||||
template<typename T, typename Allocator>
|
||||
struct function_manager_inplace_specialization<T, Allocator, typename std::enable_if<!is_inplace_allocated<T, Allocator>::value>::type>
|
||||
{
|
||||
template<typename Result, typename... Arguments>
|
||||
static Result call(const functor_padding & storage, Arguments... arguments)
|
||||
{
|
||||
// do not call get_functor_ptr_ref because I want this function to be fast
|
||||
// in debug when nothing gets inlined
|
||||
return (*reinterpret_cast<const typename std::allocator_traits<Allocator>::pointer &>(storage))(FUNC_FORWARD(Arguments, arguments)...);
|
||||
}
|
||||
|
||||
static void store_functor(manager_storage_type & self, T to_store)
|
||||
{
|
||||
Allocator & allocator = self.get_allocator<Allocator>();;
|
||||
static_assert(sizeof(typename std::allocator_traits<Allocator>::pointer) <= sizeof(self.functor), "The allocator's pointer type is too big");
|
||||
typename std::allocator_traits<Allocator>::pointer * ptr = new (&get_functor_ptr_ref(self)) typename std::allocator_traits<Allocator>::pointer(std::allocator_traits<Allocator>::allocate(allocator, 1));
|
||||
std::allocator_traits<Allocator>::construct(allocator, *ptr, FUNC_FORWARD(T, to_store));
|
||||
}
|
||||
static void move_functor(manager_storage_type & lhs, manager_storage_type && rhs) FUNC_NOEXCEPT
|
||||
{
|
||||
static_assert(std::is_nothrow_move_constructible<typename std::allocator_traits<Allocator>::pointer>::value, "we can't offer a noexcept swap if the pointer type is not nothrow move constructible");
|
||||
new (&get_functor_ptr_ref(lhs)) typename std::allocator_traits<Allocator>::pointer(FUNC_MOVE(get_functor_ptr_ref(rhs)));
|
||||
// this next assignment makes the destroy function easier
|
||||
get_functor_ptr_ref(rhs) = nullptr;
|
||||
}
|
||||
static void destroy_functor(Allocator & allocator, manager_storage_type & storage) FUNC_NOEXCEPT
|
||||
{
|
||||
typename std::allocator_traits<Allocator>::pointer & pointer = get_functor_ptr_ref(storage);
|
||||
if (!pointer) return;
|
||||
std::allocator_traits<Allocator>::destroy(allocator, pointer);
|
||||
std::allocator_traits<Allocator>::deallocate(allocator, pointer, 1);
|
||||
}
|
||||
static T & get_functor_ref(const manager_storage_type & storage) FUNC_NOEXCEPT
|
||||
{
|
||||
return *get_functor_ptr_ref(storage);
|
||||
}
|
||||
static typename std::allocator_traits<Allocator>::pointer & get_functor_ptr_ref(manager_storage_type & storage) FUNC_NOEXCEPT
|
||||
{
|
||||
return reinterpret_cast<typename std::allocator_traits<Allocator>::pointer &>(storage.functor);
|
||||
}
|
||||
static const typename std::allocator_traits<Allocator>::pointer & get_functor_ptr_ref(const manager_storage_type & storage) FUNC_NOEXCEPT
|
||||
{
|
||||
return reinterpret_cast<const typename std::allocator_traits<Allocator>::pointer &>(storage.functor);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename Allocator>
|
||||
static const function_manager & get_default_manager();
|
||||
|
||||
template<typename T, typename Allocator>
|
||||
static void create_manager(manager_storage_type & storage, Allocator && allocator)
|
||||
{
|
||||
new (&storage.get_allocator<Allocator>()) Allocator(FUNC_MOVE(allocator));
|
||||
storage.manager = &get_default_manager<T, Allocator>();
|
||||
}
|
||||
|
||||
// this struct acts as a vtable. it is an optimization to prevent
|
||||
// code-bloat from rtti. see the documentation of boost::function
|
||||
struct function_manager
|
||||
{
|
||||
template<typename T, typename Allocator>
|
||||
inline static FUNC_CONSTEXPR function_manager create_default_manager()
|
||||
{
|
||||
# ifdef _MSC_VER
|
||||
function_manager result =
|
||||
# else
|
||||
return function_manager
|
||||
# endif
|
||||
{
|
||||
&templated_call_move_and_destroy<T, Allocator>,
|
||||
&templated_call_copy<T, Allocator>,
|
||||
&templated_call_copy_functor_only<T, Allocator>,
|
||||
&templated_call_destroy<T, Allocator>,
|
||||
# ifndef FUNC_NO_RTTI
|
||||
&templated_call_type_id<T, Allocator>,
|
||||
&templated_call_target<T, Allocator>
|
||||
# endif
|
||||
};
|
||||
# ifdef _MSC_VER
|
||||
return result;
|
||||
# endif
|
||||
}
|
||||
|
||||
void (* const call_move_and_destroy)(manager_storage_type & lhs, manager_storage_type && rhs);
|
||||
void (* const call_copy)(manager_storage_type & lhs, const manager_storage_type & rhs);
|
||||
void (* const call_copy_functor_only)(manager_storage_type & lhs, const manager_storage_type & rhs);
|
||||
void (* const call_destroy)(manager_storage_type & manager);
|
||||
# ifndef FUNC_NO_RTTI
|
||||
const std::type_info & (* const call_type_id)();
|
||||
void * (* const call_target)(const manager_storage_type & manager, const std::type_info & type);
|
||||
# endif
|
||||
|
||||
template<typename T, typename Allocator>
|
||||
static void templated_call_move_and_destroy(manager_storage_type & lhs, manager_storage_type && rhs)
|
||||
{
|
||||
typedef function_manager_inplace_specialization<T, Allocator> specialization;
|
||||
specialization::move_functor(lhs, FUNC_MOVE(rhs));
|
||||
specialization::destroy_functor(rhs.get_allocator<Allocator>(), rhs);
|
||||
create_manager<T, Allocator>(lhs, FUNC_MOVE(rhs.get_allocator<Allocator>()));
|
||||
rhs.get_allocator<Allocator>().~Allocator();
|
||||
}
|
||||
template<typename T, typename Allocator>
|
||||
static void templated_call_copy(manager_storage_type & lhs, const manager_storage_type & rhs)
|
||||
{
|
||||
typedef function_manager_inplace_specialization<T, Allocator> specialization;
|
||||
create_manager<T, Allocator>(lhs, Allocator(rhs.get_allocator<Allocator>()));
|
||||
specialization::store_functor(lhs, specialization::get_functor_ref(rhs));
|
||||
}
|
||||
template<typename T, typename Allocator>
|
||||
static void templated_call_destroy(manager_storage_type & self)
|
||||
{
|
||||
typedef function_manager_inplace_specialization<T, Allocator> specialization;
|
||||
specialization::destroy_functor(self.get_allocator<Allocator>(), self);
|
||||
self.get_allocator<Allocator>().~Allocator();
|
||||
}
|
||||
template<typename T, typename Allocator>
|
||||
static void templated_call_copy_functor_only(manager_storage_type & lhs, const manager_storage_type & rhs)
|
||||
{
|
||||
typedef function_manager_inplace_specialization<T, Allocator> specialization;
|
||||
specialization::store_functor(lhs, specialization::get_functor_ref(rhs));
|
||||
}
|
||||
# ifndef FUNC_NO_RTTI
|
||||
template<typename T, typename>
|
||||
static const std::type_info & templated_call_type_id()
|
||||
{
|
||||
return typeid(T);
|
||||
}
|
||||
template<typename T, typename Allocator>
|
||||
static void * templated_call_target(const manager_storage_type & self, const std::type_info & type)
|
||||
{
|
||||
typedef function_manager_inplace_specialization<T, Allocator> specialization;
|
||||
if (type == typeid(T))
|
||||
return &specialization::get_functor_ref(self);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
# endif
|
||||
};
|
||||
template<typename T, typename Allocator>
|
||||
inline static const function_manager & get_default_manager()
|
||||
{
|
||||
static FUNC_CONSTEXPR function_manager default_manager = function_manager::create_default_manager<T, Allocator>();
|
||||
return default_manager;
|
||||
}
|
||||
|
||||
template<typename Result, typename...>
|
||||
struct typedeffer
|
||||
{
|
||||
typedef Result result_type;
|
||||
};
|
||||
template<typename Result, typename Argument>
|
||||
struct typedeffer<Result, Argument>
|
||||
{
|
||||
typedef Result result_type;
|
||||
typedef Argument argument_type;
|
||||
};
|
||||
template<typename Result, typename First_Argument, typename Second_Argument>
|
||||
struct typedeffer<Result, First_Argument, Second_Argument>
|
||||
{
|
||||
typedef Result result_type;
|
||||
typedef First_Argument first_argument_type;
|
||||
typedef Second_Argument second_argument_type;
|
||||
};
|
||||
}
|
||||
|
||||
template<typename Result, typename... Arguments>
|
||||
class function<Result (Arguments...)>
|
||||
: public detail::typedeffer<Result, Arguments...>
|
||||
{
|
||||
public:
|
||||
function() FUNC_NOEXCEPT
|
||||
{
|
||||
initialize_empty();
|
||||
}
|
||||
function(std::nullptr_t) FUNC_NOEXCEPT
|
||||
{
|
||||
initialize_empty();
|
||||
}
|
||||
function(function && other) FUNC_NOEXCEPT
|
||||
{
|
||||
initialize_empty();
|
||||
swap(other);
|
||||
}
|
||||
function(const function & other)
|
||||
: call(other.call)
|
||||
{
|
||||
other.manager_storage.manager->call_copy(manager_storage, other.manager_storage);
|
||||
}
|
||||
template<typename T>
|
||||
function(T functor,
|
||||
typename std::enable_if<detail::is_valid_function_argument<T, Result (Arguments...)>::value, detail::empty_struct>::type = detail::empty_struct()) FUNC_TEMPLATE_NOEXCEPT(T, std::allocator<typename detail::functor_type<T>::type>)
|
||||
{
|
||||
if (detail::is_null(functor))
|
||||
{
|
||||
initialize_empty();
|
||||
}
|
||||
else
|
||||
{
|
||||
typedef typename detail::functor_type<T>::type functor_type;
|
||||
initialize(detail::to_functor(FUNC_FORWARD(T, functor)), std::allocator<functor_type>());
|
||||
}
|
||||
}
|
||||
template<typename Allocator>
|
||||
function(std::allocator_arg_t, const Allocator &)
|
||||
{
|
||||
// ignore the allocator because I don't allocate
|
||||
initialize_empty();
|
||||
}
|
||||
template<typename Allocator>
|
||||
function(std::allocator_arg_t, const Allocator &, std::nullptr_t)
|
||||
{
|
||||
// ignore the allocator because I don't allocate
|
||||
initialize_empty();
|
||||
}
|
||||
template<typename Allocator, typename T>
|
||||
function(std::allocator_arg_t, const Allocator & allocator, T functor,
|
||||
typename std::enable_if<detail::is_valid_function_argument<T, Result (Arguments...)>::value, detail::empty_struct>::type = detail::empty_struct())
|
||||
FUNC_TEMPLATE_NOEXCEPT(T, Allocator)
|
||||
{
|
||||
if (detail::is_null(functor))
|
||||
{
|
||||
initialize_empty();
|
||||
}
|
||||
else
|
||||
{
|
||||
initialize(detail::to_functor(FUNC_FORWARD(T, functor)), Allocator(allocator));
|
||||
}
|
||||
}
|
||||
template<typename Allocator>
|
||||
function(std::allocator_arg_t, const Allocator & allocator, const function & other)
|
||||
: call(other.call)
|
||||
{
|
||||
typedef typename std::allocator_traits<Allocator>::template rebind_alloc<function> MyAllocator;
|
||||
|
||||
// first try to see if the allocator matches the target type
|
||||
detail::manager_type manager_for_allocator = &detail::get_default_manager<typename std::allocator_traits<Allocator>::value_type, Allocator>();
|
||||
if (other.manager_storage.manager == manager_for_allocator)
|
||||
{
|
||||
detail::create_manager<typename std::allocator_traits<Allocator>::value_type, Allocator>(manager_storage, Allocator(allocator));
|
||||
manager_for_allocator->call_copy_functor_only(manager_storage, other.manager_storage);
|
||||
}
|
||||
// if it does not, try to see if the target contains my type. this
|
||||
// breaks the recursion of the last case. otherwise repeated copies
|
||||
// would allocate more and more memory
|
||||
else
|
||||
{
|
||||
detail::manager_type manager_for_function = &detail::get_default_manager<function, MyAllocator>();
|
||||
if (other.manager_storage.manager == manager_for_function)
|
||||
{
|
||||
detail::create_manager<function, MyAllocator>(manager_storage, MyAllocator(allocator));
|
||||
manager_for_function->call_copy_functor_only(manager_storage, other.manager_storage);
|
||||
}
|
||||
else
|
||||
{
|
||||
// else store the other function as my target
|
||||
initialize(other, MyAllocator(allocator));
|
||||
}
|
||||
}
|
||||
}
|
||||
template<typename Allocator>
|
||||
function(std::allocator_arg_t, const Allocator &, function && other) FUNC_NOEXCEPT
|
||||
{
|
||||
// ignore the allocator because I don't allocate
|
||||
initialize_empty();
|
||||
swap(other);
|
||||
}
|
||||
|
||||
function & operator=(function other) FUNC_NOEXCEPT
|
||||
{
|
||||
swap(other);
|
||||
return *this;
|
||||
}
|
||||
~function() FUNC_NOEXCEPT
|
||||
{
|
||||
manager_storage.manager->call_destroy(manager_storage);
|
||||
}
|
||||
|
||||
Result operator()(Arguments... arguments) const
|
||||
{
|
||||
return call(manager_storage.functor, FUNC_FORWARD(Arguments, arguments)...);
|
||||
}
|
||||
|
||||
template<typename T, typename Allocator>
|
||||
void assign(T && functor, const Allocator & allocator) FUNC_TEMPLATE_NOEXCEPT(T, Allocator)
|
||||
{
|
||||
function(std::allocator_arg, allocator, functor).swap(*this);
|
||||
}
|
||||
|
||||
void swap(function & other) FUNC_NOEXCEPT
|
||||
{
|
||||
detail::manager_storage_type temp_storage;
|
||||
other.manager_storage.manager->call_move_and_destroy(temp_storage, FUNC_MOVE(other.manager_storage));
|
||||
manager_storage.manager->call_move_and_destroy(other.manager_storage, FUNC_MOVE(manager_storage));
|
||||
temp_storage.manager->call_move_and_destroy(manager_storage, FUNC_MOVE(temp_storage));
|
||||
|
||||
std::swap(call, other.call);
|
||||
}
|
||||
|
||||
|
||||
# ifndef FUNC_NO_RTTI
|
||||
const std::type_info & target_type() const FUNC_NOEXCEPT
|
||||
{
|
||||
return manager_storage.manager->call_type_id();
|
||||
}
|
||||
template<typename T>
|
||||
T * target() FUNC_NOEXCEPT
|
||||
{
|
||||
return static_cast<T *>(manager_storage.manager->call_target(manager_storage, typeid(T)));
|
||||
}
|
||||
template<typename T>
|
||||
const T * target() const FUNC_NOEXCEPT
|
||||
{
|
||||
return static_cast<const T *>(manager_storage.manager->call_target(manager_storage, typeid(T)));
|
||||
}
|
||||
# endif
|
||||
|
||||
operator bool() const FUNC_NOEXCEPT
|
||||
{
|
||||
|
||||
# ifdef FUNC_NO_EXCEPTIONS
|
||||
return call != nullptr;
|
||||
# else
|
||||
return call != &detail::empty_call<Result, Arguments...>;
|
||||
# endif
|
||||
}
|
||||
|
||||
private:
|
||||
detail::manager_storage_type manager_storage;
|
||||
Result (*call)(const detail::functor_padding &, Arguments...);
|
||||
|
||||
template<typename T, typename Allocator>
|
||||
void initialize(T functor, Allocator && allocator)
|
||||
{
|
||||
call = &detail::function_manager_inplace_specialization<T, Allocator>::template call<Result, Arguments...>;
|
||||
detail::create_manager<T, Allocator>(manager_storage, FUNC_FORWARD(Allocator, allocator));
|
||||
detail::function_manager_inplace_specialization<T, Allocator>::store_functor(manager_storage, FUNC_FORWARD(T, functor));
|
||||
}
|
||||
|
||||
typedef Result(*Empty_Function_Type)(Arguments...);
|
||||
void initialize_empty() FUNC_NOEXCEPT
|
||||
{
|
||||
typedef std::allocator<Empty_Function_Type> Allocator;
|
||||
static_assert(detail::is_inplace_allocated<Empty_Function_Type, Allocator>::value, "The empty function should benefit from small functor optimization");
|
||||
|
||||
detail::create_manager<Empty_Function_Type, Allocator>(manager_storage, Allocator());
|
||||
detail::function_manager_inplace_specialization<Empty_Function_Type, Allocator>::store_functor(manager_storage, nullptr);
|
||||
# ifdef FUNC_NO_EXCEPTIONS
|
||||
call = nullptr;
|
||||
# else
|
||||
call = &detail::empty_call<Result, Arguments...>;
|
||||
# endif
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
bool operator==(std::nullptr_t, const function<T> & rhs) FUNC_NOEXCEPT
|
||||
{
|
||||
return !rhs;
|
||||
}
|
||||
template<typename T>
|
||||
bool operator==(const function<T> & lhs, std::nullptr_t) FUNC_NOEXCEPT
|
||||
{
|
||||
return !lhs;
|
||||
}
|
||||
template<typename T>
|
||||
bool operator!=(std::nullptr_t, const function<T> & rhs) FUNC_NOEXCEPT
|
||||
{
|
||||
return rhs;
|
||||
}
|
||||
template<typename T>
|
||||
bool operator!=(const function<T> & lhs, std::nullptr_t) FUNC_NOEXCEPT
|
||||
{
|
||||
return lhs;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void swap(function<T> & lhs, function<T> & rhs)
|
||||
{
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
} // end namespace func
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<typename Result, typename... Arguments, typename Allocator>
|
||||
struct uses_allocator<func::function<Result (Arguments...)>, Allocator>
|
||||
: std::true_type
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
#undef FUNC_NOEXCEPT
|
||||
#undef FUNC_TEMPLATE_NOEXCEPT
|
||||
#undef FUNC_FORWARD
|
||||
#undef FUNC_MOVE
|
||||
#undef FUNC_CONSTEXPR
|
||||
Reference in New Issue
Block a user