aboutsummaryrefslogtreecommitdiff
path: root/NvBlast/sdk/toolkit/source/NvBlastTkEventQueue.h
diff options
context:
space:
mode:
Diffstat (limited to 'NvBlast/sdk/toolkit/source/NvBlastTkEventQueue.h')
-rw-r--r--NvBlast/sdk/toolkit/source/NvBlastTkEventQueue.h231
1 files changed, 231 insertions, 0 deletions
diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkEventQueue.h b/NvBlast/sdk/toolkit/source/NvBlastTkEventQueue.h
new file mode 100644
index 0000000..00a1a61
--- /dev/null
+++ b/NvBlast/sdk/toolkit/source/NvBlastTkEventQueue.h
@@ -0,0 +1,231 @@
+/*
+* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
+*
+* NVIDIA CORPORATION and its licensors retain all intellectual property
+* and proprietary rights in and to this software, related documentation
+* and any modifications thereto. Any use, reproduction, disclosure or
+* distribution of this software and related documentation without an express
+* license agreement from NVIDIA CORPORATION is strictly prohibited.
+*/
+
+#ifndef NVBLASTTKEVENTQUEUE_H
+#define NVBLASTTKEVENTQUEUE_H
+
+#include <algorithm>
+#include <vector>
+
+#include <mutex>
+#include <atomic>
+
+#include "PxAllocatorCallback.h"
+#include "NvBlastTkFrameworkImpl.h"
+#include "NvBlastAssert.h"
+
+
+namespace Nv {
+namespace Blast {
+
+/**
+A dispatcher queue providing preallocation and thread-safe insertions therein.
+
+Typical usage:
+- preallocate space for events and payload:
+ - reserveEvents, reserveData
+- enable asserts to detect undersized storage (allocations are not thread safe):
+ - protect(true)
+- get pointers to payload data and events to fill in, thread safe for preallocated memory:
+ - allocData, addEvent
+- back on main thread, ensure consistency:
+ - protect(false)
+
+- continue adding events and payload on main thread if necessary like above (allocations are safe here)
+eventually dispatch, or reset if dispatched by proxy
+*/
+class TkEventQueue
+{
+public:
+ TkEventQueue() : m_currentEvent(0), m_poolCapacity(0), m_pool(nullptr), m_allowAllocs(true) {}
+
+ /**
+ Peek events queue for dispatch.
+ Do not use in protected state.
+ */
+ operator const TkArray<TkEvent>::type&()
+ {
+ NVBLAST_ASSERT(m_allowAllocs);
+ NVBLAST_ASSERT(m_currentEvent == m_events.size());
+ return m_events;
+ }
+
+ /**
+ Debug help to catch (unwanted) allocations during task work.
+ Note that this will not actually avoid allocations, but assert in debug builds.
+
+ Set true before using in distributed environment.
+ Set false to return to single-thread mode.
+ */
+ void protect(bool enable)
+ {
+ // During parallel use, m_events.size() and m_currentEvent are allowed to diverge.
+ // This is fine because resizeUninitialized does not alter the stored data.
+ NVBLAST_ASSERT(m_currentEvent <= m_events.capacity());
+ m_events.resizeUninitialized(m_currentEvent);
+ m_allowAllocs = !enable;
+ }
+
+ /**
+ Restores initial state.
+ Data memory is currently not being reused. To be improved.
+ */
+ void reset()
+ {
+ m_events.clear();
+ m_currentEvent = 0;
+ for (void* mem : m_memory)
+ {
+ NVBLASTTK_FREE(mem);
+ }
+ m_memory.clear();
+ m_currentData = 0;
+ m_allowAllocs = true;
+ m_poolCapacity = 0;
+ m_pool = nullptr;
+ }
+
+ /**
+ Queue an event with a payload.
+ */
+ template<class T>
+ void addEvent(T* payload)
+ {
+ uint32_t index = m_currentEvent.fetch_add(1);
+
+ // Should not allocate in protected state.
+ NVBLAST_ASSERT(m_allowAllocs || m_currentEvent <= m_events.capacity());
+
+ m_events.resizeUninitialized(m_currentEvent);
+
+ // During parallel use, m_events.size() and m_currentEvent are allowed to diverge.
+ // Consistency is restored in protect().
+ NVBLAST_ASSERT(!m_allowAllocs || m_currentEvent == m_events.size());
+
+ TkEvent& evt = m_events[index];
+ evt.type = TkEvent::Type(T::EVENT_TYPE);
+ evt.payload = payload;
+ }
+
+ /**
+ Request storage for payload.
+ */
+ template<typename T>
+ T* allocData()
+ {
+ uint32_t index = m_currentData.fetch_add(sizeof(T));
+ if (m_currentData <= m_poolCapacity)
+ {
+ return reinterpret_cast<T*>(&m_pool[index]);
+ }
+ else
+ {
+ // Could do larger block allocation here.
+ reserveData(sizeof(T));
+ // Account for the requested size.
+ m_currentData = sizeof(T);
+ return reinterpret_cast<T*>(&m_pool[0]);
+ }
+ }
+
+ /**
+ Preallocate a memory block of size Bytes for payload data.
+ Note that this will inevitably allocate a new memory block.
+ Subsequent calls to allocData will use this memory piecewise.
+ */
+ void reserveData(size_t size)
+ {
+ NVBLAST_ASSERT(m_allowAllocs);
+ m_pool = reinterpret_cast<uint8_t*>(allocDataBySize(size));
+ m_poolCapacity = size;
+ m_currentData = 0;
+ }
+
+ /**
+ Preallocate space for events.
+ */
+ void reserveEvents(uint32_t n)
+ {
+ NVBLAST_ASSERT(m_allowAllocs);
+ m_events.reserve(m_events.size() + n);
+ }
+
+ /**
+ Add a listener to dispatch to.
+ */
+ void addListener(TkEventListener& l)
+ {
+ m_listeners.pushBack(&l);
+ }
+
+ /**
+ Remove a listener from dispatch list.
+ */
+ void removeListener(TkEventListener& l)
+ {
+ m_listeners.findAndReplaceWithLast(&l);
+ }
+
+ /**
+ Dispatch the stored events to the registered listeners.
+ After dispatch, all data is invalidated.
+ */
+ void dispatch()
+ {
+ dispatch(*this);
+ reset();
+ }
+
+ /**
+ Proxy function to dispatch events to this queue's listeners.
+ */
+ void dispatch(const TkArray<TkEvent>::type& events) const
+ {
+ if (events.size())
+ {
+ for (TkEventListener* l : m_listeners)
+ {
+ PERF_SCOPE_M("TkEventQueue::dispatch");
+ l->receive(events.begin(), events.size());
+ }
+ }
+ }
+
+private:
+ /**
+ Allocates and stores a block of size Bytes of payload data.
+ */
+ void* allocDataBySize(size_t size)
+ {
+ void* memory = nullptr;
+ if (size > 0)
+ {
+ memory = NVBLASTTK_ALLOC(size, "TkEventQueue Data");
+ m_memory.pushBack(memory);
+ }
+ return memory;
+ }
+
+
+ TkArray<TkEvent>::type m_events; //!< holds events
+ TkArray<void*>::type m_memory; //!< holds allocated data memory blocks
+ std::atomic<uint32_t> m_currentEvent; //!< reference index for event insertion
+ std::atomic<uint32_t> m_currentData; //!< reference index for data insertion
+ size_t m_poolCapacity; //!< size of the currently active memory block (m_pool)
+ uint8_t* m_pool; //!< the current memory block allocData() uses
+ bool m_allowAllocs; //!< assert guard
+ TkInlineArray<TkEventListener*,4>::type m_listeners; //!< objects to dispatch to
+};
+
+} // namespace Blast
+} // namespace Nv
+
+
+#endif // ifndef NVBLASTTKEVENTQUEUE_H