diff --git a/src/renderer/buffers/vk_buffer.cpp b/src/renderer/buffers/vk_buffer.cpp index 903de12..a092d6d 100644 --- a/src/renderer/buffers/vk_buffer.cpp +++ b/src/renderer/buffers/vk_buffer.cpp @@ -6,7 +6,7 @@ /* By: maldavid +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* 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) diff --git a/src/renderer/buffers/vk_buffer.h b/src/renderer/buffers/vk_buffer.h index 203b1ce..65eebc1 100644 --- a/src/renderer/buffers/vk_buffer.h +++ b/src/renderer/buffers/vk_buffer.h @@ -16,10 +16,11 @@ #include #include #include +#include namespace mlx { - class Buffer + class Buffer : public CmdResource { public: enum class kind { dynamic, dynamic_device_local, uniform, constant }; diff --git a/src/renderer/command/cmd_manager.cpp b/src/renderer/command/cmd_manager.cpp index 37524a5..1e6118e 100644 --- a/src/renderer/command/cmd_manager.cpp +++ b/src/renderer/command/cmd_manager.cpp @@ -6,7 +6,7 @@ /* By: maldavid +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* 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) diff --git a/src/renderer/command/single_time_cmd_manager.cpp b/src/renderer/command/single_time_cmd_manager.cpp index 7d1ffc8..2feb498 100644 --- a/src/renderer/command/single_time_cmd_manager.cpp +++ b/src/renderer/command/single_time_cmd_manager.cpp @@ -6,12 +6,13 @@ /* By: maldavid +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* 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 #include +#include #include 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(); } diff --git a/src/renderer/command/single_time_cmd_manager.h b/src/renderer/command/single_time_cmd_manager.h index bf08869..ebba883 100644 --- a/src/renderer/command/single_time_cmd_manager.h +++ b/src/renderer/command/single_time_cmd_manager.h @@ -6,7 +6,7 @@ /* By: maldavid +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* 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 #include -#include namespace mlx { + class CmdBuffer; + class SingleTimeCmdManager { public: diff --git a/src/renderer/command/vk_cmd_buffer.cpp b/src/renderer/command/vk_cmd_buffer.cpp index 7405dc7..ec0f855 100644 --- a/src/renderer/command/vk_cmd_buffer.cpp +++ b/src/renderer/command/vk_cmd_buffer.cpp @@ -14,16 +14,18 @@ #include #include #include +#include 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()) diff --git a/src/renderer/command/vk_cmd_buffer.h b/src/renderer/command/vk_cmd_buffer.h index 1e7bccf..93fd55f 100644 --- a/src/renderer/command/vk_cmd_buffer.h +++ b/src/renderer/command/vk_cmd_buffer.h @@ -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; }; } diff --git a/src/renderer/core/cmd_resource.h b/src/renderer/core/cmd_resource.h new file mode 100644 index 0000000..8abd63b --- /dev/null +++ b/src/renderer/core/cmd_resource.h @@ -0,0 +1,62 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* cmd_resource.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: maldavid +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* 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 + +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&& 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 _destroyer; + bool _destroy_required = false; + }; +} + +#endif diff --git a/src/renderer/images/texture.h b/src/renderer/images/texture.h index 19f7d24..a7690d6 100644 --- a/src/renderer/images/texture.h +++ b/src/renderer/images/texture.h @@ -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; } diff --git a/src/renderer/images/vk_image.cpp b/src/renderer/images/vk_image.cpp index 9cca944..a6703c2 100644 --- a/src/renderer/images/vk_image.cpp +++ b/src/renderer/images/vk_image.cpp @@ -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) diff --git a/src/renderer/images/vk_image.h b/src/renderer/images/vk_image.h index f552fee..7cd4d65 100644 --- a/src/renderer/images/vk_image.h +++ b/src/renderer/images/vk_image.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -25,7 +26,7 @@ namespace mlx { uint32_t formatSize(VkFormat format); - class Image + class Image : public CmdResource { friend class SwapChain; diff --git a/third_party/function.h b/third_party/function.h new file mode 100644 index 0000000..291d46a --- /dev/null +++ b/third_party/function.h @@ -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 + */ +// despite that it would be nice if you give credit to Malte Skarupke + + +#pragma once +#include +#include +#include +#include +#include +#include + +#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::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::type &&>(value) +#define FUNC_FORWARD(type, value) static_cast(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 +struct force_function_heap_allocation + : std::false_type +{ +}; + +template +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 + Result empty_call(const functor_padding &, Arguments...) + { + throw bad_function_call(); + } +# endif + + template + 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::value % std::alignment_of::value == 0 + // so that we can offer noexcept move + && std::is_nothrow_move_constructible::value + // so that the user can override it + && !force_function_heap_allocation::value; + }; + + template + T to_functor(T && func) + { + return FUNC_FORWARD(T, func); + } + template + auto to_functor(Result (Class::*func)(Arguments...)) -> decltype(std::mem_fn(func)) + { + return std::mem_fn(func); + } + template + auto to_functor(Result (Class::*func)(Arguments...) const) -> decltype(std::mem_fn(func)) + { + return std::mem_fn(func); + } + + template + struct functor_type + { + typedef decltype(to_functor(std::declval())) type; + }; + + template + bool is_null(const T &) + { + return false; + } + template + bool is_null(Result (* const & function_pointer)(Arguments...)) + { + return function_pointer == nullptr; + } + template + bool is_null(Result (Class::* const & function_pointer)(Arguments...)) + { + return function_pointer == nullptr; + } + template + bool is_null(Result (Class::* const & function_pointer)(Arguments...) const) + { + return function_pointer == nullptr; + } + + template + struct is_valid_function_argument + { + static const bool value = false; + }; + + template + struct is_valid_function_argument, Result (Arguments...)> + { + static const bool value = false; + }; + + template + struct is_valid_function_argument + { +# ifdef _MSC_VER + // as of january 2013 visual studio doesn't support the SFINAE below + static const bool value = true; +# else + template + static decltype(to_functor(std::declval())(std::declval()...)) check(U *); + template + static empty_struct check(...); + + static const bool value = std::is_convertible(nullptr)), Result>::value; +# endif + }; + + typedef const function_manager * manager_type; + + struct manager_storage_type + { + template + Allocator & get_allocator() FUNC_NOEXCEPT + { + return reinterpret_cast(manager); + } + template + const Allocator & get_allocator() const FUNC_NOEXCEPT + { + return reinterpret_cast(manager); + } + + functor_padding functor; + manager_type manager; + }; + + template + struct function_manager_inplace_specialization + { + template + 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(reinterpret_cast(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(reinterpret_cast(storage.functor)); + } + }; + template + struct function_manager_inplace_specialization::value>::type> + { + template + 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::pointer &>(storage))(FUNC_FORWARD(Arguments, arguments)...); + } + + static void store_functor(manager_storage_type & self, T to_store) + { + Allocator & allocator = self.get_allocator();; + static_assert(sizeof(typename std::allocator_traits::pointer) <= sizeof(self.functor), "The allocator's pointer type is too big"); + typename std::allocator_traits::pointer * ptr = new (&get_functor_ptr_ref(self)) typename std::allocator_traits::pointer(std::allocator_traits::allocate(allocator, 1)); + std::allocator_traits::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::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::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::pointer & pointer = get_functor_ptr_ref(storage); + if (!pointer) return; + std::allocator_traits::destroy(allocator, pointer); + std::allocator_traits::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::pointer & get_functor_ptr_ref(manager_storage_type & storage) FUNC_NOEXCEPT + { + return reinterpret_cast::pointer &>(storage.functor); + } + static const typename std::allocator_traits::pointer & get_functor_ptr_ref(const manager_storage_type & storage) FUNC_NOEXCEPT + { + return reinterpret_cast::pointer &>(storage.functor); + } + }; + + template + static const function_manager & get_default_manager(); + + template + static void create_manager(manager_storage_type & storage, Allocator && allocator) + { + new (&storage.get_allocator()) Allocator(FUNC_MOVE(allocator)); + storage.manager = &get_default_manager(); + } + + // 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 + 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, + &templated_call_copy, + &templated_call_copy_functor_only, + &templated_call_destroy, +# ifndef FUNC_NO_RTTI + &templated_call_type_id, + &templated_call_target +# 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 + static void templated_call_move_and_destroy(manager_storage_type & lhs, manager_storage_type && rhs) + { + typedef function_manager_inplace_specialization specialization; + specialization::move_functor(lhs, FUNC_MOVE(rhs)); + specialization::destroy_functor(rhs.get_allocator(), rhs); + create_manager(lhs, FUNC_MOVE(rhs.get_allocator())); + rhs.get_allocator().~Allocator(); + } + template + static void templated_call_copy(manager_storage_type & lhs, const manager_storage_type & rhs) + { + typedef function_manager_inplace_specialization specialization; + create_manager(lhs, Allocator(rhs.get_allocator())); + specialization::store_functor(lhs, specialization::get_functor_ref(rhs)); + } + template + static void templated_call_destroy(manager_storage_type & self) + { + typedef function_manager_inplace_specialization specialization; + specialization::destroy_functor(self.get_allocator(), self); + self.get_allocator().~Allocator(); + } + template + static void templated_call_copy_functor_only(manager_storage_type & lhs, const manager_storage_type & rhs) + { + typedef function_manager_inplace_specialization specialization; + specialization::store_functor(lhs, specialization::get_functor_ref(rhs)); + } +# ifndef FUNC_NO_RTTI + template + static const std::type_info & templated_call_type_id() + { + return typeid(T); + } + template + static void * templated_call_target(const manager_storage_type & self, const std::type_info & type) + { + typedef function_manager_inplace_specialization specialization; + if (type == typeid(T)) + return &specialization::get_functor_ref(self); + else + return nullptr; + } +# endif + }; + template + inline static const function_manager & get_default_manager() + { + static FUNC_CONSTEXPR function_manager default_manager = function_manager::create_default_manager(); + return default_manager; + } + + template + struct typedeffer + { + typedef Result result_type; + }; + template + struct typedeffer + { + typedef Result result_type; + typedef Argument argument_type; + }; + template + struct typedeffer + { + typedef Result result_type; + typedef First_Argument first_argument_type; + typedef Second_Argument second_argument_type; + }; +} + +template +class function + : public detail::typedeffer +{ +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 + function(T functor, + typename std::enable_if::value, detail::empty_struct>::type = detail::empty_struct()) FUNC_TEMPLATE_NOEXCEPT(T, std::allocator::type>) + { + if (detail::is_null(functor)) + { + initialize_empty(); + } + else + { + typedef typename detail::functor_type::type functor_type; + initialize(detail::to_functor(FUNC_FORWARD(T, functor)), std::allocator()); + } + } + template + function(std::allocator_arg_t, const Allocator &) + { + // ignore the allocator because I don't allocate + initialize_empty(); + } + template + function(std::allocator_arg_t, const Allocator &, std::nullptr_t) + { + // ignore the allocator because I don't allocate + initialize_empty(); + } + template + function(std::allocator_arg_t, const Allocator & allocator, T functor, + typename std::enable_if::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 + function(std::allocator_arg_t, const Allocator & allocator, const function & other) + : call(other.call) + { + typedef typename std::allocator_traits::template rebind_alloc MyAllocator; + + // first try to see if the allocator matches the target type + detail::manager_type manager_for_allocator = &detail::get_default_manager::value_type, Allocator>(); + if (other.manager_storage.manager == manager_for_allocator) + { + detail::create_manager::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(); + if (other.manager_storage.manager == manager_for_function) + { + detail::create_manager(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 + 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 + 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 + T * target() FUNC_NOEXCEPT + { + return static_cast(manager_storage.manager->call_target(manager_storage, typeid(T))); + } + template + const T * target() const FUNC_NOEXCEPT + { + return static_cast(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; +# endif + } + +private: + detail::manager_storage_type manager_storage; + Result (*call)(const detail::functor_padding &, Arguments...); + + template + void initialize(T functor, Allocator && allocator) + { + call = &detail::function_manager_inplace_specialization::template call; + detail::create_manager(manager_storage, FUNC_FORWARD(Allocator, allocator)); + detail::function_manager_inplace_specialization::store_functor(manager_storage, FUNC_FORWARD(T, functor)); + } + + typedef Result(*Empty_Function_Type)(Arguments...); + void initialize_empty() FUNC_NOEXCEPT + { + typedef std::allocator Allocator; + static_assert(detail::is_inplace_allocated::value, "The empty function should benefit from small functor optimization"); + + detail::create_manager(manager_storage, Allocator()); + detail::function_manager_inplace_specialization::store_functor(manager_storage, nullptr); +# ifdef FUNC_NO_EXCEPTIONS + call = nullptr; +# else + call = &detail::empty_call; +# endif + } +}; + +template +bool operator==(std::nullptr_t, const function & rhs) FUNC_NOEXCEPT +{ + return !rhs; +} +template +bool operator==(const function & lhs, std::nullptr_t) FUNC_NOEXCEPT +{ + return !lhs; +} +template +bool operator!=(std::nullptr_t, const function & rhs) FUNC_NOEXCEPT +{ + return rhs; +} +template +bool operator!=(const function & lhs, std::nullptr_t) FUNC_NOEXCEPT +{ + return lhs; +} + +template +void swap(function & lhs, function & rhs) +{ + lhs.swap(rhs); +} + +} // end namespace func + +namespace std +{ +template +struct uses_allocator, 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