From ea3bc89bdd36dbb5462822eb72e25ccb75de69a9 Mon Sep 17 00:00:00 2001 From: devsh Date: Thu, 2 Mar 2023 16:58:42 +0100 Subject: [PATCH 01/24] fix a bunch of docs --- include/nbl/system/IAsyncQueueDispatcher.h | 73 +++++++++++-------- .../system/ICancellableAsyncQueueDispatcher.h | 67 +++++++++-------- 2 files changed, 78 insertions(+), 62 deletions(-) diff --git a/include/nbl/system/IAsyncQueueDispatcher.h b/include/nbl/system/IAsyncQueueDispatcher.h index e5bc84c029..ba31d533c1 100644 --- a/include/nbl/system/IAsyncQueueDispatcher.h +++ b/include/nbl/system/IAsyncQueueDispatcher.h @@ -30,10 +30,18 @@ class IAsyncQueueDispatcherBase { static_assert(std::atomic_uint32_t::is_always_lock_free); } - ~request_base_t() = default; + ~request_base_t() + { + // must have been consumed before exit ! + const auto atExit = state.load(); + assert(atExit==ES_INITIAL); + } + + // in case you need it (which you won't + E_STATE queryState() const {return static_cast(state.load());} // lock when overwriting the request - void reset() + void start() { transition(ES_INITIAL,ES_RECORDING); } @@ -58,16 +66,21 @@ class IAsyncQueueDispatcherBase assert(prev==ES_EXECUTING); state.notify_one(); } + + // non-blocking query + bool is_ready() const + { + return state.load()==ES_READY; + } // to call to await the request to finish processing - void wait_ready() + void wait_ready() const { wait_for(ES_READY); } // to call after done reading the request and its memory can be recycled void discard_storage() { - const auto prev = state.exchange(ES_INITIAL); - assert(prev==ES_READY); + transition(ES_READY,ES_INITIAL); state.notify_one(); } @@ -82,7 +95,7 @@ class IAsyncQueueDispatcherBase } assert(expected==from); } - void wait_for(const E_STATE waitVal) + void wait_for(const E_STATE waitVal) const { uint32_t current; while ((current=state.load())!=waitVal) @@ -95,11 +108,21 @@ class IAsyncQueueDispatcherBase } /** +* Required accessible methods of class being CRTP parameter: +* +* void init(internal_state_t* state); // required only in case of custom internal state +* +* void exit(internal_state_t* state); // optional, no `state` parameter in case of no internal state +* +* void request_impl(request_t& req, ...); // `...` are parameteres forwarded from request(), the request's state is locked with a mutex during the call +* void process_request(request_t& req, internal_state_t& state); // no `state` parameter in case of no internal state +* void background_work() // optional, does nothing if not provided +* +* * Provided RequestType shall define 5 methods: -* T reset(); -* void finalize(T&&); +* void start(); +* void finalize(); * T wait_for_work(); -* T wait_for_result(); * T notify_all_ready(T&&); * TODO: [outdated docs] lock() will be called just before processing the request, and unlock() will be called just after processing the request. * Those are to enable safe external write access to the request struct for user-defined purposes. @@ -110,7 +133,7 @@ class IAsyncQueueDispatcherBase * notify_all_ready() takes an r-value reference to an already locked mutex and notifies any waiters then releases the lock */ template -class IAsyncQueueDispatcher : public IThreadHandler, public impl::IAsyncQueueDispatcherBase +class IAsyncQueueDispatcher : public IThreadHandler, protected impl::IAsyncQueueDispatcherBase { static_assert(std::is_base_of_v, "Request type must derive from request_base_t!"); static_assert(BufferSize>0u, "BufferSize must not be 0!"); @@ -118,10 +141,12 @@ class IAsyncQueueDispatcher : public IThreadHandler, pu protected: using base_t = IThreadHandler; - friend base_t; + friend base_t; // TODO: remove, some functions should just be protected + private: constexpr static inline uint32_t MaxRequestCount = BufferSize; + // maybe one day we'll abstract this into a lockless queue using atomic_counter_t = std::atomic_uint64_t; using counter_t = atomic_counter_t::value_type; @@ -135,11 +160,9 @@ class IAsyncQueueDispatcher : public IThreadHandler, pu return x & Mask; } - public: - - IAsyncQueueDispatcher() {}; - ~IAsyncQueueDispatcher() {}; + inline IAsyncQueueDispatcher() {} + inline ~IAsyncQueueDispatcher() {} using mutex_t = typename base_t::mutex_t; using lock_t = typename base_t::lock_t; @@ -148,20 +171,10 @@ class IAsyncQueueDispatcher : public IThreadHandler, pu using request_t = RequestType; - /////// - // Required accessible methods of class being CRTP parameter: - - //void init(internal_state_t* state); // required only in case of custom internal state - - //void exit(internal_state_t* state); // optional, no `state` parameter in case of no internal state - - //void request_impl(request_t& req, ...); // `...` are parameteres forwarded from request(), the request's state is locked with a mutex during the call - //void process_request(request_t& req, internal_state_t& state); // no `state` parameter in case of no internal state - //void background_work() // optional, does nothing if not provided - /////// - - using base_t::base_t; - + // Returns a reference to a request's storage in the circular buffer after processing the moved arguments + // YOU MUST CONSUME THE REQUEST by calling `discard_storage()` on it EXACTLY ONCE! + // YOU MUST CALL IT EVEN IF THERE'S NO DATA YOU WISH TO GET BACK FROM IT! + // (if you don't the queue will deadlock because of an unresolved overflow) template request_t& request(Args&&... args) { @@ -174,7 +187,7 @@ class IAsyncQueueDispatcher : public IThreadHandler, pu const auto r_id = wrapAround(virtualIx); request_t& req = request_pool[r_id]; - req.reset(); + req.start(); static_cast(this)->request_impl(req, std::forward(args)...); req.finalize(); diff --git a/include/nbl/system/ICancellableAsyncQueueDispatcher.h b/include/nbl/system/ICancellableAsyncQueueDispatcher.h index 594dd7af16..76269ba614 100644 --- a/include/nbl/system/ICancellableAsyncQueueDispatcher.h +++ b/include/nbl/system/ICancellableAsyncQueueDispatcher.h @@ -45,8 +45,37 @@ class ICancellableAsyncQueueDispatcherBase friend future_base_t; friend ICancellableAsyncQueueDispatcherBase; - //! Atomically cancels this request - bool set_cancel(); + //! Atomically cancels this request, returns false if we haven't cancelled the request in time before it was executed + bool set_cancel() + { + // double cancellation + if (!future) + return false; + // wait in case of processing + uint32_t expected = ES_PENDING; + while (!state.compare_exchange_strong(expected, ES_INITIAL)) + { + if (expected == ES_READY) + { + transition(ES_READY, ES_INITIAL); + return true; + } + else if (expected == ES_INITIAL) // cancel after await + { + return false; + } + // was executing, we didnt get here on time + assert(expected == ES_EXECUTING); + state.wait(expected); + expected = ES_PENDING; + } + // we've actually cancelled a pending request, and need to cleanup the future + if (future) + future->request = nullptr; + future = nullptr; + return true; + } + //! bool query_cancel() const { return state.load()==ES_INITIAL||future==nullptr; @@ -83,6 +112,7 @@ class ICancellableAsyncQueueDispatcherBase bool cancel() { + // atomic exchange of pointer to ensure only one thread gets to cancel at once request_base_t* req = request.exchange(nullptr); if (req) return req->set_cancel(); @@ -106,7 +136,10 @@ class ICancellableAsyncQueueDispatcherBase // all the data is stored inside the future during the request execution, so we dont need access to the request struct after its done executing // could have used wait_ready() && discard_storate() but its more efficient that way if (req) + { req->transition(impl::IAsyncQueueDispatcherBase::request_base_t::ES_READY,impl::IAsyncQueueDispatcherBase::request_base_t::ES_INITIAL); + req->state.notify_one(); + } } }; @@ -220,36 +253,6 @@ class ICancellableAsyncQueueDispatcher : public IAsyncQueueDispatcherrequest = nullptr; - future = nullptr; - return true; -} - } #endif From de28767cc1dd5931e8f0d95ba521868b7f35feca Mon Sep 17 00:00:00 2001 From: devsh Date: Thu, 9 Mar 2023 15:51:45 +0100 Subject: [PATCH 02/24] ideas --- include/nbl/core/StorageTrivializer.h | 41 +++ include/nbl/system/IAsyncQueueDispatcher.h | 398 ++++++++++++++++++--- 2 files changed, 382 insertions(+), 57 deletions(-) create mode 100644 include/nbl/core/StorageTrivializer.h diff --git a/include/nbl/core/StorageTrivializer.h b/include/nbl/core/StorageTrivializer.h new file mode 100644 index 0000000000..9bc5e130c4 --- /dev/null +++ b/include/nbl/core/StorageTrivializer.h @@ -0,0 +1,41 @@ +#ifndef __NBL_CORE_STORAGE_TRIVIALIZER_H_INCLUDED__ +#define __NBL_CORE_STORAGE_TRIVIALIZER_H_INCLUDED__ + +namespace nbl::core +{ + +// This construct makes it so that we don't trigger T's constructors and destructors. +template +struct StorageTrivializer; + +template<> +struct NBL_FORCE_EBO StorageTrivializer +{ + void* getStorage() {return nullptr;} + const void* getStorage() const {return nullptr;} + + void construct() {} + void destruct() {} +}; + +template +struct alignas(T) StorageTrivializer +{ + T* getStorage() {return reinterpret_cast(storage); } + const T* getStorage() const {return reinterpret_cast(storage);} + + void construct(Args&&... args) + { + new (getStorage()) T(std::forward(args)...); + } + void destruct() + { + getStorage->~T(); + } + + uint8_t* storage[sizeof(T)]; +}; + +} + +#endif \ No newline at end of file diff --git a/include/nbl/system/IAsyncQueueDispatcher.h b/include/nbl/system/IAsyncQueueDispatcher.h index ba31d533c1..db2b124ff8 100644 --- a/include/nbl/system/IAsyncQueueDispatcher.h +++ b/include/nbl/system/IAsyncQueueDispatcher.h @@ -12,99 +12,383 @@ namespace impl { class IAsyncQueueDispatcherBase { - public: + protected: IAsyncQueueDispatcherBase() = default; ~IAsyncQueueDispatcherBase() = default; - // dont want to play around with relaxed memory ordering yet - struct request_base_t + + template(0u)> + class atomic_state_t { - enum E_STATE : uint32_t - { - ES_INITIAL=0, - ES_RECORDING=1, - ES_PENDING=2, - ES_EXECUTING=3, - ES_READY=4 - }; - request_base_t() : state(ES_INITIAL) + static_assert(std::is_enum_v); + + public: + ~atomic_state_t() { static_assert(std::atomic_uint32_t::is_always_lock_free); - } - ~request_base_t() - { // must have been consumed before exit ! const auto atExit = state.load(); - assert(atExit==ES_INITIAL); + assert(static_cast(atExit)==kInitial); } - // in case you need it (which you won't - E_STATE queryState() const {return static_cast(state.load());} + inline STATE query() const {return static_cast(state.load());} - // lock when overwriting the request - void start() + inline void wait(const STATE targetState) const { - transition(ES_INITIAL,ES_RECORDING); + uint32_t current; + while ((current=state.load()) != targetState) + state.wait(current); } - // unlock request after we've written everything into it - void finalize() + + inline bool tryTransition(STATE& expected, const STATE to) { - const auto prev = state.exchange(ES_PENDING); - assert(prev==ES_RECORDING); - state.notify_one(); + return state.compare_exchange_strong(reinterpret_cast(from),static_cast(to)); } - // returns when work is ready and also if we can proceed to do the work - // this will deadlock if the state will not eventually transition to pending (a cancellable request needs to overload this) - bool wait_for_work() + + inline void waitTransition(const STATE from, const STATE to) { - transition(ES_PENDING,ES_EXECUTING); + STATE expected = from; + while (!tryTransition(expected,to)) + { + state.wait(static_cast(expected)); + expected = from; + } + assert(expected==from); + } + + inline bool waitAbortableTransition(const STATE from, const STATE to, const STATE abortState) + { + uint32_t expected = static_cast(from); + while (!state.compare_exchange_strong(expected,static_cast(to))) + { + state.wait(expected); + if (expected==static_cast(abortState)) + return false; + expected = from; + } + assert(expected==from); return true; } - // to call after request is done being processed, will deadlock if the request was not executed (a cancellable request needs to overload this) - void notify_ready() + + inline void exchangeNotify(const STATE expected, const STATE to) { - const auto prev = state.exchange(ES_READY); - assert(prev==ES_EXECUTING); + const auto prev = state.exchange(static_cast(to)); + assert(static_cast(prev)==expected); state.notify_one(); } - // non-blocking query - bool is_ready() const + private: + std::atomic_uint32_t state = static_cast(kInitial); + }; + + struct future_base_t; + // dont want to play around with relaxed memory ordering yet + struct request_base_t + { + public: + enum class STATE : uint32_t + { + INITIAL=0, + RECORDING=1, + PENDING=2, + EXECUTING=3, + CANCELLED=4 + }; + + // in case you need it, which you won't + inline const auto& getState() const {return state;} + + //! REQUESTING THREAD: lock when overwriting the request's data + inline void start() + { + state.waitTransition(STATE::INITIAL,STATE::RECORDING); + // previous thing cleaned up after itself + assert(!future); + } + //! REQUESTING THREAD: unlock request after we've written everything into it + void finalize(future_base_t* fut); + + //! WORKER THREAD: returns when work is ready, will deadlock if the state will not eventually transition to pending + bool wait(); + //! WORKER THREAD: to call after request is done being processed, will deadlock if the request was not executed + void notify(); +//TODO + //! ANY THREAD: via future_base_t + inline bool cancel() + { + + //state.waitAbortableTransition(STATE::PENDING,STATE::CANCELLED,STATE::INITIAL); + //assert(future); + // + /* + // we're expecting PENDING + uint32_t expected = ES_PENDING; + while (!state.compare_exchange_strong(expected, ES_INITIAL)) + { + // if we cancel + if (expected == ES_READY) + { + transition(ES_READY, ES_INITIAL); + return true; + } + else if (expected == ES_INITIAL) // cancel after await + { + return false; + } + // was executing, we didnt get here on time + assert(expected == ES_EXECUTING); + state.wait(expected); + expected = ES_PENDING; + } + */ + } + + protected: + // the base class is not directly usable + inline ~request_base_t() + { + // fully cleaned up + assert(!future); + } + + // ban certain operators + request_base_t(const request_base_t&) = delete; + request_base_t(request_base_t&&) = delete; + request_base_t& operator=(const request_base_t&) = delete; + request_base_t& operator=(request_base_t&&) = delete; + + private: + future_base_t* future = nullptr; + atomic_state_t state = {}; + }; + struct future_base_t + { + public: + enum class STATE : uint32_t { - return state.load()==ES_READY; + // after constructor, cancellation, move or destructor + INITIAL=0, + // after submitting request to work queue + ASSOCIATED=1, + // while being executed + EXECUTING=2, + // ready for consumption + READY=3, + // uncancellable + LOCKED=4 + }; + + //! REQUESTING THREAD: done as part of filling out the request + virtual void associate_request(request_base_t* req) + { + // sanity check + assert(req->getState().query()==request_base_t::STATE::RECORDING); + // if not initial state then wait until it gets moved, etc. + state.waitTransition(STATE::INITIAL,STATE::ASSOCIATED); } - // to call to await the request to finish processing - void wait_ready() const + //! WORKER THREAD: done as part of execution at the very start, after we begin work + inline void disassociate_request() { - wait_for(ES_READY); + state.exchangeNotify(STATE::ASSOCIATED,STATE::EXECUTING); // should we really notify? + request_base_t* noOtherRequest = request.exchange(nullptr); + assert(noOtherRequest && noOtherRequest->getState().query()==request_base_t::STATE::EXECUTING); } - // to call after done reading the request and its memory can be recycled - void discard_storage() + //! WORKER THREAD: done as part of execution at the very end, after object is constructed + inline void notify() { - transition(ES_READY,ES_INITIAL); - state.notify_one(); + state.exchangeNotify(STATE::EXECUTING,STATE::READY); + } + + //! ANY THREAD [except WORKER]: Check if worker thread actually processed out request + inline bool ready() const + { + switch (state.query()) + { + case STATE::LOCKED: + [[fallthrough]]; + case STATE::READY: + return true; + default: + break; + } + return false; } protected: - void transition(const E_STATE from, const E_STATE to) + // the base class is not directly usable + inline ~future_base_t() { - uint32_t expected = from; - while (!state.compare_exchange_strong(expected,to)) + // non-cancellable future just need to get to this state, and cancellable will move here + state.wait(STATE::INITIAL); + } + // future_t is non-copyable and non-movable because request needs a pointer to it + future_base_t(const future_base_t&) = delete; + future_base_t(future_base_t&&) = delete; + future_base_t& operator=(const future_base_t&) = delete; + future_base_t& operator=(future_base_t&&) = delete; + + // this tells us whether an object with a lifetime has been constructed over the memory backing the future + // also acts as a lock + atomic_state_t state; + }; + + public: + struct cancellable_future_t final : protected future_base_t + { + using base_t = future_base_t; + std::atomic request = nullptr; + + public: + inline ~cancellable_future_t() + { + cancel(); + // either we never had a request at all to begin with or derived called + // its own `cancel` in the destructor, I'm just checking its already done + assert(!request.load()); + } + + inline void associate_request(request_base_t* req) override + { + base_t::associate_request(req); + request_base_t* noOtherRequest = request.exchange(req); + // sanity check + assert(request==nullptr); + } + + inline void cancel() + { + STATE expected = STATE::ASSOCIATED; + state.tryTransition(expected,STATE::INITIAL); +// if +// core::StorageTrivializer::destroy(); + } + }; + struct retval_future_t : public future_base_t + { + public: + inline ~retval_future_t() + { + if (cancellable) + destroy = cancel(); + else { - state.wait(expected); - expected = from; + wait_ready(); + cond_destroy(); } - assert(expected==from); } - void wait_for(const E_STATE waitVal) const + + + //! NOTE: Deliberately named `...acquire` instead of `...lock` to make them incompatible with `unique_lock` + // and other RAII locks as the blocking aquire can fail and that needs to be handled. + //! ANY THREAD [except WORKER]: If we're READY transition to LOCKED + [[nodiscard]] inline bool try_acquire() { - uint32_t current; - while ((current=state.load())!=waitVal) - state.wait(current); + auto expected = STATE::READY; + return state.tryTransition(expected,STATE::LOCKED); + } + //! ANY THREAD [except WORKER]: Wait till we're either in READY and move us to LOCKED or bail on INITIAL + // this accounts for being cancelled or consumed while waiting + [[nodiscard]] inline bool acquire() + { + return state.waitAbortableTransition(STATE::READY,STATE::LOCKED,STATE::INITIAL); + } + //! ANY THREAD [except WORKER]: Release a lock + inline void release() + { + state.exchangeNotify(STATE::LOCKED,STATE::READY); } - std::atomic_uint32_t state; + //! ANY THREAD [except WORKER]: returns whether we actually managed to cancel + bool cancel() + { + bool actuallyCancelled = false; + // atomic exchange of pointer to ensure only one thread gets to cancel, ever + request_base_t* req = request.exchange(nullptr); + if (req) + actuallyCancelled = req->set_cancel(); + const bool constructed = valid_flag.exchange(false); + if (constructed) + destroyStorage(); + else + { + // if we cancelled then the object never got constructed + assert(!actuallyCancelled); + } + return actuallyCancelled; + } + + protected: + inline void cancel() + { + assert(cancellable); + cond_destroy(); + } + inline void cond_destroy() + { + core::StorageTrivializer::destroy(); + } + }; + template + struct future_t : private core::StorageTrivializer, public future_base_t + { + public: + inline bool ready() const { return future_base_t::ready(); } + static inline constexpr bool Cancellable = _Cancellable; + + inline future_t() = default; + + + //! NOTE: Deliberately named `...acquire` instead of `...lock` to make them incompatible with `unique_lock` + // and other RAII locks as the blocking aquire can fail and that needs to be handled. + //! ANY THREAD [except WORKER]: If we're READY transition to LOCKED + [[nodiscard]] inline bool try_acquire() + { + auto expected = STATE::READY; + return state.tryTransition(expected,STATE::LOCKED); + } + //! ANY THREAD [except WORKER]: Wait till we're either in READY and move us to LOCKED or bail on INITIAL + // this accounts for being cancelled or consumed while waiting + [[nodiscard]] inline bool acquire() + { + return state.waitAbortableTransition(STATE::READY,STATE::LOCKED,STATE::INITIAL); + } + //! ANY THREAD [except WORKER]: Release a lock + inline void release() + { + state.exchangeNotify(STATE::LOCKED,STATE::READY); + } }; }; + +inline void IAsyncQueueDispatcherBase::request_base_t::finalize(future_base_t* fut) +{ + future = fut; + future->associate_request(this); + state.exchangeNotify(STATE::RECORDING,STATE::PENDING); +} + +inline bool IAsyncQueueDispatcherBase::request_base_t::wait() +{ + const bool notCancelled = state.waitAbortableTransition(STATE::PENDING,STATE::EXECUTING,STATE::CANCELLED); + if (notCancelled) + future->disassociate_request(); + else + { + // the only other option is for `notify()` to handle this + //assert(future->cancellable); + future = nullptr; + state.exchangeNotify(STATE::CANCELLED,STATE::INITIAL); + } + return notCancelled; +} +inline void IAsyncQueueDispatcherBase::request_base_t::notify() +{ + future->notify(); + // cleanup + future = nullptr; + // allow to be recycled + state.exchangeNotify(STATE::EXECUTING,STATE::INITIAL); +} + } /** @@ -122,8 +406,8 @@ class IAsyncQueueDispatcherBase * Provided RequestType shall define 5 methods: * void start(); * void finalize(); -* T wait_for_work(); -* T notify_all_ready(T&&); +* bool wait(); +* void notify(); * TODO: [outdated docs] lock() will be called just before processing the request, and unlock() will be called just after processing the request. * Those are to enable safe external write access to the request struct for user-defined purposes. * From 418db0d4c1ec563171f4ecdedd9499dddbd7b0b9 Mon Sep 17 00:00:00 2001 From: devsh Date: Thu, 9 Mar 2023 18:19:43 +0100 Subject: [PATCH 03/24] better draft --- include/nbl/core/StorageTrivializer.h | 2 +- include/nbl/core/declarations.h | 1 + include/nbl/system/IAsyncQueueDispatcher.h | 315 ++++++++++-------- .../system/ICancellableAsyncQueueDispatcher.h | 209 ------------ 4 files changed, 170 insertions(+), 357 deletions(-) diff --git a/include/nbl/core/StorageTrivializer.h b/include/nbl/core/StorageTrivializer.h index 9bc5e130c4..fcf349f433 100644 --- a/include/nbl/core/StorageTrivializer.h +++ b/include/nbl/core/StorageTrivializer.h @@ -30,7 +30,7 @@ struct alignas(T) StorageTrivializer } void destruct() { - getStorage->~T(); + getStorage()->~T(); } uint8_t* storage[sizeof(T)]; diff --git a/include/nbl/core/declarations.h b/include/nbl/core/declarations.h index c1ec50a992..4718e71c4f 100644 --- a/include/nbl/core/declarations.h +++ b/include/nbl/core/declarations.h @@ -66,6 +66,7 @@ #include "nbl/core/EventDeferredHandler.h" #include "nbl/core/IBuffer.h" #include "nbl/core/IReferenceCounted.h" +#include "nbl/core/StorageTrivializer.h" #include "nbl/core/SRAIIBasedExiter.h" #include "nbl/core/SRange.h" #include "nbl/core/Byteswap.h" diff --git a/include/nbl/system/IAsyncQueueDispatcher.h b/include/nbl/system/IAsyncQueueDispatcher.h index db2b124ff8..1517aecbf2 100644 --- a/include/nbl/system/IAsyncQueueDispatcher.h +++ b/include/nbl/system/IAsyncQueueDispatcher.h @@ -31,7 +31,7 @@ class IAsyncQueueDispatcherBase } inline STATE query() const {return static_cast(state.load());} - +// TODO: improve inline void wait(const STATE targetState) const { uint32_t current; @@ -39,7 +39,7 @@ class IAsyncQueueDispatcherBase state.wait(current); } - inline bool tryTransition(STATE& expected, const STATE to) + [[nodiscard]] inline bool tryTransition(STATE& expected, const STATE to) { return state.compare_exchange_strong(reinterpret_cast(from),static_cast(to)); } @@ -55,7 +55,7 @@ class IAsyncQueueDispatcherBase assert(expected==from); } - inline bool waitAbortableTransition(const STATE from, const STATE to, const STATE abortState) + [[nodiscard]] inline bool waitAbortableTransition(const STATE from, const STATE to, const STATE abortState) { uint32_t expected = static_cast(from); while (!state.compare_exchange_strong(expected,static_cast(to))) @@ -68,7 +68,7 @@ class IAsyncQueueDispatcherBase assert(expected==from); return true; } - +// TODO: improve (assert and notify one vs all) inline void exchangeNotify(const STATE expected, const STATE to) { const auto prev = state.exchange(static_cast(to)); @@ -108,38 +108,18 @@ class IAsyncQueueDispatcherBase void finalize(future_base_t* fut); //! WORKER THREAD: returns when work is ready, will deadlock if the state will not eventually transition to pending - bool wait(); + [[nodiscard]] bool wait(); //! WORKER THREAD: to call after request is done being processed, will deadlock if the request was not executed void notify(); -//TODO - //! ANY THREAD: via future_base_t - inline bool cancel() - { - //state.waitAbortableTransition(STATE::PENDING,STATE::CANCELLED,STATE::INITIAL); - //assert(future); - // - /* - // we're expecting PENDING - uint32_t expected = ES_PENDING; - while (!state.compare_exchange_strong(expected, ES_INITIAL)) - { - // if we cancel - if (expected == ES_READY) - { - transition(ES_READY, ES_INITIAL); - return true; - } - else if (expected == ES_INITIAL) // cancel after await - { - return false; - } - // was executing, we didnt get here on time - assert(expected == ES_EXECUTING); - state.wait(expected); - expected = ES_PENDING; - } - */ + //! ANY THREAD [except worker]: via cancellable_future_t::cancel + inline void cancel() + { + const auto prev = state.exchangeNotify(STATE::CANCELLED); + // If we were in EXECUTING then worker thread is definitely stuck `base_t::disassociate_request` spinlock + assert(prev==STATE::PENDING || prev==STATE::EXECUTING); + // sanity check, but its not our job to set it to nullptr + assert(future); } protected: @@ -160,6 +140,7 @@ class IAsyncQueueDispatcherBase future_base_t* future = nullptr; atomic_state_t state = {}; }; + // TODO: attempt to fight the virtualism struct future_base_t { public: @@ -178,19 +159,17 @@ class IAsyncQueueDispatcherBase }; //! REQUESTING THREAD: done as part of filling out the request - virtual void associate_request(request_base_t* req) + virtual inline void associate_request(request_base_t* req) { // sanity check assert(req->getState().query()==request_base_t::STATE::RECORDING); // if not initial state then wait until it gets moved, etc. state.waitTransition(STATE::INITIAL,STATE::ASSOCIATED); } - //! WORKER THREAD: done as part of execution at the very start, after we begin work - inline void disassociate_request() + //! WORKER THREAD: done as part of execution at the very start, after we want to begin work + [[nodiscard]] virtual inline bool disassociate_request() { - state.exchangeNotify(STATE::ASSOCIATED,STATE::EXECUTING); // should we really notify? - request_base_t* noOtherRequest = request.exchange(nullptr); - assert(noOtherRequest && noOtherRequest->getState().query()==request_base_t::STATE::EXECUTING); + return state.waitAbortableTransition(STATE::ASSOCIATED,STATE::EXECUTING,STATE::INITIAL); } //! WORKER THREAD: done as part of execution at the very end, after object is constructed inline void notify() @@ -198,24 +177,9 @@ class IAsyncQueueDispatcherBase state.exchangeNotify(STATE::EXECUTING,STATE::READY); } - //! ANY THREAD [except WORKER]: Check if worker thread actually processed out request - inline bool ready() const - { - switch (state.query()) - { - case STATE::LOCKED: - [[fallthrough]]; - case STATE::READY: - return true; - default: - break; - } - return false; - } - protected: // the base class is not directly usable - inline ~future_base_t() + virtual inline ~future_base_t() { // non-cancellable future just need to get to this state, and cancellable will move here state.wait(STATE::INITIAL); @@ -228,133 +192,195 @@ class IAsyncQueueDispatcherBase // this tells us whether an object with a lifetime has been constructed over the memory backing the future // also acts as a lock - atomic_state_t state; + atomic_state_t state= {}; }; public: - struct cancellable_future_t final : protected future_base_t + template + struct future_t : private core::StorageTrivializer, protected future_base_t { - using base_t = future_base_t; - std::atomic request = nullptr; - public: - inline ~cancellable_future_t() - { - cancel(); - // either we never had a request at all to begin with or derived called - // its own `cancel` in the destructor, I'm just checking its already done - assert(!request.load()); - } - - inline void associate_request(request_base_t* req) override + inline future_t() = default; + inline ~future_t() { - base_t::associate_request(req); - request_base_t* noOtherRequest = request.exchange(req); - // sanity check - assert(request==nullptr); + discard(); } - inline void cancel() + //! + inline bool ready() const { - STATE expected = STATE::ASSOCIATED; - state.tryTransition(expected,STATE::INITIAL); -// if -// core::StorageTrivializer::destroy(); + switch (state.query()) + { + case STATE::LOCKED: + [[fallthrough]]; + case STATE::READY: + return true; + default: + break; + } + return false; } - }; - struct retval_future_t : public future_base_t - { - public: - inline ~retval_future_t() + + //! Returns after waiting till `ready()` would be true or after + inline bool wait() { - if (cancellable) - destroy = cancel(); - else + while (true) { - wait_ready(); - cond_destroy(); + switch (state.query()) + { + case STATE::INITIAL: + return false; + break; + case STATE::READY: + [[fallthrough]]; + case STATE::LOCKED: + return true; + break; + default: + break; + } } + assert(false); } - //! NOTE: Deliberately named `...acquire` instead of `...lock` to make them incompatible with `unique_lock` // and other RAII locks as the blocking aquire can fail and that needs to be handled. + //! ANY THREAD [except WORKER]: If we're READY transition to LOCKED - [[nodiscard]] inline bool try_acquire() + [[nodiscard]] inline T* try_acquire() { auto expected = STATE::READY; - return state.tryTransition(expected,STATE::LOCKED); + if (state.tryTransition(expected,STATE::LOCKED)) + return getStorage(); + return nullptr; } //! ANY THREAD [except WORKER]: Wait till we're either in READY and move us to LOCKED or bail on INITIAL // this accounts for being cancelled or consumed while waiting - [[nodiscard]] inline bool acquire() + [[nodiscard]] inline T* acquire() { - return state.waitAbortableTransition(STATE::READY,STATE::LOCKED,STATE::INITIAL); + if (state.waitAbortableTransition(STATE::READY,STATE::LOCKED,STATE::INITIAL)) + return getStorage(); + return nullptr; } - //! ANY THREAD [except WORKER]: Release a lock + //! ANY THREAD [except WORKER]: Release an acquired lock inline void release() { state.exchangeNotify(STATE::LOCKED,STATE::READY); } - //! ANY THREAD [except WORKER]: returns whether we actually managed to cancel - bool cancel() - { - bool actuallyCancelled = false; - // atomic exchange of pointer to ensure only one thread gets to cancel, ever - request_base_t* req = request.exchange(nullptr); - if (req) - actuallyCancelled = req->set_cancel(); - const bool constructed = valid_flag.exchange(false); - if (constructed) - destroyStorage(); - else - { - // if we cancelled then the object never got constructed - assert(!actuallyCancelled); - } - return actuallyCancelled; + //! NOTE: You're in charge of ensuring future doesn't transition back to INITIAL (e.g. lock or use sanely!) + inline const T* get() const + { + if (ready()) + return getStorage(); + return nullptr; + } + inline T* get() + { + if (future_base_t::state.query() != future_base_t::STATE::LOCKED) + return nullptr; + return getStorage(); + } + + //! Can only be called once! If returns false means has been cancelled and nothing happened + inline bool move_into(T& dst) + { + T* pSrc = acquire(); + if (!pSrc) + return false; + dst = std::move(*T); + discard_common(); + return true; + } + + //! + virtual inline void discard() + { + if (acquire()) + discard_common(); } protected: - inline void cancel() + // construct the retval element + template + inline void notify(Args&&... args) { - assert(cancellable); - cond_destroy(); + new (getStorage()) T(std::forward(args)...); + future_base_t::notify(); } - inline void cond_destroy() + + private: + inline discard_common() { - core::StorageTrivializer::destroy(); + destruct(); + state.exchangeNotify(STATE::LOCKED, STATE::INITIAL); } }; - template - struct future_t : private core::StorageTrivializer, public future_base_t + template + struct cancellable_future_t final : public future_t { - public: - inline bool ready() const { return future_base_t::ready(); } - static inline constexpr bool Cancellable = _Cancellable; + using base_t = future_t; + std::atomic request = nullptr; - inline future_t() = default; - + public: + //! ANY THREAD [except WORKER]: Cancel pending request if we can, returns whether we actually managed to cancel + inline bool cancel() + { + STATE expected = STATE::ASSOCIATED; + if (state.tryTransition(expected,STATE::EXECUTING)) + { + // Since we're here we've managed to move from ASSOCIATED to fake "EXECUTING" this means that the Request is either: + // 1. RECORDING but after returning from `base_t::associate_request` + while (!request.load()) {} + // 2. PENDING + // 3. EXECUTING but before returning from `base_t::disassociate_request` cause there's a spinlock there + + request.exchange(nullptr)->cancel(); + + // after doing everything, we can mark ourselves as cleaned up + state.exchangeNotify(STATE::EXECUTING,STATE::INITIAL); + return true; + } + // we're here because either: + // - there was no work submitted + // - someone else cancelled + // - request is currently executing + // - request is ready + // - storage is locked/acquired + // sanity check (there's a tiny gap between transitioning to EXECUTING and disassociating request) + assert(expected==STATE::EXECUTING || request==nullptr); + return false; + } - //! NOTE: Deliberately named `...acquire` instead of `...lock` to make them incompatible with `unique_lock` - // and other RAII locks as the blocking aquire can fail and that needs to be handled. - //! ANY THREAD [except WORKER]: If we're READY transition to LOCKED - [[nodiscard]] inline bool try_acquire() + inline void discard() override final { - auto expected = STATE::READY; - return state.tryTransition(expected,STATE::LOCKED); + // try to cancel + cancel(); + // sanity check + assert(request==nullptr); + // proceed with the usual + base_t::discard(); } - //! ANY THREAD [except WORKER]: Wait till we're either in READY and move us to LOCKED or bail on INITIAL - // this accounts for being cancelled or consumed while waiting - [[nodiscard]] inline bool acquire() + + private: + inline void associate_request(request_base_t* req) override { - return state.waitAbortableTransition(STATE::READY,STATE::LOCKED,STATE::INITIAL); + base_t::associate_request(req); + request_base_t* prev = request.exchange(req); + // sanity check + assert(prev==nullptr); } - //! ANY THREAD [except WORKER]: Release a lock - inline void release() + inline bool disassociate_request() override { - state.exchangeNotify(STATE::LOCKED,STATE::READY); + assert(request.load()->getState().query()==request_base_t::STATE::EXECUTING); + if (base_t::disassociate_request()) + { + // only assign if we didn't get cancelled mid-way, otherwise will mess up `associate_request` sanity checks + request_base_t* prev = request.exchange(nullptr); + assert(prev); + return true; + } + return false; } }; }; @@ -368,17 +394,12 @@ inline void IAsyncQueueDispatcherBase::request_base_t::finalize(future_base_t* f inline bool IAsyncQueueDispatcherBase::request_base_t::wait() { - const bool notCancelled = state.waitAbortableTransition(STATE::PENDING,STATE::EXECUTING,STATE::CANCELLED); - if (notCancelled) - future->disassociate_request(); - else - { - // the only other option is for `notify()` to handle this - //assert(future->cancellable); - future = nullptr; - state.exchangeNotify(STATE::CANCELLED,STATE::INITIAL); - } - return notCancelled; + if (state.waitAbortableTransition(STATE::PENDING,STATE::EXECUTING,STATE::CANCELLED) && future->disassociate_request()) + return true; + //assert(future->cancellable); + future = nullptr; + state.exchangeNotify(STATE::CANCELLED,STATE::INITIAL); + return false; } inline void IAsyncQueueDispatcherBase::request_base_t::notify() { diff --git a/include/nbl/system/ICancellableAsyncQueueDispatcher.h b/include/nbl/system/ICancellableAsyncQueueDispatcher.h index 76269ba614..a3b92c5054 100644 --- a/include/nbl/system/ICancellableAsyncQueueDispatcher.h +++ b/include/nbl/system/ICancellableAsyncQueueDispatcher.h @@ -8,156 +8,6 @@ namespace nbl::system { -namespace impl -{ - -class ICancellableAsyncQueueDispatcherBase -{ - public: - class future_base_t; - - struct request_base_t : impl::IAsyncQueueDispatcherBase::request_base_t - { - // do NOT allow canceling of request while they are processed - bool wait_for_work() - { - uint32_t expected = ES_PENDING; - while (!state.compare_exchange_strong(expected,ES_EXECUTING)) - { - // this will allow worker to proceed, but the request predicate will take care of not doing the work - if (expected==ES_INITIAL) - return false; - state.wait(expected); - expected = ES_PENDING; - } - assert(expected==ES_PENDING); - return true; - } - // to call after request is done being processed - void notify_ready() - { - const auto prev = state.exchange(ES_READY); - assert(prev==ES_INITIAL||prev==ES_EXECUTING); - state.notify_one(); - } - - private: - friend future_base_t; - friend ICancellableAsyncQueueDispatcherBase; - - //! Atomically cancels this request, returns false if we haven't cancelled the request in time before it was executed - bool set_cancel() - { - // double cancellation - if (!future) - return false; - // wait in case of processing - uint32_t expected = ES_PENDING; - while (!state.compare_exchange_strong(expected, ES_INITIAL)) - { - if (expected == ES_READY) - { - transition(ES_READY, ES_INITIAL); - return true; - } - else if (expected == ES_INITIAL) // cancel after await - { - return false; - } - // was executing, we didnt get here on time - assert(expected == ES_EXECUTING); - state.wait(expected); - expected = ES_PENDING; - } - // we've actually cancelled a pending request, and need to cleanup the future - if (future) - future->request = nullptr; - future = nullptr; - return true; - } - //! - bool query_cancel() const - { - return state.load()==ES_INITIAL||future==nullptr; - } - - future_base_t* future = nullptr; - - //! See ICancellableAsyncQueueDispatcher::associate_request_with_future() docs - void associate_future_object(future_base_t* _future) - { - future = _future; - } - }; - - class future_base_t - { - friend request_base_t; - - protected: - // this tells us whether and object with a lifetime has been constructed over the memory backing the future - std::atomic_bool valid_flag = false; - std::atomic request = nullptr; - - // the base class is not directly usable - future_base_t() = default; - // future_t is non-copyable and non-movable - future_base_t(const future_base_t&) = delete; - // the base class shouldn't be used by itself without casting - ~future_base_t() - { - // derived should call its own `cancel` in the destructor, I'm just checking its already done - assert(!(request.load()||valid_flag.load())); - } - - bool cancel() - { - // atomic exchange of pointer to ensure only one thread gets to cancel at once - request_base_t* req = request.exchange(nullptr); - if (req) - return req->set_cancel(); - return false; - } - - public: - - // Misused these a couple times so i'll better put [[nodiscard]] here - [[nodiscard]] bool ready() const - { - request_base_t* req = request.load(); - return !req || req->state.load()==impl::IAsyncQueueDispatcherBase::request_base_t::ES_READY; - } - [[nodiscard]] bool valid() const { return valid_flag.load(); } - - void wait() - { - // the request is backed by a circular buffer, so if there was a pointer, it will stay valid (but request might have been overwritten) - request_base_t* req = request.load(); - // all the data is stored inside the future during the request execution, so we dont need access to the request struct after its done executing - // could have used wait_ready() && discard_storate() but its more efficient that way - if (req) - { - req->transition(impl::IAsyncQueueDispatcherBase::request_base_t::ES_READY,impl::IAsyncQueueDispatcherBase::request_base_t::ES_INITIAL); - req->state.notify_one(); - } - } - }; - - protected: - template - FutureType* request_get_future_object(request_base_t& req_base) - { - return static_cast(req_base.future); - } - void request_associate_future_object(request_base_t& req, future_base_t* future) - { - req.associate_future_object(future); - } -}; - -} - - template class ICancellableAsyncQueueDispatcher : public IAsyncQueueDispatcher, public impl::ICancellableAsyncQueueDispatcherBase { @@ -165,70 +15,11 @@ class ICancellableAsyncQueueDispatcher : public IAsyncQueueDispatcher; friend base_t; - template - class future_storage_t - { - public: - alignas(T) uint8_t storage[sizeof(T)]; - - T* getStorage() { return reinterpret_cast(storage); } - }; - public: using request_base_t = impl::ICancellableAsyncQueueDispatcherBase::request_base_t; static_assert(std::is_base_of_v, "Request type must derive from request_base_t!"); - template - class future_t : private future_storage_t, public impl::ICancellableAsyncQueueDispatcherBase::future_base_t - { - friend this_async_queue_t; - - protected: - // construct the retval element - template - void notify(Args&&... args) - { - new (future_storage_t::getStorage()) T(std::forward(args)...); - valid_flag.store(true); - } - - //! See ICancellableAsyncQueueDispatcher::associate_request_with_future() docs - void associate_request(RequestType* req) - { - request = req; - } - - public: - using value_type = T; - - bool cancel() - { - const bool retval = impl::ICancellableAsyncQueueDispatcherBase::future_base_t::cancel(); - bool valid = valid_flag.exchange(false); - if (valid) - future_storage_t::getStorage()->~T(); - return retval; - } - - future_t() = default; - - ~future_t() - { - const bool didntUseFuture = cancel(); - _NBL_DEBUG_BREAK_IF(didntUseFuture); - } - - // can only be called once! - T& get() - { - future_base_t::wait(); - assert(valid_flag); - T* ptr = future_storage_t::getStorage(); - return ptr[0]; - } - }; - using base_t::base_t; protected: From afa9dec343bffcac69da4a4a7e9e8dd2c7d34074 Mon Sep 17 00:00:00 2001 From: devsh Date: Mon, 13 Mar 2023 16:20:56 +0100 Subject: [PATCH 04/24] draft complete --- include/nbl/core/StorageTrivializer.h | 1 + include/nbl/system/IAsyncQueueDispatcher.h | 189 ++++++------------ .../system/ICancellableAsyncQueueDispatcher.h | 49 ----- include/nbl/system/ISystem.h | 18 +- include/nbl/system/atomic_state.h | 90 +++++++++ 5 files changed, 164 insertions(+), 183 deletions(-) delete mode 100644 include/nbl/system/ICancellableAsyncQueueDispatcher.h create mode 100644 include/nbl/system/atomic_state.h diff --git a/include/nbl/core/StorageTrivializer.h b/include/nbl/core/StorageTrivializer.h index fcf349f433..2e15ccd916 100644 --- a/include/nbl/core/StorageTrivializer.h +++ b/include/nbl/core/StorageTrivializer.h @@ -24,6 +24,7 @@ struct alignas(T) StorageTrivializer T* getStorage() {return reinterpret_cast(storage); } const T* getStorage() const {return reinterpret_cast(storage);} + template void construct(Args&&... args) { new (getStorage()) T(std::forward(args)...); diff --git a/include/nbl/system/IAsyncQueueDispatcher.h b/include/nbl/system/IAsyncQueueDispatcher.h index 1517aecbf2..9f91d9de19 100644 --- a/include/nbl/system/IAsyncQueueDispatcher.h +++ b/include/nbl/system/IAsyncQueueDispatcher.h @@ -1,9 +1,10 @@ -#ifndef __NBL_I_ASYNC_QUEUE_DISPATCHER_H_INCLUDED__ -#define __NBL_I_ASYNC_QUEUE_DISPATCHER_H_INCLUDED__ +#ifndef _NBL_I_ASYNC_QUEUE_DISPATCHER_H_INCLUDED_ +#define _NBL_I_ASYNC_QUEUE_DISPATCHER_H_INCLUDED_ -#include #include "nbl/core/declarations.h" + #include "nbl/system/IThreadHandler.h" +#include "nbl/system/atomic_state.h" namespace nbl::system { @@ -12,74 +13,7 @@ namespace impl { class IAsyncQueueDispatcherBase { - protected: - IAsyncQueueDispatcherBase() = default; - ~IAsyncQueueDispatcherBase() = default; - - template(0u)> - class atomic_state_t - { - static_assert(std::is_enum_v); - - public: - ~atomic_state_t() - { - static_assert(std::atomic_uint32_t::is_always_lock_free); - // must have been consumed before exit ! - const auto atExit = state.load(); - assert(static_cast(atExit)==kInitial); - } - - inline STATE query() const {return static_cast(state.load());} -// TODO: improve - inline void wait(const STATE targetState) const - { - uint32_t current; - while ((current=state.load()) != targetState) - state.wait(current); - } - - [[nodiscard]] inline bool tryTransition(STATE& expected, const STATE to) - { - return state.compare_exchange_strong(reinterpret_cast(from),static_cast(to)); - } - - inline void waitTransition(const STATE from, const STATE to) - { - STATE expected = from; - while (!tryTransition(expected,to)) - { - state.wait(static_cast(expected)); - expected = from; - } - assert(expected==from); - } - - [[nodiscard]] inline bool waitAbortableTransition(const STATE from, const STATE to, const STATE abortState) - { - uint32_t expected = static_cast(from); - while (!state.compare_exchange_strong(expected,static_cast(to))) - { - state.wait(expected); - if (expected==static_cast(abortState)) - return false; - expected = from; - } - assert(expected==from); - return true; - } -// TODO: improve (assert and notify one vs all) - inline void exchangeNotify(const STATE expected, const STATE to) - { - const auto prev = state.exchange(static_cast(to)); - assert(static_cast(prev)==expected); - state.notify_one(); - } - - private: - std::atomic_uint32_t state = static_cast(kInitial); - }; - + public: struct future_base_t; // dont want to play around with relaxed memory ordering yet struct request_base_t @@ -100,7 +34,7 @@ class IAsyncQueueDispatcherBase //! REQUESTING THREAD: lock when overwriting the request's data inline void start() { - state.waitTransition(STATE::INITIAL,STATE::RECORDING); + state.waitTransition(STATE::RECORDING,STATE::INITIAL); // previous thing cleaned up after itself assert(!future); } @@ -115,8 +49,8 @@ class IAsyncQueueDispatcherBase //! ANY THREAD [except worker]: via cancellable_future_t::cancel inline void cancel() { - const auto prev = state.exchangeNotify(STATE::CANCELLED); - // If we were in EXECUTING then worker thread is definitely stuck `base_t::disassociate_request` spinlock + const auto prev = state.exchangeNotify(STATE::CANCELLED); + // If we were in EXECUTING then worker thread is definitely stuck in `base_t::disassociate_request` spinlock assert(prev==STATE::PENDING || prev==STATE::EXECUTING); // sanity check, but its not our job to set it to nullptr assert(future); @@ -164,17 +98,17 @@ class IAsyncQueueDispatcherBase // sanity check assert(req->getState().query()==request_base_t::STATE::RECORDING); // if not initial state then wait until it gets moved, etc. - state.waitTransition(STATE::INITIAL,STATE::ASSOCIATED); + state.waitTransition(STATE::ASSOCIATED,STATE::INITIAL); } //! WORKER THREAD: done as part of execution at the very start, after we want to begin work [[nodiscard]] virtual inline bool disassociate_request() { - return state.waitAbortableTransition(STATE::ASSOCIATED,STATE::EXECUTING,STATE::INITIAL); + return state.waitAbortableTransition(STATE::EXECUTING,STATE::ASSOCIATED,STATE::INITIAL); } //! WORKER THREAD: done as part of execution at the very end, after object is constructed inline void notify() { - state.exchangeNotify(STATE::EXECUTING,STATE::READY); + state.exchangeNotify(STATE::READY,STATE::EXECUTING); } protected: @@ -182,7 +116,7 @@ class IAsyncQueueDispatcherBase virtual inline ~future_base_t() { // non-cancellable future just need to get to this state, and cancellable will move here - state.wait(STATE::INITIAL); + state.wait([](const STATE _query)->bool{return _query!=STATE::INITIAL;}); } // future_t is non-copyable and non-movable because request needs a pointer to it future_base_t(const future_base_t&) = delete; @@ -195,10 +129,21 @@ class IAsyncQueueDispatcherBase atomic_state_t state= {}; }; + protected: + IAsyncQueueDispatcherBase() = default; + ~IAsyncQueueDispatcherBase() = default; + public: template - struct future_t : private core::StorageTrivializer, protected future_base_t + class future_t : private core::StorageTrivializer, protected future_base_t { + using storage_t = core::StorageTrivializer; + inline void discard_common() + { + storage_t::destruct(); + state.exchangeNotify(STATE::INITIAL,STATE::LOCKED); + } + public: inline future_t() = default; inline ~future_t() @@ -222,25 +167,27 @@ class IAsyncQueueDispatcherBase } //! Returns after waiting till `ready()` would be true or after - inline bool wait() + inline bool wait() const { - while (true) - { - switch (state.query()) + bool retval = false; + state.wait([&retval](const STATE _query)->bool{ + switch (_query) { - case STATE::INITIAL: - return false; - break; - case STATE::READY: - [[fallthrough]]; - case STATE::LOCKED: - return true; - break; - default: - break; + case STATE::INITIAL: + return false; + break; + case STATE::READY: + [[fallthrough]]; + case STATE::LOCKED: + retval = true; + return false; + break; + default: + break; } - } - assert(false); + return true; + }); + return retval; } //! NOTE: Deliberately named `...acquire` instead of `...lock` to make them incompatible with `unique_lock` @@ -250,36 +197,36 @@ class IAsyncQueueDispatcherBase [[nodiscard]] inline T* try_acquire() { auto expected = STATE::READY; - if (state.tryTransition(expected,STATE::LOCKED)) - return getStorage(); + if (state.tryTransition(STATE::LOCKED,expected)) + return storage_t::getStorage(); return nullptr; } //! ANY THREAD [except WORKER]: Wait till we're either in READY and move us to LOCKED or bail on INITIAL // this accounts for being cancelled or consumed while waiting [[nodiscard]] inline T* acquire() { - if (state.waitAbortableTransition(STATE::READY,STATE::LOCKED,STATE::INITIAL)) - return getStorage(); + if (state.waitAbortableTransition(STATE::LOCKED,STATE::READY,STATE::INITIAL)) + return storage_t::getStorage(); return nullptr; } //! ANY THREAD [except WORKER]: Release an acquired lock inline void release() { - state.exchangeNotify(STATE::LOCKED,STATE::READY); + state.exchangeNotify(STATE::READY,STATE::LOCKED); } //! NOTE: You're in charge of ensuring future doesn't transition back to INITIAL (e.g. lock or use sanely!) inline const T* get() const { if (ready()) - return getStorage(); + return storage_t::getStorage(); return nullptr; } inline T* get() { if (future_base_t::state.query() != future_base_t::STATE::LOCKED) return nullptr; - return getStorage(); + return storage_t::getStorage(); } //! Can only be called once! If returns false means has been cancelled and nothing happened @@ -288,7 +235,7 @@ class IAsyncQueueDispatcherBase T* pSrc = acquire(); if (!pSrc) return false; - dst = std::move(*T); + dst = std::move(*pSrc); discard_common(); return true; } @@ -305,16 +252,9 @@ class IAsyncQueueDispatcherBase template inline void notify(Args&&... args) { - new (getStorage()) T(std::forward(args)...); + storage_t::construct(std::forward(args)...); future_base_t::notify(); } - - private: - inline discard_common() - { - destruct(); - state.exchangeNotify(STATE::LOCKED, STATE::INITIAL); - } }; template struct cancellable_future_t final : public future_t @@ -326,8 +266,8 @@ class IAsyncQueueDispatcherBase //! ANY THREAD [except WORKER]: Cancel pending request if we can, returns whether we actually managed to cancel inline bool cancel() { - STATE expected = STATE::ASSOCIATED; - if (state.tryTransition(expected,STATE::EXECUTING)) + auto expected = base_t::STATE::ASSOCIATED; + if (state.tryTransition(STATE::EXECUTING,expected)) { // Since we're here we've managed to move from ASSOCIATED to fake "EXECUTING" this means that the Request is either: // 1. RECORDING but after returning from `base_t::associate_request` @@ -338,7 +278,7 @@ class IAsyncQueueDispatcherBase request.exchange(nullptr)->cancel(); // after doing everything, we can mark ourselves as cleaned up - state.exchangeNotify(STATE::EXECUTING,STATE::INITIAL); + state.exchangeNotify(STATE::INITIAL,STATE::EXECUTING); return true; } // we're here because either: @@ -372,12 +312,11 @@ class IAsyncQueueDispatcherBase } inline bool disassociate_request() override { - assert(request.load()->getState().query()==request_base_t::STATE::EXECUTING); if (base_t::disassociate_request()) { // only assign if we didn't get cancelled mid-way, otherwise will mess up `associate_request` sanity checks request_base_t* prev = request.exchange(nullptr); - assert(prev); + assert(prev && prev->getState().query()==request_base_t::STATE::EXECUTING); return true; } return false; @@ -389,16 +328,16 @@ inline void IAsyncQueueDispatcherBase::request_base_t::finalize(future_base_t* f { future = fut; future->associate_request(this); - state.exchangeNotify(STATE::RECORDING,STATE::PENDING); + state.exchangeNotify(STATE::PENDING,STATE::RECORDING); } inline bool IAsyncQueueDispatcherBase::request_base_t::wait() { - if (state.waitAbortableTransition(STATE::PENDING,STATE::EXECUTING,STATE::CANCELLED) && future->disassociate_request()) + if (state.waitAbortableTransition(STATE::EXECUTING,STATE::PENDING,STATE::CANCELLED) && future->disassociate_request()) return true; //assert(future->cancellable); future = nullptr; - state.exchangeNotify(STATE::CANCELLED,STATE::INITIAL); + state.exchangeNotify(STATE::INITIAL,STATE::CANCELLED); return false; } inline void IAsyncQueueDispatcherBase::request_base_t::notify() @@ -407,7 +346,7 @@ inline void IAsyncQueueDispatcherBase::request_base_t::notify() // cleanup future = nullptr; // allow to be recycled - state.exchangeNotify(STATE::EXECUTING,STATE::INITIAL); + state.exchangeNotify(STATE::INITIAL,STATE::EXECUTING); } } @@ -438,7 +377,7 @@ inline void IAsyncQueueDispatcherBase::request_base_t::notify() * notify_all_ready() takes an r-value reference to an already locked mutex and notifies any waiters then releases the lock */ template -class IAsyncQueueDispatcher : public IThreadHandler, protected impl::IAsyncQueueDispatcherBase +class IAsyncQueueDispatcher : public IThreadHandler, protected impl::IAsyncQueueDispatcherBase { static_assert(std::is_base_of_v, "Request type must derive from request_base_t!"); static_assert(BufferSize>0u, "BufferSize must not be 0!"); @@ -467,7 +406,7 @@ class IAsyncQueueDispatcher : public IThreadHandler, pr public: inline IAsyncQueueDispatcher() {} - inline ~IAsyncQueueDispatcher() {} + inline IAsyncQueueDispatcher(base_t::start_on_construction_t) : base_t(base_t::start_on_construction_t) {} using mutex_t = typename base_t::mutex_t; using lock_t = typename base_t::lock_t; @@ -505,6 +444,7 @@ class IAsyncQueueDispatcher : public IThreadHandler, pr } protected: + inline ~IAsyncQueueDispatcher() {} void background_work() {} private: @@ -538,9 +478,8 @@ class IAsyncQueueDispatcher : public IThreadHandler, pr lock.lock(); } - - bool wakeupPredicate() const { return (cb_begin != cb_end); } - bool continuePredicate() const { return (cb_begin != cb_end); } + inline bool wakeupPredicate() const { return (cb_begin != cb_end); } + inline bool continuePredicate() const { return (cb_begin != cb_end); } }; } diff --git a/include/nbl/system/ICancellableAsyncQueueDispatcher.h b/include/nbl/system/ICancellableAsyncQueueDispatcher.h deleted file mode 100644 index a3b92c5054..0000000000 --- a/include/nbl/system/ICancellableAsyncQueueDispatcher.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef _NBL_I_CANCELLABLE_ASYNC_QUEUE_DISPATCHER_H_INCLUDED_ -#define _NBL_I_CANCELLABLE_ASYNC_QUEUE_DISPATCHER_H_INCLUDED_ - -#include "nbl/system/IAsyncQueueDispatcher.h" -#include "nbl/system/SReadWriteSpinLock.h" - -namespace nbl::system -{ - - -template -class ICancellableAsyncQueueDispatcher : public IAsyncQueueDispatcher, public impl::ICancellableAsyncQueueDispatcherBase -{ - using this_async_queue_t = ICancellableAsyncQueueDispatcher; - using base_t = IAsyncQueueDispatcher; - friend base_t; - - public: - using request_base_t = impl::ICancellableAsyncQueueDispatcherBase::request_base_t; - - static_assert(std::is_base_of_v, "Request type must derive from request_base_t!"); - - using base_t::base_t; - - protected: - //! Must be called from within process_request() - //! User is responsible for providing a value into the associated future object - template - void notify_future(RequestType& req, Args&&... args) - { - request_get_future_object >(req)->notify(std::forward(args)...); - } - - //! Must be called from within request_impl() - //! User is responsible for associating future object with a request - //! Request is automatically cancelled if it is not associated with any future object - //! More than one request associated with the same future object is undefined behaviour - template - void associate_request_with_future(RequestType& req, future_t& future) - { - assert(!future.valid()); - impl::ICancellableAsyncQueueDispatcherBase::request_associate_future_object(req,&future); - future.associate_request(&req); - } -}; - -} - -#endif diff --git a/include/nbl/system/ISystem.h b/include/nbl/system/ISystem.h index cc2f5df663..5657cda87c 100644 --- a/include/nbl/system/ISystem.h +++ b/include/nbl/system/ISystem.h @@ -2,17 +2,17 @@ #define _NBL_SYSTEM_I_SYSTEM_H_INCLUDED_ -#include "nbl/builtin/common.h" - #include "nbl/core/declarations.h" #include "nbl/core/util/bitflag.h" -#include "nbl/system/ICancellableAsyncQueueDispatcher.h" -#include "nbl/system/IFileArchive.h" -//#include "nbl/builtin/builtinResources.h" +#include "nbl/builtin/common.h" #include +#include "nbl/system/IFileArchive.h" +#include "nbl/system/IAsyncQueueDispatcher.h" +//#include "nbl/builtin/builtinResources.h" + namespace nbl::system { @@ -48,7 +48,7 @@ class NBL_API2 ISystem : public core::IReferenceCounted size_t offset; size_t size; }; - struct SRequestType : impl::ICancellableAsyncQueueDispatcherBase::request_base_t + struct SRequestType : impl::IAsyncQueueDispatcherBase::request_base_t { std::variant< SRequestParams_NOOP, @@ -58,13 +58,13 @@ class NBL_API2 ISystem : public core::IReferenceCounted > params = SRequestParams_NOOP(); }; static inline constexpr uint32_t CircularBufferSize = 256u; - class CAsyncQueue : public ICancellableAsyncQueueDispatcher + class CAsyncQueue : public IAsyncQueueDispatcher { - using base_t = ICancellableAsyncQueueDispatcher; + using base_t = IAsyncQueueDispatcher; //friend base_t; public: - CAsyncQueue(core::smart_refctd_ptr&& caller) : base_t(base_t::start_on_construction), m_caller(std::move(caller)) {} + inline CAsyncQueue(core::smart_refctd_ptr&& caller) : base_t(base_t::start_on_construction), m_caller(std::move(caller)) {} template void request_impl(SRequestType& req, FutureType& future, RequestParams&& params) diff --git a/include/nbl/system/atomic_state.h b/include/nbl/system/atomic_state.h new file mode 100644 index 0000000000..3f8dc1fc89 --- /dev/null +++ b/include/nbl/system/atomic_state.h @@ -0,0 +1,90 @@ +#ifndef _NBL_SYSTEM_ATOMIC_STATE_H_INCLUDED_ +#define _NBL_SYSTEM_ATOMIC_STATE_H_INCLUDED_ + +#include + +namespace nbl::system +{ + +template(0u)> +class atomic_state_t +{ + static_assert(std::is_enum_v); + + public: + ~atomic_state_t() + { + static_assert(std::atomic_uint32_t::is_always_lock_free); + // must have been consumed before exit ! + const auto atExit = state.load(); + assert(static_cast(atExit)==kInitial); + } + + //! + inline STATE query() const {return static_cast(state.load());} + + //! + template + inline void wait(ConditionLambda&& cond) const + { + uint32_t current; + while (cond(static_cast(current=state.load()))) + state.wait(current); + } + + //! + [[nodiscard]] inline bool tryTransition(const STATE to, STATE& expected) + { + return state.compare_exchange_strong(reinterpret_cast(expected),static_cast(to)); + } + + //! + inline void waitTransition(const STATE to, const STATE from) + { + STATE expected = from; + while (!tryTransition(to,expected)) + { + state.wait(static_cast(expected)); + expected = from; + } + assert(expected==from); + } + [[nodiscard]] inline bool waitAbortableTransition(const STATE to, const STATE from, const STATE abortState) + { + uint32_t expected = static_cast(from); + while (!tryTransition(to,expected)) + { + state.wait(expected); + if (expected==static_cast(abortState)) + return false; + expected = from; + } + assert(expected==from); + return true; + } + + //! + template + [[nodiscard]] inline STATE exchangeNotify(const STATE to) + { + const auto prev = state.exchange(static_cast(to)); + if constexpr (all) + state.notify_all(); + else + state.notify_one(); + return static_cast(prev); + } + template + inline void exchangeNotify(const STATE to, const STATE expected) + { + const auto prev = exchangeNotify(to); + assert(static_cast(prev)==expected); + } + + private: + std::atomic_uint32_t state = static_cast(kInitial); +}; + +} + +#endif \ No newline at end of file From 4a111054859d00e502b4846c94a5ddaef8884d08 Mon Sep 17 00:00:00 2001 From: devsh Date: Mon, 13 Mar 2023 17:28:27 +0100 Subject: [PATCH 05/24] the draft and the docs improve --- include/nbl/system/IAsyncQueueDispatcher.h | 113 ++++++++------- include/nbl/system/ISystem.h | 157 ++++++++++----------- 2 files changed, 137 insertions(+), 133 deletions(-) diff --git a/include/nbl/system/IAsyncQueueDispatcher.h b/include/nbl/system/IAsyncQueueDispatcher.h index 9f91d9de19..0a45ec7003 100644 --- a/include/nbl/system/IAsyncQueueDispatcher.h +++ b/include/nbl/system/IAsyncQueueDispatcher.h @@ -13,10 +13,10 @@ namespace impl { class IAsyncQueueDispatcherBase { - public: + protected: struct future_base_t; // dont want to play around with relaxed memory ordering yet - struct request_base_t + struct request_base_t // TODO: protect to anyone but inheritor { public: enum class STATE : uint32_t @@ -92,6 +92,8 @@ class IAsyncQueueDispatcherBase LOCKED=4 }; + protected: + friend struct request_base_t; //! REQUESTING THREAD: done as part of filling out the request virtual inline void associate_request(request_base_t* req) { @@ -111,7 +113,6 @@ class IAsyncQueueDispatcherBase state.exchangeNotify(STATE::READY,STATE::EXECUTING); } - protected: // the base class is not directly usable virtual inline ~future_base_t() { @@ -129,7 +130,7 @@ class IAsyncQueueDispatcherBase atomic_state_t state= {}; }; - protected: + // not meant for direct usage IAsyncQueueDispatcherBase() = default; ~IAsyncQueueDispatcherBase() = default; @@ -145,7 +146,7 @@ class IAsyncQueueDispatcherBase } public: - inline future_t() = default; + inline future_t() {} inline ~future_t() { discard(); @@ -230,7 +231,7 @@ class IAsyncQueueDispatcherBase } //! Can only be called once! If returns false means has been cancelled and nothing happened - inline bool move_into(T& dst) + [[nodiscard]] inline bool move_into(T& dst) { T* pSrc = acquire(); if (!pSrc) @@ -240,6 +241,15 @@ class IAsyncQueueDispatcherBase return true; } + //! Utility to write less code, WILL ASSERT IF IT FAILS TO ACQUIRE! So don't use on futures that might be cancelled or fail. + inline T copy_out() + { + T retval; + const bool success = move_into(retval); + assert(success); + return retval; + } + //! virtual inline void discard() { @@ -250,14 +260,13 @@ class IAsyncQueueDispatcherBase protected: // construct the retval element template - inline void notify(Args&&... args) + inline void construct(Args&&... args) { storage_t::construct(std::forward(args)...); - future_base_t::notify(); } }; template - struct cancellable_future_t final : public future_t + struct cancellable_future_t : public future_t { using base_t = future_t; std::atomic request = nullptr; @@ -267,7 +276,7 @@ class IAsyncQueueDispatcherBase inline bool cancel() { auto expected = base_t::STATE::ASSOCIATED; - if (state.tryTransition(STATE::EXECUTING,expected)) + if (base_t::state.tryTransition(base_t::STATE::EXECUTING,expected)) { // Since we're here we've managed to move from ASSOCIATED to fake "EXECUTING" this means that the Request is either: // 1. RECORDING but after returning from `base_t::associate_request` @@ -278,7 +287,7 @@ class IAsyncQueueDispatcherBase request.exchange(nullptr)->cancel(); // after doing everything, we can mark ourselves as cleaned up - state.exchangeNotify(STATE::INITIAL,STATE::EXECUTING); + base_t::state.exchangeNotify(base_t::STATE::INITIAL, base_t::STATE::EXECUTING); return true; } // we're here because either: @@ -288,7 +297,7 @@ class IAsyncQueueDispatcherBase // - request is ready // - storage is locked/acquired // sanity check (there's a tiny gap between transitioning to EXECUTING and disassociating request) - assert(expected==STATE::EXECUTING || request==nullptr); + assert(expected==base_t::STATE::EXECUTING || request==nullptr); return false; } @@ -303,14 +312,14 @@ class IAsyncQueueDispatcherBase } private: - inline void associate_request(request_base_t* req) override + inline void associate_request(request_base_t* req) override final { base_t::associate_request(req); request_base_t* prev = request.exchange(req); // sanity check assert(prev==nullptr); } - inline bool disassociate_request() override + inline bool disassociate_request() override final { if (base_t::disassociate_request()) { @@ -357,29 +366,19 @@ inline void IAsyncQueueDispatcherBase::request_base_t::notify() * void init(internal_state_t* state); // required only in case of custom internal state * * void exit(internal_state_t* state); // optional, no `state` parameter in case of no internal state -* -* void request_impl(request_t& req, ...); // `...` are parameteres forwarded from request(), the request's state is locked with a mutex during the call -* void process_request(request_t& req, internal_state_t& state); // no `state` parameter in case of no internal state -* void background_work() // optional, does nothing if not provided * +* // no `state` parameter in case of no internal state +* void process_request(request_metadata_t& req, internal_state_t& state); +* +* void background_work() // optional, does nothing if not provided * -* Provided RequestType shall define 5 methods: -* void start(); -* void finalize(); -* bool wait(); -* void notify(); -* TODO: [outdated docs] lock() will be called just before processing the request, and unlock() will be called just after processing the request. -* Those are to enable safe external write access to the request struct for user-defined purposes. -* -* wait_for_result() will wait until the Async queue completes processing the request and notifies us that the request is ready, -* the request will remain locked upon return (so nothing overwrites its address on the circular buffer) * -* notify_all_ready() takes an r-value reference to an already locked mutex and notifies any waiters then releases the lock +* The `lock()` will be called just before calling into `background_work()` and processing any requests via `process_request()`, +* `unlock()` will be called just after processing the request (if any). */ -template +template class IAsyncQueueDispatcher : public IThreadHandler, protected impl::IAsyncQueueDispatcherBase { - static_assert(std::is_base_of_v, "Request type must derive from request_base_t!"); static_assert(BufferSize>0u, "BufferSize must not be 0!"); static_assert(core::isPoT(BufferSize), "BufferSize must be power of two!"); @@ -387,6 +386,11 @@ class IAsyncQueueDispatcher : public IThreadHandler, pro using base_t = IThreadHandler; friend base_t; // TODO: remove, some functions should just be protected + struct request_t : public request_base_t + { + request_metadata_t m_metadata; + }; + private: constexpr static inline uint32_t MaxRequestCount = BufferSize; @@ -394,7 +398,7 @@ class IAsyncQueueDispatcher : public IThreadHandler, pro using atomic_counter_t = std::atomic_uint64_t; using counter_t = atomic_counter_t::value_type; - RequestType request_pool[MaxRequestCount]; + request_t request_pool[MaxRequestCount]; atomic_counter_t cb_begin = 0u; atomic_counter_t cb_end = 0u; @@ -413,39 +417,41 @@ class IAsyncQueueDispatcher : public IThreadHandler, pro using cvar_t = typename base_t::cvar_t; using internal_state_t = typename base_t::internal_state_t; - using request_t = RequestType; + template + using future_t = impl::IAsyncQueueDispatcherBase::future_t; + template + using cancellable_future_t = impl::IAsyncQueueDispatcherBase::cancellable_future_t; - // Returns a reference to a request's storage in the circular buffer after processing the moved arguments - // YOU MUST CONSUME THE REQUEST by calling `discard_storage()` on it EXACTLY ONCE! - // YOU MUST CALL IT EVEN IF THERE'S NO DATA YOU WISH TO GET BACK FROM IT! - // (if you don't the queue will deadlock because of an unresolved overflow) - template - request_t& request(Args&&... args) + //! Constructs a request with `args` via `CRTP::request_impl` on the circular buffer after there's enough space to accomodate it. + //! Then it associates the request to a future passed in as the first argument. + template + void request(future_t* _future, Args&&... args) { - auto virtualIx = cb_end++; - auto safe_begin = virtualIx(0) : (virtualIx-MaxRequestCount+1u); - - for (counter_t old_begin; (old_begin = cb_begin.load()) < safe_begin; ) + // get next output index + const auto virtualIx = cb_end++; + // protect against overflow by waiting for the worker to catch up + const auto safe_begin = virtualIx(0) : (virtualIx-MaxRequestCount+1u); + for (counter_t old_begin; (old_begin=cb_begin.load())(this)->request_impl(req, std::forward(args)...); - req.finalize(); + req.m_metadata = request_metadata_t(std::forward(args)...); + req.finalize(_future); { auto global_lk = base_t::createLock(); // wake up queue thread (needs to happen under a lock to not miss a wakeup) base_t::m_cvar.notify_one(); } - return req; } protected: inline ~IAsyncQueueDispatcher() {} - void background_work() {} + inline void background_work() {} private: template @@ -463,17 +469,16 @@ class IAsyncQueueDispatcher : public IThreadHandler, pro request_t& req = request_pool[r_id]; // do NOT allow cancelling or modification of the request while working on it - if (req.wait_for_work()) + if (req.wait()) { - // if the request supports cancelling and got cancelled, the wait_for_work function may return false - static_cast(this)->process_request(req, optional_internal_state...); + // if the request supports cancelling and got cancelled, then `wait()` function may return false + static_cast(this)->process_request(req.m_metadata,optional_internal_state...); + req.notify(); } // wake the waiter up - req.notify_ready(); cb_begin++; - #if __cplusplus >= 202002L - cb_begin.notify_one(); - #endif + // this does not need to happen under a lock, because its not a condvar + cb_begin.notify_one(); } lock.lock(); } diff --git a/include/nbl/system/ISystem.h b/include/nbl/system/ISystem.h index 5657cda87c..4d0834df5f 100644 --- a/include/nbl/system/ISystem.h +++ b/include/nbl/system/ISystem.h @@ -23,85 +23,26 @@ class NBL_API2 ISystem : public core::IReferenceCounted { public: inline static constexpr uint32_t MAX_FILENAME_LENGTH = 4096; - protected: - class ICaller; - private: - struct SRequestParams_NOOP - { - }; - struct SRequestParams_CREATE_FILE - { - char filename[MAX_FILENAME_LENGTH] {}; - IFileBase::E_CREATE_FLAGS flags; - }; - struct SRequestParams_READ - { - ISystemFile* file; - void* buffer; - size_t offset; - size_t size; - }; - struct SRequestParams_WRITE - { - ISystemFile* file; - const void* buffer; - size_t offset; - size_t size; - }; - struct SRequestType : impl::IAsyncQueueDispatcherBase::request_base_t - { - std::variant< - SRequestParams_NOOP, - SRequestParams_CREATE_FILE, - SRequestParams_READ, - SRequestParams_WRITE - > params = SRequestParams_NOOP(); - }; - static inline constexpr uint32_t CircularBufferSize = 256u; - class CAsyncQueue : public IAsyncQueueDispatcher - { - using base_t = IAsyncQueueDispatcher; - //friend base_t; - - public: - inline CAsyncQueue(core::smart_refctd_ptr&& caller) : base_t(base_t::start_on_construction), m_caller(std::move(caller)) {} - - template - void request_impl(SRequestType& req, FutureType& future, RequestParams&& params) - { - req.params = std::move(params); - base_t::associate_request_with_future(req, future); - } - - void process_request(SRequestType& req); - - void init() {} - - private: - void handle_request(SRequestType& req, SRequestParams_NOOP& param); - void handle_request(SRequestType& req, SRequestParams_CREATE_FILE& param); - void handle_request(SRequestType& req, SRequestParams_READ& param); - void handle_request(SRequestType& req, SRequestParams_WRITE& param); - core::smart_refctd_ptr m_caller; - }; - friend class ISystemFile; - CAsyncQueue m_dispatcher; - - public: + //! We overrride the future a little bit, to allow to put a result in it right away without asynchronocity template - struct future_t : public CAsyncQueue::future_t + struct future_t final : public impl::IAsyncQueueDispatcherBase::cancellable_future_t { - friend class ISystem; + private: friend class IFutureManipulator; + template + inline void set_result(Args&&... args) + { + state.waitTransition(STATE::EXECUTING,STATE::INITIAL); + construct(std::forward(args)...); + notify(); + } }; class IFutureManipulator { protected: - //template - //inline void fake_notify(future_t& future, T&& value) const - inline void fake_notify(future_t& future, const size_t value) const + inline void set_result(future_t& future, const size_t value) const { - future.notify(value); + future.set_result(value); } }; @@ -110,11 +51,13 @@ class NBL_API2 ISystem : public core::IReferenceCounted inline core::smart_refctd_ptr loadBuiltinData() { #ifdef _NBL_EMBED_BUILTIN_RESOURCES_ - return impl_loadEmbeddedBuiltinData(Path.value, nbl::builtin::get_resource()); + return impl_loadEmbeddedBuiltinData(Path.value,nbl::builtin::get_resource()); #else future_t> future; createFile(future,system::path(Path.value),core::bitflag(IFileBase::ECF_READ)|IFileBase::ECF_MAPPABLE); - return future.get(); + if (future.wait()) + return future.copy_out(); + return nullptr; #endif } @@ -138,7 +81,7 @@ class NBL_API2 ISystem : public core::IReferenceCounted bool isPathReadOnly(const system::path& p) const; // - virtual bool isDirectory(const system::path& p) const + virtual inline bool isDirectory(const system::path& p) const { if (isPathReadOnly(p)) return p.extension()==""; // TODO: this is a temporary decision until we figure out how to check if a file is directory in android APK @@ -183,11 +126,9 @@ class NBL_API2 ISystem : public core::IReferenceCounted future_t> future; createFile(future, filename, core::bitflag(IFileBase::ECF_READ)|IFileBase::ECF_MAPPABLE); - auto file = future.get(); - if (!file) - return nullptr; - - return openFileArchive(std::move(file),password); + if (future.wait()) + return openFileArchive(future.copy_out(),password); + return nullptr; } // After opening and archive, you must mount it if you want the global path lookup to work seamlessly. @@ -274,6 +215,64 @@ class NBL_API2 ISystem : public core::IReferenceCounted } m_loaders; // core::CMultiObjectCache> m_cachedArchiveFiles; + + protected: + class ICaller; + private: + struct SRequestParams_NOOP + { + }; + struct SRequestParams_CREATE_FILE + { + char filename[MAX_FILENAME_LENGTH] {}; + IFileBase::E_CREATE_FLAGS flags; + }; + struct SRequestParams_READ + { + ISystemFile* file; + void* buffer; + size_t offset; + size_t size; + }; + struct SRequestParams_WRITE + { + ISystemFile* file; + const void* buffer; + size_t offset; + size_t size; + }; + struct SRequestType + { + std::variant< + SRequestParams_NOOP, + SRequestParams_CREATE_FILE, + SRequestParams_READ, + SRequestParams_WRITE + > params = SRequestParams_NOOP(); + }; + static inline constexpr uint32_t CircularBufferSize = 256u; + class CAsyncQueue : public IAsyncQueueDispatcher + { + using base_t = IAsyncQueueDispatcher; + //friend base_t; + + public: + inline CAsyncQueue(core::smart_refctd_ptr&& caller) : base_t(base_t::start_on_construction), m_caller(std::move(caller)) {} + + void process_request(SRequestType& req); + + void init() {} + + private: + void handle_request(SRequestType& req, SRequestParams_NOOP& param); + void handle_request(SRequestType& req, SRequestParams_CREATE_FILE& param); + void handle_request(SRequestType& req, SRequestParams_READ& param); + void handle_request(SRequestType& req, SRequestParams_WRITE& param); + + core::smart_refctd_ptr m_caller; + }; + friend class ISystemFile; + CAsyncQueue m_dispatcher; }; } From 4273dfb4c5a8ad1dad1c6544c85a64af0177ed0f Mon Sep 17 00:00:00 2001 From: devsh Date: Mon, 13 Mar 2023 17:58:40 +0100 Subject: [PATCH 06/24] rework parts of `nbl::system` to work with the New API --- include/nbl/asset/IAssetManager.h | 12 ++++---- include/nbl/system/CFileLogger.h | 6 ++-- include/nbl/system/IAsyncQueueDispatcher.h | 2 +- include/nbl/system/IFile.h | 32 ++++++++++++++++------ include/nbl/system/ISystem.h | 9 ++++-- include/nbl/ui/CWindowManagerWin32.h | 2 +- src/nbl/asset/interchange/CPLYMeshWriter.h | 9 ++---- 7 files changed, 43 insertions(+), 29 deletions(-) diff --git a/include/nbl/asset/IAssetManager.h b/include/nbl/asset/IAssetManager.h index e0e1009220..6cbdcd9d10 100644 --- a/include/nbl/asset/IAssetManager.h +++ b/include/nbl/asset/IAssetManager.h @@ -652,15 +652,15 @@ class NBL_API2 IAssetManager : public core::IReferenceCounted, public core::Quit _override = &defOverride; system::ISystem::future_t> future; - m_system->createFile(future, (_params.workingDirectory.generic_string() + _filename).c_str(), system::IFile::ECF_WRITE); - auto file = future.get(); - if (file) // could fail creating file (lack of permissions) + m_system->createFile(future, (_params.workingDirectory.generic_string()+_filename).c_str(), system::IFile::ECF_WRITE); + core::smart_refctd_ptr* file; + if (future.wait() && (file=future.acquire())) { - bool res = writeAsset(file.get(), _params, _override); + bool res = writeAsset(file->get(), _params, _override); + future.release(); return res; } - else - return false; + return false; } bool writeAsset(system::IFile* _file, const IAssetWriter::SAssetWriteParams& _params, IAssetWriter::IAssetWriterOverride* _override) { diff --git a/include/nbl/system/CFileLogger.h b/include/nbl/system/CFileLogger.h index d625761c5e..4a368464f0 100644 --- a/include/nbl/system/CFileLogger.h +++ b/include/nbl/system/CFileLogger.h @@ -21,9 +21,9 @@ class CFileLogger : public IThreadsafeLogger virtual void threadsafeLog_impl(const std::string_view& fmt, E_LOG_LEVEL logLevel, va_list args) override { const auto str = constructLogString(fmt, logLevel, args); - ISystem::future_t future; - m_file->write(future,str.data(),m_pos,str.length()); - m_pos += future.get(); // need to use the future to make sure op is actually executed :( + IFile::success_t succ; + m_file->write(succ,str.data(),m_pos,str.length()); + m_pos += succ.getBytesProcessed(); } core::smart_refctd_ptr m_file; diff --git a/include/nbl/system/IAsyncQueueDispatcher.h b/include/nbl/system/IAsyncQueueDispatcher.h index 0a45ec7003..e85676400d 100644 --- a/include/nbl/system/IAsyncQueueDispatcher.h +++ b/include/nbl/system/IAsyncQueueDispatcher.h @@ -29,7 +29,7 @@ class IAsyncQueueDispatcherBase }; // in case you need it, which you won't - inline const auto& getState() const {return state;} + inline const atomic_state_t& getState() const {return state;} //! REQUESTING THREAD: lock when overwriting the request's data inline void start() diff --git a/include/nbl/system/IFile.h b/include/nbl/system/IFile.h index 643f450b62..3172caf935 100644 --- a/include/nbl/system/IFile.h +++ b/include/nbl/system/IFile.h @@ -20,11 +20,12 @@ class IFile : public IFileBase, private ISystem::IFutureManipulator if (offset+sizeToRead>size) sizeToRead = size-offset; memcpy(buffer,ptr+offset,sizeToRead); - fake_notify(fut,sizeToRead); + set_result(fut,sizeToRead); } else unmappedRead(fut,buffer,offset,sizeToRead); } + // inline void write(ISystem::future_t& fut, const void* buffer, size_t offset, size_t sizeToWrite) { auto* ptr = reinterpret_cast(getMappedPointer()); @@ -34,31 +35,44 @@ class IFile : public IFileBase, private ISystem::IFutureManipulator if (offset+sizeToWrite>getSize()) sizeToWrite = getSize()-offset; memcpy(ptr+offset,buffer,sizeToWrite); - fake_notify(fut,sizeToWrite); + set_result(fut,sizeToWrite); } else unmappedWrite(fut,buffer,offset,sizeToWrite); } - // + //! Less verbose future handling struct success_t { public: success_t() = default; ~success_t() = default; + inline size_t getBytesToProcess() const {return sizeToProcess;} + + inline size_t getBytesProcessed(const bool block=true) const + { + if (block && !m_internalFuture.wait()) + return 0ull; + return *m_internalFuture.get(); + } + inline explicit operator bool() { - return m_internalFuture.get()==sizeToProcess; + return getBytesProcessed()==getBytesToProcess(); } inline bool operator!() { - return m_internalFuture.get()!=sizeToProcess; + return getBytesProcessed()!=getBytesToProcess(); } - inline size_t getSizeToProcess() const {return sizeToProcess;} - private: + // cannot move in memory or pointers go boom + success_t(const success_t&) = delete; + success_t(success_t&&) = delete; + success_t& operator=(const success_t&) = delete; + success_t& operator=(success_t&&) = delete; + friend IFile; ISystem::future_t m_internalFuture; size_t sizeToProcess; @@ -81,11 +95,11 @@ class IFile : public IFileBase, private ISystem::IFutureManipulator // virtual void unmappedRead(ISystem::future_t& fut, void* buffer, size_t offset, size_t sizeToRead) { - fake_notify(fut,0ull); + set_result(fut,0ull); } virtual void unmappedWrite(ISystem::future_t& fut, const void* buffer, size_t offset, size_t sizeToWrite) { - fake_notify(fut,0ull); + set_result(fut,0ull); } }; diff --git a/include/nbl/system/ISystem.h b/include/nbl/system/ISystem.h index 4d0834df5f..b744051b50 100644 --- a/include/nbl/system/ISystem.h +++ b/include/nbl/system/ISystem.h @@ -29,12 +29,15 @@ class NBL_API2 ISystem : public core::IReferenceCounted { private: friend class IFutureManipulator; + + using base_t = impl::IAsyncQueueDispatcherBase::cancellable_future_t; + template inline void set_result(Args&&... args) { - state.waitTransition(STATE::EXECUTING,STATE::INITIAL); - construct(std::forward(args)...); - notify(); + base_t::state.waitTransition(base_t::STATE::EXECUTING,base_t::STATE::INITIAL); + base_t::construct(std::forward(args)...); + base_t::notify(); } }; class IFutureManipulator diff --git a/include/nbl/ui/CWindowManagerWin32.h b/include/nbl/ui/CWindowManagerWin32.h index 0e7530fbc3..ebd9d8c8c0 100644 --- a/include/nbl/ui/CWindowManagerWin32.h +++ b/include/nbl/ui/CWindowManagerWin32.h @@ -168,7 +168,7 @@ class CWindowManagerWin32 : public IWindowManager uint32_t minimized : 1 = 0; uint32_t maximized : 1 = 0; }; - struct SRequest : system::impl::IAsyncQueueDispatcherBase::request_base_t + struct SRequest { E_REQUEST_TYPE type; union diff --git a/src/nbl/asset/interchange/CPLYMeshWriter.h b/src/nbl/asset/interchange/CPLYMeshWriter.h index 1617c5dfee..1de4e14142 100644 --- a/src/nbl/asset/interchange/CPLYMeshWriter.h +++ b/src/nbl/asset/interchange/CPLYMeshWriter.h @@ -73,12 +73,9 @@ class CPLYMeshWriter : public asset::IAssetWriter } auto str = ss.str(); - system::ISystem::future_t future; - context.writeContext.outputFile->write(future, str.c_str(), context.fileOffset, str.size()); - { - const auto bytesWritten = future.get(); - context.fileOffset += bytesWritten; - } + system::IFile::success_t succ; + context.writeContext.outputFile->write(succ, str.c_str(), context.fileOffset, str.size()); + context.fileOffset += succ.getBytesProcessed(); } }; From b535556a775c4e10522779734ba3959e6be648a6 Mon Sep 17 00:00:00 2001 From: devsh Date: Tue, 14 Mar 2023 18:02:54 +0100 Subject: [PATCH 07/24] fix a few things here and there --- include/nbl/system/IAsyncQueueDispatcher.h | 33 ++++++++++++++++++---- include/nbl/system/atomic_state.h | 6 ++-- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/include/nbl/system/IAsyncQueueDispatcher.h b/include/nbl/system/IAsyncQueueDispatcher.h index e85676400d..cbad7b7f59 100644 --- a/include/nbl/system/IAsyncQueueDispatcher.h +++ b/include/nbl/system/IAsyncQueueDispatcher.h @@ -58,6 +58,7 @@ class IAsyncQueueDispatcherBase protected: // the base class is not directly usable + inline request_base_t() = default; inline ~request_base_t() { // fully cleaned up @@ -114,6 +115,7 @@ class IAsyncQueueDispatcherBase } // the base class is not directly usable + inline future_base_t() = default; virtual inline ~future_base_t() { // non-cancellable future just need to get to this state, and cancellable will move here @@ -127,7 +129,7 @@ class IAsyncQueueDispatcherBase // this tells us whether an object with a lifetime has been constructed over the memory backing the future // also acts as a lock - atomic_state_t state= {}; + atomic_state_t state = {}; }; // not meant for direct usage @@ -146,7 +148,7 @@ class IAsyncQueueDispatcherBase } public: - inline future_t() {} + inline future_t() : future_base_t() {} inline ~future_t() { discard(); @@ -258,6 +260,8 @@ class IAsyncQueueDispatcherBase } protected: + // to get access to the below + friend class IAsyncQueueDispatcherBase; // construct the retval element template inline void construct(Args&&... args) @@ -331,6 +335,21 @@ class IAsyncQueueDispatcherBase return false; } }; + + protected: + template + class future_constructor_t final + { + future_t* pFuture; + public: + inline future_constructor_t(future_base_t* _future_base) : pFuture(static_cast*>(_future_base)) {} + + template + inline void operator()(Args&&... args) + { + pFuture->construct(std::forward(args)...); + } + }; }; inline void IAsyncQueueDispatcherBase::request_base_t::finalize(future_base_t* fut) @@ -388,7 +407,9 @@ class IAsyncQueueDispatcher : public IThreadHandler, pro struct request_t : public request_base_t { - request_metadata_t m_metadata; + inline request_t() : request_base_t() {} + + request_metadata_t m_metadata = {}; }; private: @@ -410,7 +431,7 @@ class IAsyncQueueDispatcher : public IThreadHandler, pro public: inline IAsyncQueueDispatcher() {} - inline IAsyncQueueDispatcher(base_t::start_on_construction_t) : base_t(base_t::start_on_construction_t) {} + inline IAsyncQueueDispatcher(base_t::start_on_construction_t) : base_t(base_t::start_on_construction) {} using mutex_t = typename base_t::mutex_t; using lock_t = typename base_t::lock_t; @@ -454,7 +475,7 @@ class IAsyncQueueDispatcher : public IThreadHandler, pro inline void background_work() {} private: - template + template void work(lock_t& lock, Args&&... optional_internal_state) { lock.unlock(); @@ -472,7 +493,7 @@ class IAsyncQueueDispatcher : public IThreadHandler, pro if (req.wait()) { // if the request supports cancelling and got cancelled, then `wait()` function may return false - static_cast(this)->process_request(req.m_metadata,optional_internal_state...); + static_cast(this)->process_request(nullptr/*TODO*/,req.m_metadata,optional_internal_state...); req.notify(); } // wake the waiter up diff --git a/include/nbl/system/atomic_state.h b/include/nbl/system/atomic_state.h index 3f8dc1fc89..cf3aa78581 100644 --- a/include/nbl/system/atomic_state.h +++ b/include/nbl/system/atomic_state.h @@ -51,11 +51,11 @@ class atomic_state_t } [[nodiscard]] inline bool waitAbortableTransition(const STATE to, const STATE from, const STATE abortState) { - uint32_t expected = static_cast(from); + STATE expected = from; while (!tryTransition(to,expected)) { - state.wait(expected); - if (expected==static_cast(abortState)) + state.wait(static_cast(expected)); + if (expected==abortState) return false; expected = from; } From 87eeacb2502abdab30ce699c4177c43e8af37d5d Mon Sep 17 00:00:00 2001 From: devsh Date: Tue, 14 Mar 2023 23:19:13 +0100 Subject: [PATCH 08/24] figure out how we're gonna construct the results over the future's storage --- include/nbl/asset/IAssetManager.h | 21 +-- include/nbl/system/IAsyncQueueDispatcher.h | 164 +++++++++++---------- include/nbl/system/ISystem.h | 37 ++--- include/nbl/system/ISystemFile.h | 7 +- src/nbl/system/ISystem.cpp | 65 ++++---- 5 files changed, 150 insertions(+), 144 deletions(-) diff --git a/include/nbl/asset/IAssetManager.h b/include/nbl/asset/IAssetManager.h index 6cbdcd9d10..2c03e42372 100644 --- a/include/nbl/asset/IAssetManager.h +++ b/include/nbl/asset/IAssetManager.h @@ -330,14 +330,12 @@ class NBL_API2 IAssetManager : public core::IReferenceCounted, public core::Quit filePath = _params.workingDirectory/filePath; _override->getLoadFilename(filePath, m_system.get(), ctx, _hierarchyLevel); } - + system::ISystem::future_t> future; m_system->createFile(future, filePath, system::IFile::ECF_READ); - auto file = future.get(); - if (!file) - return SAssetBundle(0); - - return getAssetInHierarchy_impl(file.get(), filePath.string(), ctx.params, _hierarchyLevel, _override); + if (auto file=future.acquire()) + return getAssetInHierarchy_impl(file->get(), filePath.string(), ctx.params, _hierarchyLevel, _override); + return SAssetBundle(0); } //TODO change name @@ -653,14 +651,9 @@ class NBL_API2 IAssetManager : public core::IReferenceCounted, public core::Quit system::ISystem::future_t> future; m_system->createFile(future, (_params.workingDirectory.generic_string()+_filename).c_str(), system::IFile::ECF_WRITE); - core::smart_refctd_ptr* file; - if (future.wait() && (file=future.acquire())) - { - bool res = writeAsset(file->get(), _params, _override); - future.release(); - return res; - } - return false; + if (auto file=future.acquire()) + return writeAsset(file->get(), _params, _override); + return false; } bool writeAsset(system::IFile* _file, const IAssetWriter::SAssetWriteParams& _params, IAssetWriter::IAssetWriterOverride* _override) { diff --git a/include/nbl/system/IAsyncQueueDispatcher.h b/include/nbl/system/IAsyncQueueDispatcher.h index cbad7b7f59..6164c60370 100644 --- a/include/nbl/system/IAsyncQueueDispatcher.h +++ b/include/nbl/system/IAsyncQueueDispatcher.h @@ -138,20 +138,18 @@ class IAsyncQueueDispatcherBase public: template - class future_t : private core::StorageTrivializer, protected future_base_t + class future_t : private core::StorageTrivializer, public future_base_t { using storage_t = core::StorageTrivializer; - inline void discard_common() - { - storage_t::destruct(); - state.exchangeNotify(STATE::INITIAL,STATE::LOCKED); - } + + friend class storage_lock_t; public: inline future_t() : future_base_t() {} - inline ~future_t() + virtual inline ~future_t() { - discard(); + if (auto lock=acquire()) + lock.discard(); } //! @@ -193,31 +191,6 @@ class IAsyncQueueDispatcherBase return retval; } - //! NOTE: Deliberately named `...acquire` instead of `...lock` to make them incompatible with `unique_lock` - // and other RAII locks as the blocking aquire can fail and that needs to be handled. - - //! ANY THREAD [except WORKER]: If we're READY transition to LOCKED - [[nodiscard]] inline T* try_acquire() - { - auto expected = STATE::READY; - if (state.tryTransition(STATE::LOCKED,expected)) - return storage_t::getStorage(); - return nullptr; - } - //! ANY THREAD [except WORKER]: Wait till we're either in READY and move us to LOCKED or bail on INITIAL - // this accounts for being cancelled or consumed while waiting - [[nodiscard]] inline T* acquire() - { - if (state.waitAbortableTransition(STATE::LOCKED,STATE::READY,STATE::INITIAL)) - return storage_t::getStorage(); - return nullptr; - } - //! ANY THREAD [except WORKER]: Release an acquired lock - inline void release() - { - state.exchangeNotify(STATE::READY,STATE::LOCKED); - } - //! NOTE: You're in charge of ensuring future doesn't transition back to INITIAL (e.g. lock or use sanely!) inline const T* get() const { @@ -225,38 +198,86 @@ class IAsyncQueueDispatcherBase return storage_t::getStorage(); return nullptr; } - inline T* get() + + //! Utility to write less code, WILL ASSERT IF IT FAILS! So don't use on futures that might be cancelled or fail. + inline T copy() const { - if (future_base_t::state.query() != future_base_t::STATE::LOCKED) - return nullptr; - return storage_t::getStorage(); + const bool success = wait(); + assert(success); + return *get(); } - //! Can only be called once! If returns false means has been cancelled and nothing happened - [[nodiscard]] inline bool move_into(T& dst) + //! NOTE: Deliberately named `...acquire` instead of `...lock` to make them incompatible with `std::unique_lock` + // and other RAII locks as the blocking aquire can fail and that needs to be handled. + class storage_lock_t final { - T* pSrc = acquire(); - if (!pSrc) - return false; - dst = std::move(*pSrc); - discard_common(); - return true; - } + using state_enum = future_base_t::STATE; + future_t* m_future; + + //! constructor, arg is nullptr if locked + friend class future_t; + inline storage_lock_t(future_t* _future) : m_future(_future) + { + assert(m_future->state.query()==state_enum::LOCKED); + } + //! as usual for "unique" things + inline storage_lock_t(const storage_lock_t&) = delete; + inline storage_lock_t& operator=(const storage_lock_t&) = delete; + + public: + inline ~storage_lock_t() + { + if (m_future) + m_future->state.exchangeNotify(state_enum::READY,state_enum::LOCKED); + } + + //! + inline explicit operator bool() + { + return m_future; + } + inline bool operator!() + { + return !m_future; + } - //! Utility to write less code, WILL ASSERT IF IT FAILS TO ACQUIRE! So don't use on futures that might be cancelled or fail. - inline T copy_out() + //! + inline T* operator->() const + { + if (m_future) + return m_future->getStorage(); + return nullptr; + } + template requires (std::is_same_v && !std::is_void_v) + inline U& operator*() const {return *operator->();} + + //! Can only be called once! + inline void discard() + { + assert(m_future); + m_future->destruct(); + m_future->state.exchangeNotify(state_enum::INITIAL,state_enum::LOCKED); + } + //! Can only be called once! + template requires (std::is_same_v && !std::is_void_v) + inline void move_into(U& dst) + { + dst = std::move(operator*()); + discard(); + } + }; + + //! ANY THREAD [except WORKER]: If we're READY transition to LOCKED + inline storage_lock_t try_acquire() { - T retval; - const bool success = move_into(retval); - assert(success); - return retval; + auto expected = STATE::READY; + return storage_lock_t(state.tryTransition(STATE::LOCKED,expected) ? this:nullptr); } - - //! - virtual inline void discard() + //! ANY THREAD [except WORKER]: Wait till we're either in READY and move us to LOCKED or bail on INITIAL + // this accounts for being cancelled or consumed while waiting + inline storage_lock_t acquire() { - if (acquire()) - discard_common(); + return storage_lock_t(state.waitAbortableTransition(STATE::LOCKED,STATE::READY,STATE::INITIAL) ? this:nullptr); } protected: @@ -276,6 +297,12 @@ class IAsyncQueueDispatcherBase std::atomic request = nullptr; public: + inline ~cancellable_future_t() + { + // try to cancel + cancel(); + } + //! ANY THREAD [except WORKER]: Cancel pending request if we can, returns whether we actually managed to cancel inline bool cancel() { @@ -305,16 +332,6 @@ class IAsyncQueueDispatcherBase return false; } - inline void discard() override final - { - // try to cancel - cancel(); - // sanity check - assert(request==nullptr); - // proceed with the usual - base_t::discard(); - } - private: inline void associate_request(request_base_t* req) override final { @@ -338,18 +355,7 @@ class IAsyncQueueDispatcherBase protected: template - class future_constructor_t final - { - future_t* pFuture; - public: - inline future_constructor_t(future_base_t* _future_base) : pFuture(static_cast*>(_future_base)) {} - - template - inline void operator()(Args&&... args) - { - pFuture->construct(std::forward(args)...); - } - }; + static inline core::StorageTrivializer* future_storage_cast(future_base_t* _future_base) {return static_cast*>(_future_base);} }; inline void IAsyncQueueDispatcherBase::request_base_t::finalize(future_base_t* fut) diff --git a/include/nbl/system/ISystem.h b/include/nbl/system/ISystem.h index b744051b50..4ef3184e33 100644 --- a/include/nbl/system/ISystem.h +++ b/include/nbl/system/ISystem.h @@ -28,6 +28,7 @@ class NBL_API2 ISystem : public core::IReferenceCounted struct future_t final : public impl::IAsyncQueueDispatcherBase::cancellable_future_t { private: + friend class ISystem; friend class IFutureManipulator; using base_t = impl::IAsyncQueueDispatcherBase::cancellable_future_t; @@ -59,7 +60,7 @@ class NBL_API2 ISystem : public core::IReferenceCounted future_t> future; createFile(future,system::path(Path.value),core::bitflag(IFileBase::ECF_READ)|IFileBase::ECF_MAPPABLE); if (future.wait()) - return future.copy_out(); + return future.copy(); return nullptr; #endif } @@ -130,7 +131,7 @@ class NBL_API2 ISystem : public core::IReferenceCounted createFile(future, filename, core::bitflag(IFileBase::ECF_READ)|IFileBase::ECF_MAPPABLE); if (future.wait()) - return openFileArchive(future.copy_out(),password); + return openFileArchive(future.copy(),password); return nullptr; } @@ -218,20 +219,26 @@ class NBL_API2 ISystem : public core::IReferenceCounted } m_loaders; // core::CMultiObjectCache> m_cachedArchiveFiles; - - protected: - class ICaller; + private: struct SRequestParams_NOOP { + using retval_t = void; + inline void operator()(core::StorageTrivializer* retval, ICaller* _caller) {} }; struct SRequestParams_CREATE_FILE { + using retval_t = core::smart_refctd_ptr; + void operator()(core::StorageTrivializer* retval, ICaller* _caller); + char filename[MAX_FILENAME_LENGTH] {}; IFileBase::E_CREATE_FLAGS flags; }; struct SRequestParams_READ { + using retval_t = size_t; + void operator()(core::StorageTrivializer* retval, ICaller* _caller); + ISystemFile* file; void* buffer; size_t offset; @@ -239,6 +246,9 @@ class NBL_API2 ISystem : public core::IReferenceCounted }; struct SRequestParams_WRITE { + using retval_t = size_t; + void operator()(core::StorageTrivializer* retval, ICaller* _caller); + ISystemFile* file; const void* buffer; size_t offset; @@ -254,27 +264,20 @@ class NBL_API2 ISystem : public core::IReferenceCounted > params = SRequestParams_NOOP(); }; static inline constexpr uint32_t CircularBufferSize = 256u; - class CAsyncQueue : public IAsyncQueueDispatcher + class CAsyncQueue final : public IAsyncQueueDispatcher { using base_t = IAsyncQueueDispatcher; - //friend base_t; + + core::smart_refctd_ptr m_caller; public: inline CAsyncQueue(core::smart_refctd_ptr&& caller) : base_t(base_t::start_on_construction), m_caller(std::move(caller)) {} - void process_request(SRequestType& req); + void process_request(base_t::future_base_t* _future_base, SRequestType& req); void init() {} - - private: - void handle_request(SRequestType& req, SRequestParams_NOOP& param); - void handle_request(SRequestType& req, SRequestParams_CREATE_FILE& param); - void handle_request(SRequestType& req, SRequestParams_READ& param); - void handle_request(SRequestType& req, SRequestParams_WRITE& param); - - core::smart_refctd_ptr m_caller; }; - friend class ISystemFile; + friend class ISystemFile; // TODO: do we need this friendship? CAsyncQueue m_dispatcher; }; diff --git a/include/nbl/system/ISystemFile.h b/include/nbl/system/ISystemFile.h index 85f994d25f..22836388d3 100644 --- a/include/nbl/system/ISystemFile.h +++ b/include/nbl/system/ISystemFile.h @@ -31,7 +31,7 @@ class ISystemFile : public IFile params.file = this; params.offset = offset; params.size = sizeToRead; - m_system->m_dispatcher.request(fut,params); + m_system->m_dispatcher.request(&fut,params); } inline void unmappedWrite(ISystem::future_t& fut, const void* buffer, size_t offset, size_t sizeToWrite) override final { @@ -40,12 +40,13 @@ class ISystemFile : public IFile params.file = this; params.offset = offset; params.size = sizeToWrite; - m_system->m_dispatcher.request(fut,params); + m_system->m_dispatcher.request(&fut,params); } // - friend class ISystem::CAsyncQueue; + friend struct ISystem::SRequestParams_READ; virtual size_t asyncRead(void* buffer, size_t offset, size_t sizeToRead) = 0; + friend struct ISystem::SRequestParams_WRITE; virtual size_t asyncWrite(const void* buffer, size_t offset, size_t sizeToWrite) = 0; diff --git a/src/nbl/system/ISystem.cpp b/src/nbl/system/ISystem.cpp index 788e714112..01eb3f427c 100644 --- a/src/nbl/system/ISystem.cpp +++ b/src/nbl/system/ISystem.cpp @@ -163,17 +163,22 @@ bool ISystem::copy(const system::path& from, const system::path& to) auto copyFile = [this](const system::path& from, const system::path& to) -> bool { future_t> readFileFut, writeFileFut; - createFile(readFileFut,from,core::bitflag(IFile::ECF_READ)|IFile::ECF_COHERENT); createFile(writeFileFut,to,IFile::ECF_WRITE); - auto readF = readFileFut.get(); - const IFile* readFptr = readF.get(); - auto writeF = writeFileFut.get(); - if (!readF || !readFptr->getMappedPointer() || !writeF) - return false; - - IFile::success_t bytesWritten; - writeF->write(bytesWritten,readFptr->getMappedPointer(),0,readF->getSize()); - return bool(bytesWritten); + if (auto writeF=writeFileFut.acquire()) + { + createFile(readFileFut,from,core::bitflag(IFile::ECF_READ)|IFile::ECF_COHERENT); + if (auto readF=readFileFut.acquire()) + { + auto& readFptr = *readF; + if (auto pSrc=readFptr->getMappedPointer()) + { + IFile::success_t bytesWritten; + (*writeF)->write(bytesWritten,pSrc,0,readFptr->getSize()); + return bool(bytesWritten); + } + } + } + return false; }; if (isPathReadOnly(from)) { @@ -210,6 +215,7 @@ void ISystem::createFile(future_t>& future, std::f // canonicalize if (std::filesystem::exists(filename)) filename = std::filesystem::canonical(filename); + // try builtins if (!(flags.value&IFile::ECF_WRITE) && builtin::hasPathPrefix(filename.string())) { @@ -217,7 +223,7 @@ void ISystem::createFile(future_t>& future, std::f auto file = impl_loadEmbeddedBuiltinData(filename.string(),nbl::builtin::get_resource_runtime(filename.string())); if (file) { - future.notify(core::smart_refctd_ptr(const_cast(file.get()))); + future.set_result(const_cast(file.get())); return; } #else @@ -234,7 +240,7 @@ void ISystem::createFile(future_t>& future, std::f auto file = found.archive->getFile(found.pathRelativeToArchive,accessToken); if (file) { - future.notify(std::move(file)); + future.set_result(std::move(file)); return; } } @@ -245,15 +251,15 @@ void ISystem::createFile(future_t>& future, std::f filename = std::filesystem::absolute(filename).generic_string(); if (filename.string().size()>=MAX_FILENAME_LENGTH) { - future.notify(nullptr); + future.set_result(nullptr); return; } + SRequestParams_CREATE_FILE params; strcpy(params.filename,filename.string().c_str()); params.flags = flags.value; - - m_dispatcher.request(future,params); + m_dispatcher.request(&future,params); } core::smart_refctd_ptr ISystem::openFileArchive(core::smart_refctd_ptr&& file, const std::string_view& password) @@ -306,27 +312,24 @@ ISystem::FoundArchiveFile ISystem::findFileInArchive(const system::path& absolut } -void ISystem::CAsyncQueue::process_request(SRequestType& req) +void ISystem::CAsyncQueue::process_request(base_t::future_base_t* _future_base, SRequestType& req) { - std::visit([&](auto& visitor) { - handle_request(req, visitor); + std::visit([=](auto& visitor) { + using retval_t = std::remove_reference_t::retval_t; + visitor(base_t::future_storage_cast(_future_base),m_caller.get()); }, req.params); } - -void ISystem::CAsyncQueue::handle_request(SRequestType& req, SRequestParams_NOOP& param) { - assert(false); // should never be called -} - -void ISystem::CAsyncQueue::handle_request(SRequestType& req, SRequestParams_CREATE_FILE& param) { - base_t::notify_future>(req, m_caller->createFile(param.filename, param.flags)); +void ISystem::SRequestParams_CREATE_FILE::operator()(core::StorageTrivializer* retval, ICaller* _caller) +{ + retval->construct(_caller->createFile(filename,flags)); } - -void ISystem::CAsyncQueue::handle_request(SRequestType& req, SRequestParams_READ& param) { - base_t::notify_future(req, param.file->asyncRead(param.buffer, param.offset, param.size)); +void ISystem::SRequestParams_READ::operator()(core::StorageTrivializer* retval, ICaller* _caller) +{ + retval->construct(file->asyncRead(buffer,offset,size)); } - -void ISystem::CAsyncQueue::handle_request(SRequestType& req, SRequestParams_WRITE& param) { - base_t::notify_future(req, param.file->asyncWrite(param.buffer, param.offset, param.size)); +void ISystem::SRequestParams_WRITE::operator()(core::StorageTrivializer* retval, ICaller* _caller) +{ + retval->construct(file->asyncWrite(buffer,offset,size)); } bool ISystem::ICaller::invalidateMapping(IFile* file, size_t offset, size_t size) From 2c461088e4f574dc9b885c3357fc72b5cf909277 Mon Sep 17 00:00:00 2001 From: devsh Date: Tue, 14 Mar 2023 23:26:06 +0100 Subject: [PATCH 09/24] final missing piece --- include/nbl/system/IAsyncQueueDispatcher.h | 12 ++++++------ include/nbl/system/ISystem.h | 4 +++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/include/nbl/system/IAsyncQueueDispatcher.h b/include/nbl/system/IAsyncQueueDispatcher.h index 6164c60370..5594e34e61 100644 --- a/include/nbl/system/IAsyncQueueDispatcher.h +++ b/include/nbl/system/IAsyncQueueDispatcher.h @@ -42,7 +42,7 @@ class IAsyncQueueDispatcherBase void finalize(future_base_t* fut); //! WORKER THREAD: returns when work is ready, will deadlock if the state will not eventually transition to pending - [[nodiscard]] bool wait(); + [[nodiscard]] future_base_t* wait(); //! WORKER THREAD: to call after request is done being processed, will deadlock if the request was not executed void notify(); @@ -365,14 +365,14 @@ inline void IAsyncQueueDispatcherBase::request_base_t::finalize(future_base_t* f state.exchangeNotify(STATE::PENDING,STATE::RECORDING); } -inline bool IAsyncQueueDispatcherBase::request_base_t::wait() +inline IAsyncQueueDispatcherBase::future_base_t* IAsyncQueueDispatcherBase::request_base_t::wait() { if (state.waitAbortableTransition(STATE::EXECUTING,STATE::PENDING,STATE::CANCELLED) && future->disassociate_request()) - return true; + return future; //assert(future->cancellable); future = nullptr; state.exchangeNotify(STATE::INITIAL,STATE::CANCELLED); - return false; + return nullptr; } inline void IAsyncQueueDispatcherBase::request_base_t::notify() { @@ -496,10 +496,10 @@ class IAsyncQueueDispatcher : public IThreadHandler, pro request_t& req = request_pool[r_id]; // do NOT allow cancelling or modification of the request while working on it - if (req.wait()) + if (future_base_t* future=req.wait()) { // if the request supports cancelling and got cancelled, then `wait()` function may return false - static_cast(this)->process_request(nullptr/*TODO*/,req.m_metadata,optional_internal_state...); + static_cast(this)->process_request(future,req.m_metadata,optional_internal_state...); req.notify(); } // wake the waiter up diff --git a/include/nbl/system/ISystem.h b/include/nbl/system/ISystem.h index 4ef3184e33..aad1cee207 100644 --- a/include/nbl/system/ISystem.h +++ b/include/nbl/system/ISystem.h @@ -277,7 +277,9 @@ class NBL_API2 ISystem : public core::IReferenceCounted void init() {} }; - friend class ISystemFile; // TODO: do we need this friendship? + // friendship needed to be able to know about the request types + friend class ISystemFile; + CAsyncQueue m_dispatcher; }; From 1fa40e6102978cb5f8ae4fbf4195a1809855867c Mon Sep 17 00:00:00 2001 From: devsh Date: Wed, 15 Mar 2023 00:42:29 +0100 Subject: [PATCH 10/24] rework all loaders and a few other things to use new file API now --- include/nbl/system/ISystem.h | 2 +- .../asset/interchange/CImageLoaderOpenEXR.cpp | 9 +- src/nbl/asset/interchange/CImageLoaderPNG.cpp | 6 +- .../asset/interchange/CImageWriterOpenEXR.cpp | 7 +- src/nbl/asset/interchange/CImageWriterPNG.cpp | 2 +- .../asset/interchange/CPLYMeshFileLoader.cpp | 7 +- src/nbl/asset/interchange/CPLYMeshWriter.cpp | 98 ++++---- .../asset/interchange/CSTLMeshFileLoader.cpp | 49 ++-- src/nbl/asset/interchange/CSTLMeshWriter.cpp | 232 +++++++++--------- src/nbl/asset/utils/IShaderCompiler.cpp | 4 +- src/nbl/system/CArchiveLoaderZip.cpp | 18 +- 11 files changed, 203 insertions(+), 231 deletions(-) diff --git a/include/nbl/system/ISystem.h b/include/nbl/system/ISystem.h index aad1cee207..2c2538a3a0 100644 --- a/include/nbl/system/ISystem.h +++ b/include/nbl/system/ISystem.h @@ -112,7 +112,7 @@ class NBL_API2 ISystem : public core::IReferenceCounted from - a path to the source file or directory. Must exist. Can be both readonly and mutable path. to - a path to the destination file or directory. Must be mutable path (isPathReadonly(to) must be false). */ - bool copy(const system::path& from, const system::path& to); + bool copy(const system::path& from, const system::path& to); // TODO: as a future // void createFile( diff --git a/src/nbl/asset/interchange/CImageLoaderOpenEXR.cpp b/src/nbl/asset/interchange/CImageLoaderOpenEXR.cpp index 24e2c53bc7..7f5a61c576 100644 --- a/src/nbl/asset/interchange/CImageLoaderOpenEXR.cpp +++ b/src/nbl/asset/interchange/CImageLoaderOpenEXR.cpp @@ -71,12 +71,11 @@ class nblIStream : public IMF::IStream virtual bool read(char c[/*n*/], int n) override { - system::ISystem::future_t future; - nblFile->read(future, c, fileOffset, n); - const auto bytesRead = future.get(); - fileOffset += bytesRead; + system::IFile::success_t success; + nblFile->read(success, c, fileOffset, n); + fileOffset += success.getBytesProcessed(); - return bytesRead==static_cast(n); + return bool(success); } //-------------------------------------------------------- diff --git a/src/nbl/asset/interchange/CImageLoaderPNG.cpp b/src/nbl/asset/interchange/CImageLoaderPNG.cpp index 4a9325b92f..8b9ade44f6 100644 --- a/src/nbl/asset/interchange/CImageLoaderPNG.cpp +++ b/src/nbl/asset/interchange/CImageLoaderPNG.cpp @@ -61,9 +61,9 @@ void PNGAPI user_read_data_fcn(png_structp png_pt, png_bytep data, png_size_t le system::IFile* file=(system::IFile*)png_get_io_ptr(png_pt); - system::ISystem::future_t future; - file->read(future, data, file_pos, length); - check = future.get(); + system::IFile::success_t success; + file->read(success, data, file_pos, length); + check = success.getBytesProcessed(); file_pos += length; updateFilePos(png_pt, file_pos); diff --git a/src/nbl/asset/interchange/CImageWriterOpenEXR.cpp b/src/nbl/asset/interchange/CImageWriterOpenEXR.cpp index 4bcbafa0ef..0478090c64 100644 --- a/src/nbl/asset/interchange/CImageWriterOpenEXR.cpp +++ b/src/nbl/asset/interchange/CImageWriterOpenEXR.cpp @@ -65,10 +65,9 @@ namespace nbl::asset::impl virtual void write(const char c[/*n*/], int n) override { - system::ISystem::future_t future; - nblFile->write(future, c, fileOffset, n); - const auto bytesWritten = future.get(); - fileOffset += bytesWritten; + system::IFile::success_t success; + nblFile->write(success, c, fileOffset, n); + fileOffset += success.getBytesProcessed(); } //--------------------------------------------------------- diff --git a/src/nbl/asset/interchange/CImageWriterPNG.cpp b/src/nbl/asset/interchange/CImageWriterPNG.cpp index 0c3296344b..b56862e185 100644 --- a/src/nbl/asset/interchange/CImageWriterPNG.cpp +++ b/src/nbl/asset/interchange/CImageWriterPNG.cpp @@ -57,7 +57,7 @@ void PNGAPI user_write_data_fcn(png_structp png_ptr, png_bytep data, png_size_t if (!success) png_error(png_ptr, "Write Error"); - usrData->file_pos += success.getSizeToProcess(); + usrData->file_pos += success.getBytesToProcess(); png_set_read_user_chunk_fn(png_ptr, usrData, nullptr); } #endif // _NBL_COMPILE_WITH_LIBPNG_ diff --git a/src/nbl/asset/interchange/CPLYMeshFileLoader.cpp b/src/nbl/asset/interchange/CPLYMeshFileLoader.cpp index 714e9f452f..4ad0710dbf 100644 --- a/src/nbl/asset/interchange/CPLYMeshFileLoader.cpp +++ b/src/nbl/asset/interchange/CPLYMeshFileLoader.cpp @@ -754,12 +754,11 @@ void CPLYMeshFileLoader::fillBuffer(SContext& _ctx) else { // read data from the file - system::ISystem::future_t future; - _ctx.inner.mainFile->read(future, _ctx.EndPointer, _ctx.fileOffset, PLY_INPUT_BUFFER_SIZE - length); - const auto bytesRead = future.get(); + system::IFile::success_t success; + _ctx.inner.mainFile->read(success, _ctx.EndPointer, _ctx.fileOffset, PLY_INPUT_BUFFER_SIZE - length); + const size_t bytesRead = success.getBytesProcessed(); _ctx.fileOffset += bytesRead; - // increment the end pointer by the number of bytes read _ctx.EndPointer += bytesRead; diff --git a/src/nbl/asset/interchange/CPLYMeshWriter.cpp b/src/nbl/asset/interchange/CPLYMeshWriter.cpp index 8e97f6aa19..a4cc3be9b3 100644 --- a/src/nbl/asset/interchange/CPLYMeshWriter.cpp +++ b/src/nbl/asset/interchange/CPLYMeshWriter.cpp @@ -212,10 +212,9 @@ bool CPLYMeshWriter::writeAsset(system::IFile* _file, const SAssetWriteParams& _ header += "end_header\n"; { - system::ISystem::future_t future; - file->write(future, header.c_str(), context.fileOffset, header.size()); - const auto bytesWritten = future.get(); - context.fileOffset += bytesWritten; + system::IFile::success_t success; + file->write(success, header.c_str(), context.fileOffset, header.size()); + context.fileOffset += success.getBytesProcessed(); } if (flags & asset::EWF_BINARY) @@ -279,17 +278,15 @@ void CPLYMeshWriter::writeBinary(const asset::ICPUMeshBuffer* _mbuf, size_t _vtx for (size_t i = 0u; i < _fcCount; ++i) { { - system::ISystem::future_t future; - context.writeContext.outputFile->write(future, &listSize, context.fileOffset, sizeof(listSize)); - const auto bytesWritten = future.get(); - context.fileOffset += bytesWritten; + system::IFile::success_t success; + context.writeContext.outputFile->write(success, &listSize, context.fileOffset, sizeof(listSize)); + context.fileOffset += success.getBytesProcessed(); } { - system::ISystem::future_t future; - context.writeContext.outputFile->write(future, ind, context.fileOffset, listSize * 4); - const auto bytesWritten = future.get(); - context.fileOffset += bytesWritten; + system::IFile::success_t success; + context.writeContext.outputFile->write(success, ind, context.fileOffset, listSize * 4); + context.fileOffset += success.getBytesProcessed(); } ind += listSize; @@ -301,17 +298,15 @@ void CPLYMeshWriter::writeBinary(const asset::ICPUMeshBuffer* _mbuf, size_t _vtx for (size_t i = 0u; i < _fcCount; ++i) { { - system::ISystem::future_t future; - context.writeContext.outputFile->write(future, &listSize, context.fileOffset, sizeof(listSize)); - const auto bytesWritten = future.get(); - context.fileOffset += bytesWritten; + system::IFile::success_t success; + context.writeContext.outputFile->write(success, &listSize, context.fileOffset, sizeof(listSize)); + context.fileOffset += success.getBytesProcessed(); } { - system::ISystem::future_t future; - context.writeContext.outputFile->write(future, ind, context.fileOffset, listSize * 2); - const auto bytesWritten = future.get(); - context.fileOffset += bytesWritten; + system::IFile::success_t success; + context.writeContext.outputFile->write(success, ind, context.fileOffset, listSize * 2); + context.fileOffset += success.getBytesProcessed(); } ind += listSize; @@ -379,10 +374,9 @@ void CPLYMeshWriter::writeText(const asset::ICPUMeshBuffer* _mbuf, size_t _vtxCo } { - system::ISystem::future_t future; - context.writeContext.outputFile->write(future, "\n", context.fileOffset, 1); - const auto bytesWritten = future.get(); - context.fileOffset += bytesWritten; + system::IFile::success_t success; + context.writeContext.outputFile->write(success, "\n", context.fileOffset, 1); + context.fileOffset += success.getBytesProcessed(); } } @@ -408,19 +402,17 @@ void CPLYMeshWriter::writeText(const asset::ICPUMeshBuffer* _mbuf, size_t _vtxCo for (size_t i = 0u; i < _fcCount; ++i) { { - system::ISystem::future_t future; - context.writeContext.outputFile->write(future, listSize, context.fileOffset, 2); - const auto bytesWritten = future.get(); - context.fileOffset += bytesWritten; + system::IFile::success_t success; + context.writeContext.outputFile->write(success, listSize, context.fileOffset, 2); + context.fileOffset += success.getBytesProcessed(); } writeVectorAsText(context, ind, 3); { - system::ISystem::future_t future; - context.writeContext.outputFile->write(future, "\n", context.fileOffset, 1); - const auto bytesWritten = future.get(); - context.fileOffset += bytesWritten; + system::IFile::success_t success; + context.writeContext.outputFile->write(success, "\n", context.fileOffset, 1); + context.fileOffset += success.getBytesProcessed(); } ind += 3; @@ -432,19 +424,17 @@ void CPLYMeshWriter::writeText(const asset::ICPUMeshBuffer* _mbuf, size_t _vtxCo for (size_t i = 0u; i < _fcCount; ++i) { { - system::ISystem::future_t future; - context.writeContext.outputFile->write(future, listSize, context.fileOffset, 2); - const auto bytesWritten = future.get(); - context.fileOffset += bytesWritten; + system::IFile::success_t success; + context.writeContext.outputFile->write(success, listSize, context.fileOffset, 2); + context.fileOffset += success.getBytesProcessed(); } writeVectorAsText(context, ind, 3); { - system::ISystem::future_t future; - context.writeContext.outputFile->write(future, "\n", context.fileOffset, 1); - const auto bytesWritten = future.get(); - context.fileOffset += bytesWritten; + system::IFile::success_t success; + context.writeContext.outputFile->write(success, "\n", context.fileOffset, 1); + context.fileOffset += success.getBytesProcessed(); } ind += 3; @@ -475,10 +465,9 @@ void CPLYMeshWriter::writeAttribBinary(SContext& context, asset::ICPUMeshBuffer* a[k] = ui[k]; { - system::ISystem::future_t future; - context.writeContext.outputFile->write(future, a, context.fileOffset, _cpa); - const auto bytesWritten = future.get(); - context.fileOffset += bytesWritten; + system::IFile::success_t success; + context.writeContext.outputFile->write(success, a, context.fileOffset, _cpa); + context.fileOffset += success.getBytesProcessed(); } } else if (bytesPerCh == 2u) @@ -488,19 +477,17 @@ void CPLYMeshWriter::writeAttribBinary(SContext& context, asset::ICPUMeshBuffer* a[k] = ui[k]; { - system::ISystem::future_t future; - context.writeContext.outputFile->write(future, a, context.fileOffset, 2 * _cpa); - const auto bytesWritten = future.get(); - context.fileOffset += bytesWritten; + system::IFile::success_t success; + context.writeContext.outputFile->write(success, a, context.fileOffset, 2 * _cpa); + context.fileOffset += success.getBytesProcessed(); } } else if (bytesPerCh == 4u) { { - system::ISystem::future_t future; - context.writeContext.outputFile->write(future, ui, context.fileOffset, 4 * _cpa); - const auto bytesWritten = future.get(); - context.fileOffset += bytesWritten; + system::IFile::success_t success; + context.writeContext.outputFile->write(success, ui, context.fileOffset, 4 * _cpa); + context.fileOffset += success.getBytesProcessed(); } } } @@ -511,10 +498,9 @@ void CPLYMeshWriter::writeAttribBinary(SContext& context, asset::ICPUMeshBuffer* f[0] = -f[0]; { - system::ISystem::future_t future; - context.writeContext.outputFile->write(future, f.pointer, context.fileOffset, 4 * _cpa); - const auto bytesWritten = future.get(); - context.fileOffset += bytesWritten; + system::IFile::success_t success; + context.writeContext.outputFile->write(success, f.pointer, context.fileOffset, 4 * _cpa); + context.fileOffset += success.getBytesProcessed(); } } } diff --git a/src/nbl/asset/interchange/CSTLMeshFileLoader.cpp b/src/nbl/asset/interchange/CSTLMeshFileLoader.cpp index 7f31a58c5a..c080857c63 100644 --- a/src/nbl/asset/interchange/CSTLMeshFileLoader.cpp +++ b/src/nbl/asset/interchange/CSTLMeshFileLoader.cpp @@ -354,27 +354,21 @@ void CSTLMeshFileLoader::getNextVector(SContext* context, core::vectorSIMDf& vec if (binary) { { - system::ISystem::future_t future; - context->inner.mainFile->read(future, &vec.X, context->fileOffset, 4); - - const auto bytesRead = future.get(); - context->fileOffset += bytesRead; + system::IFile::success_t success; + context->inner.mainFile->read(success, &vec.X, context->fileOffset, 4); + context->fileOffset += success.getBytesProcessed(); } { - system::ISystem::future_t future; - context->inner.mainFile->read(future, &vec.Y, context->fileOffset, 4); - - const auto bytesRead = future.get(); - context->fileOffset += bytesRead; + system::IFile::success_t success; + context->inner.mainFile->read(success, &vec.Y, context->fileOffset, 4); + context->fileOffset += success.getBytesProcessed(); } { - system::ISystem::future_t future; - context->inner.mainFile->read(future, &vec.Z, context->fileOffset, 4); - - const auto bytesRead = future.get(); - context->fileOffset += bytesRead; + system::IFile::success_t success; + context->inner.mainFile->read(success, &vec.Z, context->fileOffset, 4); + context->fileOffset += success.getBytesProcessed(); } } else @@ -401,10 +395,9 @@ const std::string& CSTLMeshFileLoader::getNextToken(SContext* context, std::stri while (context->fileOffset != context->inner.mainFile->getSize()) { - system::ISystem::future_t future; - context->inner.mainFile->read(future, &c, context->fileOffset, sizeof(c)); - const auto bytesRead = future.get(); - context->fileOffset += bytesRead; + system::IFile::success_t success; + context->inner.mainFile->read(success, &c, context->fileOffset, sizeof(c)); + context->fileOffset += success.getBytesProcessed(); // found it, so leave if (core::isspace(c)) @@ -420,15 +413,14 @@ void CSTLMeshFileLoader::goNextWord(SContext* context) const uint8_t c; while (context->fileOffset != context->inner.mainFile->getSize()) // TODO: check it { - system::ISystem::future_t future; - context->inner.mainFile->read(future, &c, context->fileOffset, sizeof(c)); - const auto bytesRead = future.get(); - context->fileOffset += bytesRead; + system::IFile::success_t success; + context->inner.mainFile->read(success, &c, context->fileOffset, sizeof(c)); + context->fileOffset += success.getBytesProcessed(); // found it, so leave if (!core::isspace(c)) { - context->fileOffset -= bytesRead; + context->fileOffset -= success.getBytesProcessed(); break; } } @@ -441,12 +433,9 @@ void CSTLMeshFileLoader::goNextLine(SContext* context) const // look for newline characters while (context->fileOffset != context->inner.mainFile->getSize()) // TODO: check it { - system::ISystem::future_t future; - context->inner.mainFile->read(future, &c, context->fileOffset, sizeof(c)); - { - const auto bytesRead = future.get(); - context->fileOffset += bytesRead; - } + system::IFile::success_t success; + context->inner.mainFile->read(success, &c, context->fileOffset, sizeof(c)); + context->fileOffset += success.getBytesProcessed(); // found it, so leave if (c == '\n' || c == '\r') diff --git a/src/nbl/asset/interchange/CSTLMeshWriter.cpp b/src/nbl/asset/interchange/CSTLMeshWriter.cpp index 6fdfdc5a9b..bf40ff8932 100644 --- a/src/nbl/asset/interchange/CSTLMeshWriter.cpp +++ b/src/nbl/asset/interchange/CSTLMeshWriter.cpp @@ -131,38 +131,38 @@ inline void writeFacesBinary(const asset::ICPUMeshBuffer* buffer, const bool& no flipVectors(); { - system::ISystem::future_t future; - file->write(future, &normal, *fileOffset, 12); - const auto bytesWritten = future.get(); - *fileOffset += bytesWritten; + system::IFile::success_t success;; + file->write(success, &normal, *fileOffset, 12); + + *fileOffset += success.getBytesProcessed(); } { - system::ISystem::future_t future; - file->write(future, &vertex1, *fileOffset, 12); - const auto bytesWritten = future.get(); - *fileOffset += bytesWritten; + system::IFile::success_t success;; + file->write(success, &vertex1, *fileOffset, 12); + + *fileOffset += success.getBytesProcessed(); } { - system::ISystem::future_t future; - file->write(future, &vertex2, *fileOffset, 12); - const auto bytesWritten = future.get(); - *fileOffset += bytesWritten; + system::IFile::success_t success;; + file->write(success, &vertex2, *fileOffset, 12); + + *fileOffset += success.getBytesProcessed(); } { - system::ISystem::future_t future; - file->write(future, &vertex3, *fileOffset, 12); - const auto bytesWritten = future.get(); - *fileOffset += bytesWritten; + system::IFile::success_t success;; + file->write(success, &vertex3, *fileOffset, 12); + + *fileOffset += success.getBytesProcessed(); } { - system::ISystem::future_t future; - file->write(future, &color, *fileOffset, 2); // saving color using non-standard VisCAM/SolidView trick - const auto bytesWritten = future.get(); - *fileOffset += bytesWritten; + system::IFile::success_t success;; + file->write(success, &color, *fileOffset, 2); // saving color using non-standard VisCAM/SolidView trick + + *fileOffset += success.getBytesProcessed(); } } } @@ -175,10 +175,10 @@ bool CSTLMeshWriter::writeMeshBinary(const asset::ICPUMesh* mesh, SContext* cont constexpr size_t HEADER_SIZE = 80u; { - system::ISystem::future_t future; - context->writeContext.outputFile->write(future, headerTxt, context->fileOffset, sizeof(headerTxt)); - const auto bytesWritten = future.get(); - context->fileOffset += bytesWritten; + system::IFile::success_t success;; + context->writeContext.outputFile->write(success, headerTxt, context->fileOffset, sizeof(headerTxt)); + + context->fileOffset += success.getBytesProcessed(); } const std::string name = context->writeContext.outputFile->getFileName().filename().replace_extension().string(); // TODO: check it @@ -186,27 +186,27 @@ bool CSTLMeshWriter::writeMeshBinary(const asset::ICPUMesh* mesh, SContext* cont if (sizeleft < 0) { - system::ISystem::future_t future; - context->writeContext.outputFile->write(future, name.c_str(), context->fileOffset, HEADER_SIZE - sizeof(headerTxt)); - const auto bytesWritten = future.get(); - context->fileOffset += bytesWritten; + system::IFile::success_t success;; + context->writeContext.outputFile->write(success, name.c_str(), context->fileOffset, HEADER_SIZE - sizeof(headerTxt)); + + context->fileOffset += success.getBytesProcessed(); } else { const char buf[80] = {0}; { - system::ISystem::future_t future; - context->writeContext.outputFile->write(future, name.c_str(), context->fileOffset, name.size()); - const auto bytesWritten = future.get(); - context->fileOffset += bytesWritten; + system::IFile::success_t success;; + context->writeContext.outputFile->write(success, name.c_str(), context->fileOffset, name.size()); + + context->fileOffset += success.getBytesProcessed(); } { - system::ISystem::future_t future; - context->writeContext.outputFile->write(future, buf, context->fileOffset, sizeleft); - const auto bytesWritten = future.get(); - context->fileOffset += bytesWritten; + system::IFile::success_t success;; + context->writeContext.outputFile->write(success, buf, context->fileOffset, sizeleft); + + context->fileOffset += success.getBytesProcessed(); } } @@ -214,10 +214,10 @@ bool CSTLMeshWriter::writeMeshBinary(const asset::ICPUMesh* mesh, SContext* cont for (auto& mb : mesh->getMeshBuffers()) facenum += mb->getIndexCount()/3; { - system::ISystem::future_t future; - context->writeContext.outputFile->write(future, &facenum, context->fileOffset, sizeof(facenum)); - const auto bytesWritten = future.get(); - context->fileOffset += bytesWritten; + system::IFile::success_t success;; + context->writeContext.outputFile->write(success, &facenum, context->fileOffset, sizeof(facenum)); + + context->fileOffset += success.getBytesProcessed(); } // write mesh buffers @@ -244,35 +244,35 @@ bool CSTLMeshWriter::writeMeshASCII(const asset::ICPUMesh* mesh, SContext* conte const char headerTxt[] = "Irrlicht-baw Engine "; { - system::ISystem::future_t future; - context->writeContext.outputFile->write(future, "solid ", context->fileOffset, 6); - const auto bytesWritten = future.get(); - context->fileOffset += bytesWritten; + system::IFile::success_t success;; + context->writeContext.outputFile->write(success, "solid ", context->fileOffset, 6); + + context->fileOffset += success.getBytesProcessed(); } { - system::ISystem::future_t future; - context->writeContext.outputFile->write(future, headerTxt, context->fileOffset, sizeof(headerTxt) - 1); - const auto bytesWritten = future.get(); - context->fileOffset += bytesWritten; + system::IFile::success_t success;; + context->writeContext.outputFile->write(success, headerTxt, context->fileOffset, sizeof(headerTxt) - 1); + + context->fileOffset += success.getBytesProcessed(); } const std::string name = context->writeContext.outputFile->getFileName().filename().replace_extension().string(); { - system::ISystem::future_t future; - context->writeContext.outputFile->write(future, name.c_str(), context->fileOffset, name.size()); - const auto bytesWritten = future.get(); - context->fileOffset += bytesWritten; + system::IFile::success_t success;; + context->writeContext.outputFile->write(success, name.c_str(), context->fileOffset, name.size()); + + context->fileOffset += success.getBytesProcessed(); } { - system::ISystem::future_t future; - context->writeContext.outputFile->write(future, "\n", context->fileOffset, 1); - const auto bytesWritten = future.get(); - context->fileOffset += bytesWritten; + system::IFile::success_t success;; + context->writeContext.outputFile->write(success, "\n", context->fileOffset, 1); + + context->fileOffset += success.getBytesProcessed(); } // write mesh buffers @@ -324,32 +324,32 @@ bool CSTLMeshWriter::writeMeshASCII(const asset::ICPUMesh* mesh, SContext* conte } { - system::ISystem::future_t future; - context->writeContext.outputFile->write(future, "\n", context->fileOffset, 1); - const auto bytesWritten = future.get(); - context->fileOffset += bytesWritten; + system::IFile::success_t success;; + context->writeContext.outputFile->write(success, "\n", context->fileOffset, 1); + + context->fileOffset += success.getBytesProcessed(); } } { - system::ISystem::future_t future; - context->writeContext.outputFile->write(future, "endsolid ", context->fileOffset, 9); - const auto bytesWritten = future.get(); - context->fileOffset += bytesWritten; + system::IFile::success_t success;; + context->writeContext.outputFile->write(success, "endsolid ", context->fileOffset, 9); + + context->fileOffset += success.getBytesProcessed(); } { - system::ISystem::future_t future; - context->writeContext.outputFile->write(future, headerTxt, context->fileOffset, sizeof(headerTxt) - 1); - const auto bytesWritten = future.get(); - context->fileOffset += bytesWritten; + system::IFile::success_t success;; + context->writeContext.outputFile->write(success, headerTxt, context->fileOffset, sizeof(headerTxt) - 1); + + context->fileOffset += success.getBytesProcessed(); } { - system::ISystem::future_t future; - context->writeContext.outputFile->write(future, name.c_str(), context->fileOffset, name.size()); - const auto bytesWritten = future.get(); - context->fileOffset += bytesWritten; + system::IFile::success_t success;; + context->writeContext.outputFile->write(success, name.c_str(), context->fileOffset, name.size()); + + context->fileOffset += success.getBytesProcessed(); } return true; @@ -386,88 +386,88 @@ void CSTLMeshWriter::writeFaceText( flipVectors(); { - system::ISystem::future_t future; - context->writeContext.outputFile->write(future, "facet normal ", context->fileOffset, 13); - const auto bytesWritten = future.get(); - context->fileOffset += bytesWritten; + system::IFile::success_t success;; + context->writeContext.outputFile->write(success, "facet normal ", context->fileOffset, 13); + + context->fileOffset += success.getBytesProcessed(); } getVectorAsStringLine(normal, tmp); { - system::ISystem::future_t future; - context->writeContext.outputFile->write(future, tmp.c_str(), context->fileOffset, tmp.size()); - const auto bytesWritten = future.get(); - context->fileOffset += bytesWritten; + system::IFile::success_t success;; + context->writeContext.outputFile->write(success, tmp.c_str(), context->fileOffset, tmp.size()); + + context->fileOffset += success.getBytesProcessed(); } { - system::ISystem::future_t future; - context->writeContext.outputFile->write(future, " outer loop\n", context->fileOffset, 13); - const auto bytesWritten = future.get(); - context->fileOffset += bytesWritten; + system::IFile::success_t success;; + context->writeContext.outputFile->write(success, " outer loop\n", context->fileOffset, 13); + + context->fileOffset += success.getBytesProcessed(); } { - system::ISystem::future_t future; - context->writeContext.outputFile->write(future, " vertex ", context->fileOffset, 11); - const auto bytesWritten = future.get(); - context->fileOffset += bytesWritten; + system::IFile::success_t success;; + context->writeContext.outputFile->write(success, " vertex ", context->fileOffset, 11); + + context->fileOffset += success.getBytesProcessed(); } getVectorAsStringLine(vertex1, tmp); { - system::ISystem::future_t future; - context->writeContext.outputFile->write(future, tmp.c_str(), context->fileOffset, tmp.size()); - const auto bytesWritten = future.get(); - context->fileOffset += bytesWritten; + system::IFile::success_t success;; + context->writeContext.outputFile->write(success, tmp.c_str(), context->fileOffset, tmp.size()); + + context->fileOffset += success.getBytesProcessed(); } { - system::ISystem::future_t future; - context->writeContext.outputFile->write(future, " vertex ", context->fileOffset, 11); - const auto bytesWritten = future.get(); - context->fileOffset += bytesWritten; + system::IFile::success_t success;; + context->writeContext.outputFile->write(success, " vertex ", context->fileOffset, 11); + + context->fileOffset += success.getBytesProcessed(); } getVectorAsStringLine(vertex2, tmp); { - system::ISystem::future_t future; - context->writeContext.outputFile->write(future, tmp.c_str(), context->fileOffset, tmp.size()); - const auto bytesWritten = future.get(); - context->fileOffset += bytesWritten; + system::IFile::success_t success;; + context->writeContext.outputFile->write(success, tmp.c_str(), context->fileOffset, tmp.size()); + + context->fileOffset += success.getBytesProcessed(); } { - system::ISystem::future_t future; - context->writeContext.outputFile->write(future, " vertex ", context->fileOffset, 11); - const auto bytesWritten = future.get(); - context->fileOffset += bytesWritten; + system::IFile::success_t success;; + context->writeContext.outputFile->write(success, " vertex ", context->fileOffset, 11); + + context->fileOffset += success.getBytesProcessed(); } getVectorAsStringLine(vertex3, tmp); { - system::ISystem::future_t future; - context->writeContext.outputFile->write(future, tmp.c_str(), context->fileOffset, tmp.size()); - const auto bytesWritten = future.get(); - context->fileOffset += bytesWritten; + system::IFile::success_t success;; + context->writeContext.outputFile->write(success, tmp.c_str(), context->fileOffset, tmp.size()); + + context->fileOffset += success.getBytesProcessed(); } { - system::ISystem::future_t future; - context->writeContext.outputFile->write(future, " endloop\n", context->fileOffset, 10); - const auto bytesWritten = future.get(); - context->fileOffset += bytesWritten; + system::IFile::success_t success;; + context->writeContext.outputFile->write(success, " endloop\n", context->fileOffset, 10); + + context->fileOffset += success.getBytesProcessed(); } { - system::ISystem::future_t future; - context->writeContext.outputFile->write(future, "endfacet\n", context->fileOffset, 9); - const auto bytesWritten = future.get(); - context->fileOffset += bytesWritten; + system::IFile::success_t success;; + context->writeContext.outputFile->write(success, "endfacet\n", context->fileOffset, 9); + + context->fileOffset += success.getBytesProcessed(); } } diff --git a/src/nbl/asset/utils/IShaderCompiler.cpp b/src/nbl/asset/utils/IShaderCompiler.cpp index 65f14c9a52..fc134814d4 100644 --- a/src/nbl/asset/utils/IShaderCompiler.cpp +++ b/src/nbl/asset/utils/IShaderCompiler.cpp @@ -140,9 +140,9 @@ std::string IShaderCompiler::CFileSystemIncludeLoader::getInclude(const system:: { system::ISystem::future_t> future; m_system->createFile(future, path.c_str(), system::IFile::ECF_READ); - f = future.get(); - if (!f) + if (!future.wait()) return {}; + future.acquire().move_into(f); } const size_t size = f->getSize(); diff --git a/src/nbl/system/CArchiveLoaderZip.cpp b/src/nbl/system/CArchiveLoaderZip.cpp index c3c06881b9..60f072cb69 100644 --- a/src/nbl/system/CArchiveLoaderZip.cpp +++ b/src/nbl/system/CArchiveLoaderZip.cpp @@ -139,7 +139,7 @@ core::smart_refctd_ptr CArchiveLoaderZip::createArchive_impl(core: file->read(success,&c,offset,sizeof(c)); if (!success) return false; - offset += success.getSizeToProcess(); + offset += success.getBytesToProcess(); charCallback(c); } // if string is not null terminated, something went wrong reading the file @@ -157,7 +157,7 @@ core::smart_refctd_ptr CArchiveLoaderZip::createArchive_impl(core: file->read(success,&gzipHeader,0ull,sizeof(gzipHeader)); if (!success) return nullptr; - offset += success.getSizeToProcess(); + offset += success.getBytesToProcess(); } //! The gzip file format seems to think that there can be multiple files in a gzip file @@ -174,7 +174,7 @@ core::smart_refctd_ptr CArchiveLoaderZip::createArchive_impl(core: file->read(success,&dataLen,offset,sizeof(dataLen)); if (!success) return nullptr; - offset += success.getSizeToProcess(); + offset += success.getBytesToProcess(); // skip the extra data offset += dataLen; } @@ -211,7 +211,7 @@ core::smart_refctd_ptr CArchiveLoaderZip::createArchive_impl(core: file->read(success,&header.DataDescriptor.CRC32,offset,sizeof(header.DataDescriptor.CRC32)); if (!success) return nullptr; - offset += success.getSizeToProcess(); + offset += success.getBytesToProcess(); } // read uncompressed size { @@ -219,7 +219,7 @@ core::smart_refctd_ptr CArchiveLoaderZip::createArchive_impl(core: file->read(success,&header.DataDescriptor.UncompressedSize,offset,sizeof(header.DataDescriptor.UncompressedSize)); if (!success) return nullptr; - offset += success.getSizeToProcess(); + offset += success.getBytesToProcess(); } // @@ -235,7 +235,7 @@ core::smart_refctd_ptr CArchiveLoaderZip::createArchive_impl(core: file->read(success,&zipHeader,offset,sizeof(zipHeader)); if (!success) break; - offset += success.getSizeToProcess(); + offset += success.getBytesToProcess(); } if (zipHeader.Sig!=0x04034b50u) @@ -247,7 +247,7 @@ core::smart_refctd_ptr CArchiveLoaderZip::createArchive_impl(core: file->read(success,filename.data(),offset,zipHeader.FilenameLength); if (!success) break; - offset += success.getSizeToProcess(); + offset += success.getBytesToProcess(); } // AES encryption @@ -265,7 +265,7 @@ core::smart_refctd_ptr CArchiveLoaderZip::createArchive_impl(core: file->read(success,&extraHeader,localOffset,sizeof(extraHeader)); if (!success) break; - localOffset += success.getSizeToProcess(); + localOffset += success.getBytesToProcess(); if (localOffset>offset) break; } @@ -278,7 +278,7 @@ core::smart_refctd_ptr CArchiveLoaderZip::createArchive_impl(core: file->read(success,&data,localOffset,sizeof(data)); if (!success) break; - localOffset += success.getSizeToProcess(); + localOffset += success.getBytesToProcess(); if (localOffset>offset) break; } From 8097e772e1fb5ffbe4dec725a2432003ef33f5b4 Mon Sep 17 00:00:00 2001 From: devsh Date: Fri, 17 Mar 2023 19:54:08 +0100 Subject: [PATCH 11/24] refactor a few windowing things --- include/nbl/ui/IWindow.h | 38 ++-- include/nbl/ui/IWindowAndroid.h | 15 +- include/nbl/ui/IWindowManager.h | 38 ++-- include/nbl/ui/IWindowManagerWin32.h | 18 ++ include/nbl/ui/IWindowWin32.h | 60 +----- include/nbl/ui/KeyCodes.h | 293 ++++++++++++++------------- include/nbl/ui/SInputEvent.h | 8 +- 7 files changed, 223 insertions(+), 247 deletions(-) create mode 100644 include/nbl/ui/IWindowManagerWin32.h diff --git a/include/nbl/ui/IWindow.h b/include/nbl/ui/IWindow.h index 0dc17db84a..6a25dc06d9 100644 --- a/include/nbl/ui/IWindow.h +++ b/include/nbl/ui/IWindow.h @@ -41,6 +41,7 @@ class IWindow : public core::IReferenceCounted class IEventCallback : public core::IReferenceCounted { public: + // TODO: rethink our boolean returns [[nodiscard]] inline bool onWindowShown(IWindow* w) { auto canShow = onWindowShown_impl(); @@ -107,6 +108,7 @@ class IWindow : public core::IReferenceCounted { return onWindowClosed_impl(); } + inline void onGainedMouseFocus(IWindow* w) { onGainedMouseFocus_impl(); @@ -160,18 +162,9 @@ class IWindow : public core::IReferenceCounted NBL_API2 virtual void onKeyboardConnected_impl(core::smart_refctd_ptr&& kbch) {} NBL_API2 virtual void onKeyboardDisconnected_impl(IKeyboardEventChannel* mch) {} }; - struct SCreationParams - { - //IWindow(core::smart_refctd_ptr&& _cb, core::smart_refctd_ptr&& _sys, uint32_t _w = 0u, uint32_t _h = 0u, E_CREATE_FLAGS _flags = static_cast(0)) : - core::smart_refctd_ptr callback; - core::smart_refctd_ptr system; - int32_t x, y; - uint32_t width = 0u, height = 0u; - E_CREATE_FLAGS flags = static_cast(0); - uint32_t eventChannelCapacityLog2[IInputEventChannel::ET_COUNT]; - std::string windowCaption; - }; - friend struct IEventCallback; + + friend class IEventCallback; + inline void setEventCallback(core::smart_refctd_ptr&& evCb) { m_cb = std::move(evCb); } inline bool isFullscreen() { return (m_flags.value & ECF_FULLSCREEN); } inline bool isHidden() { return (m_flags.value & ECF_HIDDEN); } @@ -193,28 +186,33 @@ class IWindow : public core::IReferenceCounted NBL_API2 virtual IClipboardManager* getClipboardManager() = 0; NBL_API2 virtual ICursorControl* getCursorControl() = 0; - NBL_API2 virtual IWindowManager* getManager() = 0; + NBL_API2 virtual IWindowManager* getManager() const = 0; inline IEventCallback* getEventCallback() const { return m_cb.get(); } NBL_API2 virtual void setCaption(const std::string_view& caption) = 0; + + struct SCreationParams + { + core::smart_refctd_ptr callback; + int32_t x, y; + uint32_t width = 0u, height = 0u; + E_CREATE_FLAGS flags = E_CREATE_FLAGS::ECF_NONE; + uint32_t eventChannelCapacityLog2[IInputEventChannel::ET_COUNT]; + std::string windowCaption; + }; protected: // TODO need to update constructors of all derived CWindow* classes inline IWindow(SCreationParams&& params) : - m_cb(std::move(params.callback)), m_sys(std::move(params.system)), m_width(params.width), m_height(params.height), m_x(params.x), m_y(params.y), m_flags(params.flags) + m_cb(std::move(params.callback)), m_width(params.width), m_height(params.height), m_x(params.x), m_y(params.y), m_flags(params.flags) { - } - - NBL_API2 virtual ~IWindow() = default; + inline virtual ~IWindow() = default; core::smart_refctd_ptr m_cb; - core::smart_refctd_ptr m_sys; uint32_t m_width = 0u, m_height = 0u; int32_t m_x, m_y; // gonna add it here until further instructions XD core::bitflag m_flags = static_cast(0u); - public: - inline void setEventCallback(core::smart_refctd_ptr&& evCb) { m_cb = std::move(evCb); } }; } diff --git a/include/nbl/ui/IWindowAndroid.h b/include/nbl/ui/IWindowAndroid.h index 9cf7e10e2e..bb679875af 100755 --- a/include/nbl/ui/IWindowAndroid.h +++ b/include/nbl/ui/IWindowAndroid.h @@ -4,28 +4,21 @@ #include "nbl/ui/IWindow.h" #ifdef _NBL_PLATFORM_ANDROID_ - -#include - namespace nbl::ui { class NBL_API2 IWindowAndroid : public IWindow { - protected: - virtual ~IWindowAndroid() = default; - inline IWindowAndroid(SCreationParams&& params) : IWindow(std::move(params)) {} - public: - using IWindow::IWindow; - using native_handle_t = struct ANativeWindow*; - virtual const native_handle_t& getNativeHandle() const = 0; + + protected: + virtual ~IWindowAndroid() = default; + inline IWindowAndroid(SCreationParams&& params) : IWindow(std::move(params)) {} }; } - #endif // _NBL_PLATFORM_ANDROID_ #endif diff --git a/include/nbl/ui/IWindowManager.h b/include/nbl/ui/IWindowManager.h index 6c77ef98b3..994c375537 100644 --- a/include/nbl/ui/IWindowManager.h +++ b/include/nbl/ui/IWindowManager.h @@ -1,32 +1,26 @@ -#ifndef _NBL_UI_I_WINDOWMANAGER_ -#define _NBL_UI_I_WINDOWMANAGER_ +#ifndef _NBL_UI_I_WINDOWMANAGER_INCLUDED_ +#define _NBL_UI_I_WINDOWMANAGER_INCLUDED_ -#include -#include "IWindow.h" +#include "nbl/ui/IWindow.h" namespace nbl::ui { -struct SDisplayInfo -{ - int32_t x; - int32_t y; - uint32_t resX; - uint32_t resY; - std::string name; // this one is really more of a placeholder right now -}; - class NBL_API2 IWindowManager : public core::IReferenceCounted { public: virtual core::smart_refctd_ptr createWindow(IWindow::SCreationParams&& creationParams) = 0; - virtual SDisplayInfo getPrimaryDisplayInfo() const = 0; - virtual bool setWindowSize_impl(IWindow* window, uint32_t width, uint32_t height) = 0; - virtual bool setWindowPosition_impl(IWindow* window, int32_t x, int32_t y) = 0; - virtual bool setWindowRotation_impl(IWindow* window, bool landscape) = 0; - virtual bool setWindowVisible_impl(IWindow* window, bool visible) = 0; - virtual bool setWindowMaximized_impl(IWindow* window, bool maximized) = 0; + //! + struct SDisplayInfo + { + int32_t x; + int32_t y; + uint32_t resX; + uint32_t resY; + std::string name; // this one is really more of a placeholder right now + }; + virtual SDisplayInfo getPrimaryDisplayInfo() const = 0; inline bool setWindowSize(IWindow* window, const uint32_t width, const uint32_t height) { @@ -99,6 +93,12 @@ class NBL_API2 IWindowManager : public core::IReferenceCounted protected: virtual ~IWindowManager() = default; + + virtual bool setWindowSize_impl(IWindow* window, uint32_t width, uint32_t height) = 0; + virtual bool setWindowPosition_impl(IWindow* window, int32_t x, int32_t y) = 0; + virtual bool setWindowRotation_impl(IWindow* window, bool landscape) = 0; + virtual bool setWindowVisible_impl(IWindow* window, bool visible) = 0; + virtual bool setWindowMaximized_impl(IWindow* window, bool maximized) = 0; }; } diff --git a/include/nbl/ui/IWindowManagerWin32.h b/include/nbl/ui/IWindowManagerWin32.h new file mode 100644 index 0000000000..5bf982999b --- /dev/null +++ b/include/nbl/ui/IWindowManagerWin32.h @@ -0,0 +1,18 @@ +#ifndef _NBL_UI_I_WINDOWMANAGER_WIN32_INCLUDED_ +#define _NBL_UI_I_WINDOWMANAGER_WIN32_INCLUDED_ + +#include "nbl/ui/IWindowManager.h" + +#ifdef _NBL_PLATFORM_WINDOWS_ +namespace nbl::ui +{ + +class IWindowManagerWin32 : public IWindowManager +{ + public: + NBL_API2 static core::smart_refctd_ptr create(); +}; + +} +#endif +#endif \ No newline at end of file diff --git a/include/nbl/ui/IWindowWin32.h b/include/nbl/ui/IWindowWin32.h index 4f53d49f76..f49313a419 100644 --- a/include/nbl/ui/IWindowWin32.h +++ b/include/nbl/ui/IWindowWin32.h @@ -1,64 +1,24 @@ -#ifndef __NBL_I_WINDOW_WIN32_H_INCLUDED__ -#define __NBL_I_WINDOW_WIN32_H_INCLUDED__ - -#include "nbl/system/DefaultFuncPtrLoader.h" - -#include "nbl/ui/IWindow.h" +#ifndef _NBL_I_WINDOW_WIN32_H_INCLUDED_ +#define _NBL_I_WINDOW_WIN32_H_INCLUDED_ +#include "nbl/ui/IWindowManagerWin32.h" #ifdef _NBL_PLATFORM_WINDOWS_ +// forward declare HWND +struct HWND__; namespace nbl::ui { class NBL_API2 IWindowWin32 : public IWindow { - protected: - virtual ~IWindowWin32() = default; - inline IWindowWin32(SCreationParams&& params) : IWindow(std::move(params)) {} - public: - using IWindow::IWindow; - - using native_handle_t = HWND; - + using native_handle_t = HWND__*; virtual const native_handle_t& getNativeHandle() const = 0; - - static DWORD getWindowStyle(IWindow::E_CREATE_FLAGS flags) - { - DWORD style = WS_POPUP; - - if ((flags & IWindow::ECF_FULLSCREEN) == 0) - { - if ((flags & IWindow::ECF_BORDERLESS) == 0) - { - style |= WS_BORDER; - style |= (WS_SYSMENU | WS_CAPTION); - } - // ? not sure about those below - style |= WS_CLIPCHILDREN; - style |= WS_CLIPSIBLINGS; - } - if (flags & IWindow::ECF_MINIMIZED) - { - style |= WS_MINIMIZE; - } - if (flags & IWindow::ECF_MAXIMIZED) - { - style |= WS_MAXIMIZE; - } - if (flags & IWindow::ECF_ALWAYS_ON_TOP) - { - style |= WS_EX_TOPMOST; - } - if ((flags & IWindow::ECF_HIDDEN) == 0) - { - style |= WS_VISIBLE; - } - style |= WS_OVERLAPPEDWINDOW; - - return style; - } + + protected: + inline IWindowWin32(SCreationParams&& params) : IWindow(std::move(params)) {} + virtual ~IWindowWin32() = default; }; } diff --git a/include/nbl/ui/KeyCodes.h b/include/nbl/ui/KeyCodes.h index b27e705708..98490d0d6f 100644 --- a/include/nbl/ui/KeyCodes.h +++ b/include/nbl/ui/KeyCodes.h @@ -1,149 +1,151 @@ -#ifndef _MNL_UI_KEYCODES_H_INCLUDED_ -#define _MNL_UI_KEYCODES_H_INCLUDED_ +#ifndef _NBL_UI_KEYCODES_H_INCLUDED_ +#define _NBL_UI_KEYCODES_H_INCLUDED_ + namespace nbl::ui { - enum E_KEY_CODE : uint8_t - { - EKC_NONE = 0, - EKC_BACKSPACE, - EKC_TAB, - EKC_CLEAR, - EKC_ENTER, - EKC_LEFT_SHIFT, - EKC_RIGHT_SHIFT, - EKC_LEFT_CONTROL, - EKC_RIGHT_CONTROL, - EKC_LEFT_ALT, - EKC_RIGHT_ALT, - EKC_PAUSE, - EKC_CAPS_LOCK, - EKC_ESCAPE, - EKC_SPACE, - EKC_PAGE_UP, - EKC_PAGE_DOWN, - EKC_END, - EKC_HOME, - EKC_LEFT_ARROW, - EKC_RIGHT_ARROW, - EKC_DOWN_ARROW, - EKC_UP_ARROW, - EKC_SELECT, - EKC_PRINT, - EKC_EXECUTE, - EKC_PRINT_SCREEN, - EKC_INSERT, - EKC_DELETE, - EKC_HELP, + +enum E_KEY_CODE : uint8_t +{ + EKC_NONE = 0, + EKC_BACKSPACE, + EKC_TAB, + EKC_CLEAR, + EKC_ENTER, + EKC_LEFT_SHIFT, + EKC_RIGHT_SHIFT, + EKC_LEFT_CONTROL, + EKC_RIGHT_CONTROL, + EKC_LEFT_ALT, + EKC_RIGHT_ALT, + EKC_PAUSE, + EKC_CAPS_LOCK, + EKC_ESCAPE, + EKC_SPACE, + EKC_PAGE_UP, + EKC_PAGE_DOWN, + EKC_END, + EKC_HOME, + EKC_LEFT_ARROW, + EKC_RIGHT_ARROW, + EKC_DOWN_ARROW, + EKC_UP_ARROW, + EKC_SELECT, + EKC_PRINT, + EKC_EXECUTE, + EKC_PRINT_SCREEN, + EKC_INSERT, + EKC_DELETE, + EKC_HELP, - EKC_LEFT_WIN, - EKC_RIGHT_WIN, - EKC_APPS, + EKC_LEFT_WIN, + EKC_RIGHT_WIN, + EKC_APPS, - EKC_COMMA, - EKC_PERIOD, - EKC_SEMICOLON, - EKC_OPEN_BRACKET, - EKC_CLOSE_BRACKET, - EKC_BACKSLASH, - EKC_APOSTROPHE, + EKC_COMMA, + EKC_PERIOD, + EKC_SEMICOLON, + EKC_OPEN_BRACKET, + EKC_CLOSE_BRACKET, + EKC_BACKSLASH, + EKC_APOSTROPHE, - EKC_ADD = '+', - EKC_SUBTRACT = '-', - EKC_MULTIPLY = '*', - EKC_DIVIDE = '/', + EKC_ADD = '+', + EKC_SUBTRACT = '-', + EKC_MULTIPLY = '*', + EKC_DIVIDE = '/', - EKC_0 = '0', - EKC_1, - EKC_2, - EKC_3, - EKC_4, - EKC_5, - EKC_6, - EKC_7, - EKC_8, - EKC_9, + EKC_0 = '0', + EKC_1, + EKC_2, + EKC_3, + EKC_4, + EKC_5, + EKC_6, + EKC_7, + EKC_8, + EKC_9, - EKC_A = 'A', - EKC_B, - EKC_C, - EKC_D, - EKC_E, - EKC_F, - EKC_G, - EKC_H, - EKC_I, - EKC_J, - EKC_K, - EKC_L, - EKC_M, - EKC_N, - EKC_O, - EKC_P, - EKC_Q, - EKC_R, - EKC_S, - EKC_T, - EKC_U, - EKC_V, - EKC_W, - EKC_X, - EKC_Y, - EKC_Z, + EKC_A = 'A', + EKC_B, + EKC_C, + EKC_D, + EKC_E, + EKC_F, + EKC_G, + EKC_H, + EKC_I, + EKC_J, + EKC_K, + EKC_L, + EKC_M, + EKC_N, + EKC_O, + EKC_P, + EKC_Q, + EKC_R, + EKC_S, + EKC_T, + EKC_U, + EKC_V, + EKC_W, + EKC_X, + EKC_Y, + EKC_Z, - EKC_NUMPAD_0, - EKC_NUMPAD_1, - EKC_NUMPAD_2, - EKC_NUMPAD_3, - EKC_NUMPAD_4, - EKC_NUMPAD_5, - EKC_NUMPAD_6, - EKC_NUMPAD_7, - EKC_NUMPAD_8, - EKC_NUMPAD_9, + EKC_NUMPAD_0, + EKC_NUMPAD_1, + EKC_NUMPAD_2, + EKC_NUMPAD_3, + EKC_NUMPAD_4, + EKC_NUMPAD_5, + EKC_NUMPAD_6, + EKC_NUMPAD_7, + EKC_NUMPAD_8, + EKC_NUMPAD_9, - EKC_F1, - EKC_F2, - EKC_F3, - EKC_F4, - EKC_F5, - EKC_F6, - EKC_F7, - EKC_F8, - EKC_F9, - EKC_F10, - EKC_F11, - EKC_F12, - EKC_F13, - EKC_F14, - EKC_F15, - EKC_F16, - EKC_F17, - EKC_F18, - EKC_F19, - EKC_F20, - EKC_F21, - EKC_F22, - EKC_F23, - EKC_F24, + EKC_F1, + EKC_F2, + EKC_F3, + EKC_F4, + EKC_F5, + EKC_F6, + EKC_F7, + EKC_F8, + EKC_F9, + EKC_F10, + EKC_F11, + EKC_F12, + EKC_F13, + EKC_F14, + EKC_F15, + EKC_F16, + EKC_F17, + EKC_F18, + EKC_F19, + EKC_F20, + EKC_F21, + EKC_F22, + EKC_F23, + EKC_F24, - EKC_NUM_LOCK, - EKC_SCROLL_LOCK, + EKC_NUM_LOCK, + EKC_SCROLL_LOCK, - EKC_VOLUME_MUTE, - EKC_VOLUME_UP, - EKC_VOLUME_DOWN, + EKC_VOLUME_MUTE, + EKC_VOLUME_UP, + EKC_VOLUME_DOWN, - EKC_COUNT, - }; + EKC_COUNT, +}; - inline char keyCodeToChar(E_KEY_CODE code, bool shiftPressed) +inline char keyCodeToChar(E_KEY_CODE code, bool shiftPressed) +{ + char result = 0; + if (!shiftPressed) { - char result = 0; - if (!shiftPressed) + switch (code) { - switch (code) - { case EKC_0: [[fallthrough]]; case EKC_NUMPAD_0: result = '0'; break; case EKC_1: [[fallthrough]]; @@ -205,12 +207,12 @@ namespace nbl::ui case EKC_CLOSE_BRACKET: result = ']'; break; case EKC_BACKSLASH: result = '\\'; break; case EKC_APOSTROPHE: result = '\''; break; - } } - else + } + else + { + switch (code) { - switch (code) - { case EKC_0: result = ')'; break; case EKC_1: result = '!'; break; case EKC_2: result = '@'; break; @@ -259,19 +261,20 @@ namespace nbl::ui case EKC_CLOSE_BRACKET: result = '}'; break; case EKC_BACKSLASH: result = '|'; break; case EKC_APOSTROPHE: result = '\"'; break; - } } - return result; } + return result; +} - enum E_MOUSE_BUTTON : uint8_t - { - EMB_LEFT_BUTTON, - EMB_RIGHT_BUTTON, - EMB_MIDDLE_BUTTON, - EMB_BUTTON_4, - EMB_BUTTON_5, - EMB_COUNT, - }; +enum E_MOUSE_BUTTON : uint8_t +{ + EMB_LEFT_BUTTON, + EMB_RIGHT_BUTTON, + EMB_MIDDLE_BUTTON, + EMB_BUTTON_4, + EMB_BUTTON_5, + EMB_COUNT, }; + +} #endif diff --git a/include/nbl/ui/SInputEvent.h b/include/nbl/ui/SInputEvent.h index 99e77cdc3f..d791d08e53 100644 --- a/include/nbl/ui/SInputEvent.h +++ b/include/nbl/ui/SInputEvent.h @@ -1,11 +1,15 @@ -#ifndef __NBL_UI_I_INPUT_EVENT_H_INCLUDED__ -#define __NBL_UI_I_INPUT_EVENT_H_INCLUDED__ +#ifndef _NBL_UI_I_INPUT_EVENT_H_INCLUDED_ +#define _NBL_UI_I_INPUT_EVENT_H_INCLUDED_ + #include namespace nbl::ui { + + class IWindow; + struct SEventBase { std::chrono::microseconds timeStamp; From 8902116f5e0bb1779550f87e3e5ac6a80c900c50 Mon Sep 17 00:00:00 2001 From: devsh Date: Fri, 17 Mar 2023 19:55:26 +0100 Subject: [PATCH 12/24] boot the win32 implementations to `src/nbl/ui` --- include/nbl/ui/CClipboardManagerWin32.h | 29 -- include/nbl/ui/CCursorControlWin32.h | 29 -- include/nbl/ui/CWindowAndroid.h | 90 ----- include/nbl/ui/CWindowManagerWin32.h | 478 ------------------------ include/nbl/ui/CWindowWin32.h | 145 ------- include/nbl/ui/IClipboardManager.h | 7 +- include/nbl/ui/ICursorControl.h | 4 +- 7 files changed, 5 insertions(+), 777 deletions(-) delete mode 100644 include/nbl/ui/CClipboardManagerWin32.h delete mode 100644 include/nbl/ui/CCursorControlWin32.h delete mode 100755 include/nbl/ui/CWindowAndroid.h delete mode 100644 include/nbl/ui/CWindowManagerWin32.h delete mode 100644 include/nbl/ui/CWindowWin32.h diff --git a/include/nbl/ui/CClipboardManagerWin32.h b/include/nbl/ui/CClipboardManagerWin32.h deleted file mode 100644 index e56714d4ec..0000000000 --- a/include/nbl/ui/CClipboardManagerWin32.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef _NBL_UI_C_CLIPBOARD_MANAGER_WIN32_INCLUDED_ -#define _NBL_UI_C_CLIPBOARD_MANAGER_WIN32_INCLUDED_ - -#include "nbl/ui/IClipboardManager.h" - -#ifdef _NBL_PLATFORM_WINDOWS_ -namespace nbl::ui -{ - -class NBL_API2 CClipboardManagerWin32 final : public IClipboardManager -{ - using base_t = IClipboardManager; - public: - inline CClipboardManagerWin32() = default; - - virtual std::string getClipboardText() override; - virtual bool setClipboardText(const std::string_view& data) override; - - //virtual core::smart_refctd_ptr getClipboardImage() override; - //virtual bool setClipboardImage(asset::ICPUImage* image, asset::ICPUImage::SImageCopy data) override; - private: - HGLOBAL* CPUImageToClipboardImage(asset::ICPUImage* image); -}; - -} - - -#endif -#endif \ No newline at end of file diff --git a/include/nbl/ui/CCursorControlWin32.h b/include/nbl/ui/CCursorControlWin32.h deleted file mode 100644 index 861198550b..0000000000 --- a/include/nbl/ui/CCursorControlWin32.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef __NBL_SYSTEM_C_CURSOR_CONTROL_WIN32_H_INCLUDED__ -#define __NBL_SYSTEM_C_CURSOR_CONTROL_WIN32_H_INCLUDED__ - -#include "nbl/ui/ICursorControl.h" -#include "nbl/ui/CWindowManagerWin32.h" - -#ifdef _NBL_PLATFORM_WINDOWS_ -namespace nbl::ui -{ -class NBL_API2 CCursorControlWin32 final : public ICursorControl -{ - core::smart_refctd_ptr m_windowManager; - - public: - inline CCursorControlWin32(core::smart_refctd_ptr&& wmgr) : m_windowManager(std::move(wmgr)) {} - - void setVisible(bool visible) override; - bool isVisible() const override; - - void setPosition(SPosition pos) override; - void setRelativePosition(IWindow* window, SRelativePosition pos) override; - - SPosition getPosition() override; - SRelativePosition getRelativePosition(IWindow* window) override; -}; -} -#endif - -#endif \ No newline at end of file diff --git a/include/nbl/ui/CWindowAndroid.h b/include/nbl/ui/CWindowAndroid.h deleted file mode 100755 index 37f180f189..0000000000 --- a/include/nbl/ui/CWindowAndroid.h +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef _NBL_C_WINDOW_ANDROID_H_INCLUDED_ -#define _NBL_C_WINDOW_ANDROID_H_INCLUDED_ - -#include "nbl/ui/IWindowAndroid.h" - -#ifdef _NBL_PLATFORM_ANDROID_ -#include -#include -#include - -namespace nbl::ui -{ - -class CWindowAndroid : public IWindowAndroid -{ - public: - constexpr static inline uint32_t CIRCULAR_BUFFER_CAPACITY = 256; - explicit inline CWindowAndroid(SCreationParams&& params, native_handle_t anw) : m_native(anw), IWindowAndroid(std::move(params)) - { - m_width = ANativeWindow_getWidth(anw); - m_height = ANativeWindow_getHeight(anw); - } - - virtual inline IClipboardManager* getClipboardManager() override { return nullptr; } - virtual inline ICursorControl* getCursorControl() override { return nullptr; } - - inline const native_handle_t& getNativeHandle() const override { return m_native; } - - inline void setCaption(const std::string_view& caption) override {} - virtual IWindowManager* getManager() override - { - /* - TODO: this class should have m_windowManager member... - */ - - assert(false); - return nullptr; - } - - // WHY THE FUCK ARE THESE PUBLIC? - core::map> m_mouseEventChannels; - core::map> m_keyboardEventChannels; - - inline bool hasMouseEventChannel(uint32_t deviceId) - { - return m_mouseEventChannels.find(deviceId) != m_mouseEventChannels.end(); - } - inline bool hasKeyboardEventChannel(uint32_t deviceId) - { - return m_keyboardEventChannels.find(deviceId) != m_keyboardEventChannels.end(); - } - inline bool addMouseEventChannel(uint32_t deviceId, const core::smart_refctd_ptr& channel) - { - if (m_mouseEventChannels.find(deviceId) == m_mouseEventChannels.end()) - { - m_mouseEventChannels.emplace(deviceId, channel); - return true; - } - return false; - } - inline bool addKeyboardEventChannel(uint32_t deviceId, const core::smart_refctd_ptr& channel) - { - if (m_keyboardEventChannels.find(deviceId) == m_keyboardEventChannels.end()) - { - m_keyboardEventChannels.emplace(deviceId, channel); - return true; - } - return false; - } - inline IMouseEventChannel* getMouseEventChannel(uint32_t deviceId) - { - auto ch = m_mouseEventChannels.find(deviceId); - return m_mouseEventChannels.find(deviceId)->second.get(); - } - - inline IKeyboardEventChannel* getKeyboardEventChannel(uint32_t deviceId) - { - auto ch = m_keyboardEventChannels.find(deviceId); - return m_keyboardEventChannels.find(deviceId)->second.get(); - } - - private: - native_handle_t m_native; -}; - -} - -#endif //_NBL_PLATFORM_ANDROID_ - -#endif diff --git a/include/nbl/ui/CWindowManagerWin32.h b/include/nbl/ui/CWindowManagerWin32.h deleted file mode 100644 index ebd9d8c8c0..0000000000 --- a/include/nbl/ui/CWindowManagerWin32.h +++ /dev/null @@ -1,478 +0,0 @@ -#ifndef _NBL_UI_C_WINDOWMANAGER_WIN32_ -#define _NBL_UI_C_WINDOWMANAGER_WIN32_ - -#include "nbl/ui/IWindowManager.h" - -#include -#include - -#include "nbl/ui/CWindowWin32.h" - -#ifdef _NBL_PLATFORM_WINDOWS_ -#include -#include - -//#include -#include - -namespace nbl::ui -{ - -class CWindowManagerWin32 : public IWindowManager -{ - public: - inline CWindowManagerWin32() = default; - inline ~CWindowManagerWin32() {}; - - inline core::smart_refctd_ptr createWindow(IWindow::SCreationParams&& creationParams) override final - { - CWindowWin32::native_handle_t handle = createNativeWindow( - creationParams.x, - creationParams.y, - creationParams.width, - creationParams.height, - creationParams.flags, - creationParams.windowCaption - ); - - if (handle == nullptr) - return nullptr; - - return core::make_smart_refctd_ptr(core::smart_refctd_ptr(this), std::move(creationParams), handle); - } - inline void destroyWindow(IWindow* wnd) override final - { - destroyNativeWindow(static_cast(wnd)->getNativeHandle()); - } - inline void setCursorVisibility(bool visible) - { - m_windowThreadManager.setCursorVisibility(visible); - } - inline SDisplayInfo getPrimaryDisplayInfo() const override final - { - RECT size; - BOOL res_ok = SystemParametersInfo(SPI_GETWORKAREA, 0, &size, 0); - SDisplayInfo info {}; - info.resX = size.right - size.left; - info.resY = size.bottom - size.top; - info.x = size.left; // When would this be not 0 though?? - info.y = size.top; - return info; - } - - inline bool setWindowSize_impl(IWindow* window, const uint32_t width, const uint32_t height) override - { - // Calculate real window size based on client size - RECT clientSize; - clientSize.left = 0; - clientSize.top = 0; - clientSize.right = width; - clientSize.bottom = height; - - DWORD style = IWindowWin32::getWindowStyle(window->getFlags().value); - bool res = AdjustWindowRect(&clientSize, style, false); - assert(res); - - const int32_t realWidth = clientSize.right - clientSize.left; - const int32_t realHeight = clientSize.bottom - clientSize.top; - - auto wnd = static_cast(window); - m_windowThreadManager.setWindowSize(wnd->getNativeHandle(), realWidth, realHeight); - return true; - } - inline bool setWindowPosition_impl(IWindow* window, const int32_t x, const int32_t y) override - { - auto wnd = static_cast(window); - m_windowThreadManager.setWindowPosition(wnd->getNativeHandle(), x, y); - return true; - } - inline bool setWindowRotation_impl(IWindow* window, const bool landscape) override - { - return false; - } - inline bool setWindowVisible_impl(IWindow* window, const bool visible) override - { - auto wnd = static_cast(window); - if (visible) m_windowThreadManager.showWindow(wnd->getNativeHandle()); - else m_windowThreadManager.hideWindow(wnd->getNativeHandle()); - return true; - } - inline bool setWindowMaximized_impl(IWindow* window, const bool maximized) override - { - auto wnd = static_cast(window); - if (maximized) m_windowThreadManager.maximizeWindow(wnd->getNativeHandle()); - else m_windowThreadManager.minimizeWindow(wnd->getNativeHandle()); - return true; - } - - private: - inline IWindowWin32::native_handle_t createNativeWindow(int _x, int _y, uint32_t _w, uint32_t _h, IWindow::E_CREATE_FLAGS _flags, const std::string_view& caption) - { - IWindowWin32::native_handle_t out_handle; - m_windowThreadManager.createWindow(_x, _y, _w, _h, _flags, &out_handle, caption); - return out_handle; - } - inline void destroyNativeWindow(IWindowWin32::native_handle_t wnd) - { - m_windowThreadManager.destroyWindow(wnd); - } - - private: - enum E_REQUEST_TYPE - { - ERT_CREATE_WINDOW, - ERT_DESTROY_WINDOW, - ERT_CHANGE_CURSOR_VISIBILITY, - ERT_SET_WINDOW_POS, - ERT_SHOW_WINDOW - }; - template - struct SRequestParamsBase - { - static inline constexpr E_REQUEST_TYPE type = ERT; - }; - struct SRequestParams_CreateWindow : SRequestParamsBase - { - SRequestParams_CreateWindow(int32_t _x, int32_t _y, uint32_t _w, uint32_t _h, CWindowWin32::E_CREATE_FLAGS _flags, CWindowWin32::native_handle_t* wnd, const std::string_view& caption) : - x(_x), y(_y), width(_w), height(_h), flags(_flags), nativeWindow(wnd), windowCaption(caption) - { - } - int32_t x, y; - uint32_t width, height; - CWindowWin32::E_CREATE_FLAGS flags; - CWindowWin32::native_handle_t* nativeWindow; - std::string windowCaption; - }; - struct SRequestParams_DestroyWindow : SRequestParamsBase - { - CWindowWin32::native_handle_t nativeWindow; - }; - struct SRequestParams_ChangeCursorVisibility : SRequestParamsBase - { - bool visible; - }; - struct SRequestParams_SetWindowPos : SRequestParamsBase - { - IWindowWin32::native_handle_t window; - int x, y; - uint32_t width, height; - - uint32_t ignoreXY : 1 = 0; - uint32_t ignoreSize : 1 = 0; - }; - struct SRequestParams_ShowWindow : SRequestParamsBase - { - IWindowWin32::native_handle_t window; - uint32_t hide : 1 = 0; - uint32_t show : 1 = 0; - uint32_t minimized : 1 = 0; - uint32_t maximized : 1 = 0; - }; - struct SRequest - { - E_REQUEST_TYPE type; - union - { - SRequestParams_CreateWindow createWindowParam; - SRequestParams_DestroyWindow destroyWindowParam; - SRequestParams_ChangeCursorVisibility changeCursorVisibilityParam; - SRequestParams_SetWindowPos setWindowPosParam; - SRequestParams_ShowWindow showWindowParam; - }; - SRequest() {} - ~SRequest() {} - }; - - class CThreadHandler final : public system::IAsyncQueueDispatcher - { - using base_t = system::IAsyncQueueDispatcher; - friend base_t; - friend base_t::base_t; - public: - inline void createWindow(int32_t _x, int32_t _y, uint32_t _w, uint32_t _h, CWindowWin32::E_CREATE_FLAGS _flags, CWindowWin32::native_handle_t* wnd, const std::string_view& caption) - { - SRequestParams_CreateWindow params = SRequestParams_CreateWindow(_x, _y, _w, _h, _flags, wnd, caption); - auto& rq = request(params); - waitForCompletion(rq); - } - inline void destroyWindow(CWindowWin32::native_handle_t window) - { - SRequestParams_DestroyWindow params; - params.nativeWindow = window; - auto& rq = request(params); - waitForCompletion(rq); - } - inline void setCursorVisibility(bool visible) - { - SRequestParams_ChangeCursorVisibility params; - params.visible = visible; - auto& rq = request(params); - waitForCompletion(rq); - } - inline void setWindowSize(IWindowWin32::native_handle_t window, uint32_t width, uint32_t height) - { - SRequestParams_SetWindowPos params; - params.window = window; - params.width = width; - params.height = height; - params.ignoreXY = 1; - auto& rq = request(params); - waitForCompletion(rq); - } - inline void setWindowPosition(IWindowWin32::native_handle_t window, int x, int y) - { - SRequestParams_SetWindowPos params; - params.window = window; - params.x = x; - params.y = y; - params.ignoreSize = 1; - auto& rq = request(params); - waitForCompletion(rq); - } - inline void hideWindow(IWindowWin32::native_handle_t window) - { - SRequestParams_ShowWindow params; - params.window = window; - params.hide = 1; - auto& rq = request(params); - waitForCompletion(rq); - } - inline void showWindow(IWindowWin32::native_handle_t window) - { - SRequestParams_ShowWindow params; - params.window = window; - params.show = 1; - auto& rq = request(params); - waitForCompletion(rq); - } - inline void maximizeWindow(IWindowWin32::native_handle_t window) - { - SRequestParams_ShowWindow params; - params.window = window; - params.maximized = 1; - auto& rq = request(params); - waitForCompletion(rq); - } - inline void minimizeWindow(IWindowWin32::native_handle_t window) - { - SRequestParams_ShowWindow params; - params.window = window; - params.minimized = 1; - auto& rq = request(params); - waitForCompletion(rq); - } - - inline CThreadHandler() - { - this->start(); - } - inline ~CThreadHandler() - { - } - - private: - void waitForCompletion(SRequest& req) - { - req.wait_ready(); - req.discard_storage(); - } - - private: - void init() {} - - void exit() {} - - void background_work() - { - MSG message; - constexpr uint32_t timeoutInMS = 8; // gonna become 10 anyway - if (getMessageWithTimeout(&message, timeoutInMS)) - { - TranslateMessage(&message); - DispatchMessage(&message); - } - } - - void process_request(SRequest& req) - { - switch (req.type) - { - case ERT_CREATE_WINDOW: - { - auto& params = req.createWindowParam; - HINSTANCE hinstance = GetModuleHandle(NULL); - - const char* classname = "Nabla Engine"; - - WNDCLASSEXA wcex; - wcex.cbSize = sizeof(WNDCLASSEX); - wcex.style = CS_HREDRAW | CS_VREDRAW; - wcex.lpfnWndProc = CWindowWin32::WndProc; - wcex.cbClsExtra = 0; - wcex.cbWndExtra = 0; - wcex.hInstance = hinstance; - wcex.hIcon = NULL; - wcex.hCursor = nullptr; - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); - wcex.lpszMenuName = 0; - wcex.lpszClassName = classname; - wcex.hIconSm = 0; - - RegisterClassExA(&wcex); - // calculate client size - - RECT clientSize; - clientSize.top = params.y; - clientSize.left = params.x; - clientSize.right = clientSize.left + params.width; - clientSize.bottom = clientSize.top + params.height; - - DWORD style = IWindowWin32::getWindowStyle(params.flags); - - // TODO: - // if (hasMouseCaptured()) - // if (hasInputFocus()) - // if (hasMouseFocus()) - - AdjustWindowRect(&clientSize, style, FALSE); - - const int32_t realWidth = clientSize.right - clientSize.left; - const int32_t realHeight = clientSize.bottom - clientSize.top; - - - *params.nativeWindow = CreateWindowA(classname, params.windowCaption.c_str(), style, clientSize.left, clientSize.top, - realWidth, realHeight, NULL, NULL, hinstance, NULL); - if ((params.flags & CWindowWin32::ECF_HIDDEN) == 0) - ShowWindow(*params.nativeWindow, SW_SHOWNORMAL); - UpdateWindow(*params.nativeWindow); - - // fix ugly ATI driver bugs. Thanks to ariaci - // TODO still needed? - MoveWindow(*params.nativeWindow, clientSize.left, clientSize.top, realWidth, realHeight, TRUE); - { - //TODO: thoroughly test this stuff (what is this about, you need to register devices yourself!? I thought Windows can give you a list of raw input devices!?) - constexpr uint32_t INPUT_DEVICES_COUNT = 5; - RAWINPUTDEVICE inputDevices[INPUT_DEVICES_COUNT]; - inputDevices[0].hwndTarget = *params.nativeWindow; - inputDevices[0].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; - inputDevices[0].usUsagePage = HID_USAGE_PAGE_GENERIC; - inputDevices[0].usUsage = HID_USAGE_GENERIC_POINTER; - - inputDevices[1].hwndTarget = *params.nativeWindow; - inputDevices[1].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; - inputDevices[1].usUsagePage = HID_USAGE_PAGE_GENERIC; - inputDevices[1].usUsage = HID_USAGE_GENERIC_MOUSE; - - inputDevices[2].hwndTarget = *params.nativeWindow; - inputDevices[2].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; - inputDevices[2].usUsagePage = HID_USAGE_PAGE_GENERIC; - inputDevices[2].usUsage = HID_USAGE_GENERIC_KEYBOARD; - - inputDevices[3].hwndTarget = *params.nativeWindow; - inputDevices[3].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; - inputDevices[3].usUsagePage = HID_USAGE_PAGE_GAME; - inputDevices[3].usUsage = HID_USAGE_GENERIC_JOYSTICK; - - inputDevices[4].hwndTarget = *params.nativeWindow; - inputDevices[4].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; - inputDevices[4].usUsagePage = HID_USAGE_PAGE_GAME; - inputDevices[4].usUsage = HID_USAGE_GENERIC_GAMEPAD; - - RegisterRawInputDevices(inputDevices, INPUT_DEVICES_COUNT, sizeof(RAWINPUTDEVICE)); - } - break; - } - case ERT_DESTROY_WINDOW: - { - auto& params = req.destroyWindowParam; - DestroyWindow(params.nativeWindow); - break; - } - case ERT_CHANGE_CURSOR_VISIBILITY: - { - auto& params = req.changeCursorVisibilityParam; - if(params.visible) - { - int ret = ShowCursor(true); - while (ret < 0) ret = ShowCursor(true); - } - else - { - int ret = ShowCursor(false); - while (ret >= 0) ret = ShowCursor(false); - } - break; - } - case ERT_SET_WINDOW_POS: - { - auto& params = req.setWindowPosParam; - uint32_t flags = SWP_NOACTIVATE | SWP_NOZORDER; - if (params.ignoreXY) flags |= SWP_NOMOVE | SWP_NOREPOSITION; - if (params.ignoreSize) flags |= SWP_NOSIZE; - SetWindowPos(params.window, nullptr, params.x, params.y, params.width, params.height, flags); - break; - } - case ERT_SHOW_WINDOW: - { - auto& params = req.showWindowParam; - int showCmd; - if (params.hide) showCmd = SW_HIDE; - if (params.show) showCmd = SW_SHOW; - if (params.minimized) showCmd = SW_MINIMIZE; - if (params.maximized) showCmd = SW_MAXIMIZE; - ShowWindow(params.window, showCmd); - break; - } - } - } - - template - void request_impl(SRequest& req, RequestParams&& params) - { - req.type = params.type; - if constexpr (std::is_same_v) - { - req.createWindowParam = std::move(params); - } - else if constexpr (std::is_same_v) - { - req.destroyWindowParam = std::move(params); - } - else if constexpr (std::is_same_v) - { - req.changeCursorVisibilityParam = std::move(params); - } - else if constexpr (std::is_same_v) - { - req.setWindowPosParam = std::move(params); - } - else if constexpr (std::is_same_v) - { - req.showWindowParam = std::move(params); - } - } - - inline bool wakeupPredicate() const { return true; } - inline bool continuePredicate() const { return true; } - - private: - static inline bool getMessageWithTimeout(MSG* msg, uint32_t timeoutInMilliseconds) - { - bool res; - UINT_PTR timerId = SetTimer(NULL, NULL, timeoutInMilliseconds, NULL); - res = GetMessage(msg, nullptr, 0, 0); - - KillTimer(NULL, timerId); - - if (!res) - return false; - if (msg->message == WM_TIMER && msg->hwnd == NULL && msg->wParam == timerId) - return false; - return true; - } - } m_windowThreadManager; -}; - -} -#endif -#endif \ No newline at end of file diff --git a/include/nbl/ui/CWindowWin32.h b/include/nbl/ui/CWindowWin32.h deleted file mode 100644 index 645c641de7..0000000000 --- a/include/nbl/ui/CWindowWin32.h +++ /dev/null @@ -1,145 +0,0 @@ -#ifndef __C_WINDOW_WIN32_H_INCLUDED__ -#define __C_WINDOW_WIN32_H_INCLUDED__ - - -#include "nbl/ui/IWindowWin32.h" -#include "nbl/ui/CClipboardManagerWin32.h" - -#include - - -#ifdef _NBL_PLATFORM_WINDOWS_ - -namespace nbl::ui -{ -class CCursorControlWin32; -class CWindowManagerWin32; - -class NBL_API2 CWindowWin32 final : public IWindowWin32 -{ -public: - static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); - static E_KEY_CODE getNablaKeyCodeFromNative(uint8_t nativeWindowsKeyCode); - - CWindowWin32(core::smart_refctd_ptr&& winManager, SCreationParams&& params, native_handle_t hwnd); - - inline const native_handle_t& getNativeHandle() const override { return m_native; } - void setCaption(const std::string_view& caption) override; - virtual IWindowManager* getManager() override; - - ~CWindowWin32() override; -private: - inline CWindowWin32(CWindowManagerWin32* winManager, core::smart_refctd_ptr&& sys, SCreationParams&& params); - - native_handle_t m_native; - - core::smart_refctd_ptr m_windowManager; - - core::map> m_mouseEventChannels; - core::map> m_keyboardEventChannels; - - core::smart_refctd_ptr m_cursorControl; - core::smart_refctd_ptr m_clipboardManager; - - /* - * Storing this data is required for the device removal to work properly - * When you get a message about the device removal, its type isn't accessible anymore. - * When adding new devices, we return if we didnt have the device in the list before. - */ - core::map m_deviceTypes; - inline bool addMouseEventChannel(HANDLE deviceHandle, const core::smart_refctd_ptr& channel) - { - if (m_mouseEventChannels.find(deviceHandle) == m_mouseEventChannels.end()) - { - m_mouseEventChannels.emplace(deviceHandle, channel); - m_deviceTypes.emplace(deviceHandle, RIM_TYPEMOUSE); - return true; - } - return false; - } - inline bool addKeyboardEventChannel(HANDLE deviceHandle, const core::smart_refctd_ptr& channel) - { - if (m_keyboardEventChannels.find(deviceHandle) == m_keyboardEventChannels.end()) - { - m_keyboardEventChannels.emplace(deviceHandle, channel); - m_deviceTypes.emplace(deviceHandle, RIM_TYPEKEYBOARD); - return true; - } - return false; - } - - inline core::smart_refctd_ptr removeMouseEventChannel(HANDLE deviceHandle) - { - RAWINPUT; - auto it = m_mouseEventChannels.find(deviceHandle); - auto channel = std::move(it->second); - m_mouseEventChannels.erase(it); - m_deviceTypes.erase(m_deviceTypes.find(deviceHandle)); - return channel; - } - - inline core::smart_refctd_ptr removeKeyboardEventChannel(HANDLE deviceHandle) - { - auto it = m_keyboardEventChannels.find(deviceHandle); - auto channel = std::move(it->second); - m_keyboardEventChannels.erase(it); - m_deviceTypes.erase(m_deviceTypes.find(deviceHandle)); - return channel; - } - - inline int32_t getDeviceType(HANDLE h) - { - auto type = m_deviceTypes.find(h); - if (type != m_deviceTypes.end()) return type->second; - return -1; - } - - inline IMouseEventChannel* getMouseEventChannel(HANDLE deviceHandle) - { - /** - * This checking is necessary because some devices (like a laptop precision touchpad) - * don't get listed in GetRawInputDeviceList but will visible when you get an actual input - * from it (the handle to it will be nullptr). - **/ - auto ch = m_mouseEventChannels.find(deviceHandle); - // windows is a special boy - if (ch==m_mouseEventChannels.end()) - { - auto channel = core::make_smart_refctd_ptr(CIRCULAR_BUFFER_CAPACITY); - if (addMouseEventChannel(deviceHandle,std::move(channel))) - m_cb->onMouseConnected(this,std::move(channel)); - } - return m_mouseEventChannels.find(deviceHandle)->second.get(); - } - - inline IKeyboardEventChannel* getKeyboardEventChannel(HANDLE deviceHandle) - { - auto ch = m_keyboardEventChannels.find(deviceHandle); - // anydesk makes windows a special boy - if (ch==m_keyboardEventChannels.end()) - { - auto channel = core::make_smart_refctd_ptr(CIRCULAR_BUFFER_CAPACITY); - if (addKeyboardEventChannel(deviceHandle, std::move(channel))) - m_cb->onKeyboardConnected(this, std::move(channel)); - } - return m_keyboardEventChannels.find(deviceHandle)->second.get(); - } - - - // Inherited via IWindowWin32 - virtual IClipboardManager* getClipboardManager() override; - virtual ICursorControl* getCursorControl() override; - -private: - static constexpr uint32_t CIRCULAR_BUFFER_CAPACITY = 256; - - void addAlreadyConnectedInputDevices(); - POINT workspaceCoordinatesToScreen(const POINT& p); -}; - -} - -#endif - -#endif - diff --git a/include/nbl/ui/IClipboardManager.h b/include/nbl/ui/IClipboardManager.h index 590f5ffa07..dcd5bc8a5d 100644 --- a/include/nbl/ui/IClipboardManager.h +++ b/include/nbl/ui/IClipboardManager.h @@ -1,10 +1,9 @@ -#ifndef __NBL_I_CLIPBOARD_MANAGER_H_INCLUDED__ -#define __NBL_I_CLIPBOARD_MANAGER_H_INCLUDED__ +#ifndef _NBL_I_CLIPBOARD_MANAGER_H_INCLUDED_ +#define _NBL_I_CLIPBOARD_MANAGER_H_INCLUDED_ #include "nbl/core/IReferenceCounted.h" -#include "nbl/system/ISystem.h" + #include "nbl/asset/ICPUImage.h" -#include "nbl/asset/ICommandBuffer.h" namespace nbl::ui { diff --git a/include/nbl/ui/ICursorControl.h b/include/nbl/ui/ICursorControl.h index 87ce45f8c1..693993bb38 100644 --- a/include/nbl/ui/ICursorControl.h +++ b/include/nbl/ui/ICursorControl.h @@ -1,5 +1,5 @@ -#ifndef __NBL_SYSTEM_I_CURSOR_CONTROL_H_INCLUDED__ -#define __NBL_SYSTEM_I_CURSOR_CONTROL_H_INCLUDED__ +#ifndef _NBL_SYSTEM_I_CURSOR_CONTROL_H_INCLUDED_ +#define _NBL_SYSTEM_I_CURSOR_CONTROL_H_INCLUDED_ #include "nbl/core/declarations.h" #include "nbl/core/decl/Types.h" From a1b82a3b9730c3c697791c8731430cae324b7a49 Mon Sep 17 00:00:00 2001 From: devsh Date: Fri, 17 Mar 2023 21:51:16 +0100 Subject: [PATCH 13/24] Everything makes far more sense after this refactor! --- include/nbl/system/ISystem.h | 2 +- include/nbl/ui/ICursorControl.h | 2 +- .../nbl/ui/IGraphicalApplicationFramework.h | 2 + include/nbl/ui/IWindowManager.h | 2 +- include/nbl/ui/declarations.h | 7 +- include/nbl/ui/definitions.h | 14 +- src/nbl/CMakeLists.txt | 3 +- src/nbl/ui/CClipboardManagerWin32.cpp | 38 -- src/nbl/ui/CClipboardManagerWin32.h | 57 ++ src/nbl/ui/CCursorControlWin32.cpp | 50 -- .../nbl/ui/CGraphicalApplicationAndroid.h | 1 - src/nbl/ui/CWindowAndroid.h | 91 ++++ .../nbl/ui/CWindowManagerAndroid.h | 0 src/nbl/ui/CWindowManagerWin32.cpp | 237 ++++++++ src/nbl/ui/CWindowManagerWin32.h | 216 ++++++++ src/nbl/ui/CWindowWin32.cpp | 515 ++++++++---------- src/nbl/ui/CWindowWin32.h | 146 +++++ 17 files changed, 997 insertions(+), 386 deletions(-) delete mode 100644 src/nbl/ui/CClipboardManagerWin32.cpp create mode 100644 src/nbl/ui/CClipboardManagerWin32.h delete mode 100644 src/nbl/ui/CCursorControlWin32.cpp rename {include => src}/nbl/ui/CGraphicalApplicationAndroid.h (99%) create mode 100644 src/nbl/ui/CWindowAndroid.h rename {include => src}/nbl/ui/CWindowManagerAndroid.h (100%) create mode 100644 src/nbl/ui/CWindowManagerWin32.cpp create mode 100644 src/nbl/ui/CWindowManagerWin32.h create mode 100644 src/nbl/ui/CWindowWin32.h diff --git a/include/nbl/system/ISystem.h b/include/nbl/system/ISystem.h index 2c2538a3a0..5143435465 100644 --- a/include/nbl/system/ISystem.h +++ b/include/nbl/system/ISystem.h @@ -224,7 +224,7 @@ class NBL_API2 ISystem : public core::IReferenceCounted struct SRequestParams_NOOP { using retval_t = void; - inline void operator()(core::StorageTrivializer* retval, ICaller* _caller) {} + inline void operator()(core::StorageTrivializer* retval, ICaller* _caller) {assert(false);} }; struct SRequestParams_CREATE_FILE { diff --git a/include/nbl/ui/ICursorControl.h b/include/nbl/ui/ICursorControl.h index 693993bb38..36086d5d1c 100644 --- a/include/nbl/ui/ICursorControl.h +++ b/include/nbl/ui/ICursorControl.h @@ -9,7 +9,7 @@ namespace nbl::ui { -class ICursorControl : public core::IReferenceCounted +class ICursorControl : public virtual core::IReferenceCounted { public: struct SPosition diff --git a/include/nbl/ui/IGraphicalApplicationFramework.h b/include/nbl/ui/IGraphicalApplicationFramework.h index 41b9a6ccbe..dedaff7d06 100644 --- a/include/nbl/ui/IGraphicalApplicationFramework.h +++ b/include/nbl/ui/IGraphicalApplicationFramework.h @@ -1,6 +1,8 @@ #ifndef _NBL_UI_I_GRAPHICAL_APPLICATION_FRAMEWORK_H_INCLUDED_ #define _NBL_UI_I_GRAPHICAL_APPLICATION_FRAMEWORK_H_INCLUDED_ +#include "nbl/ui/IWindow.h" + #include #include diff --git a/include/nbl/ui/IWindowManager.h b/include/nbl/ui/IWindowManager.h index 994c375537..87cbb61f48 100644 --- a/include/nbl/ui/IWindowManager.h +++ b/include/nbl/ui/IWindowManager.h @@ -6,7 +6,7 @@ namespace nbl::ui { -class NBL_API2 IWindowManager : public core::IReferenceCounted +class NBL_API2 IWindowManager : public virtual core::IReferenceCounted { public: virtual core::smart_refctd_ptr createWindow(IWindow::SCreationParams&& creationParams) = 0; diff --git a/include/nbl/ui/declarations.h b/include/nbl/ui/declarations.h index 9759871dc6..5d15be4b5f 100644 --- a/include/nbl/ui/declarations.h +++ b/include/nbl/ui/declarations.h @@ -1,16 +1,15 @@ // Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_UI_DECLARATIONS_H_INCLUDED__ -#define __NBL_UI_DECLARATIONS_H_INCLUDED__ +#ifndef _NBL_UI_DECLARATIONS_H_INCLUDED_ +#define _NBL_UI_DECLARATIONS_H_INCLUDED_ // dependencies #include "nbl/system/declarations.h" // windows #if defined(_NBL_PLATFORM_WINDOWS_) -# include "nbl/ui/CWindowManagerWin32.h" +# include "nbl/ui/IWindowManagerWin32.h" #elif defined(_NBL_BUILD_WITH_WAYLAND) && defined(_NBL_TEST_WAYLAND) # include "nbl/ui/CWindowManagerWayland.h" #elif defined(_NBL_PLATFORM_LINUX_) diff --git a/include/nbl/ui/definitions.h b/include/nbl/ui/definitions.h index b420017941..1eab60d597 100644 --- a/include/nbl/ui/definitions.h +++ b/include/nbl/ui/definitions.h @@ -1,20 +1,10 @@ // Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_UI_DEFINITIONS_H_INCLUDED__ -#define __NBL_UI_DEFINITIONS_H_INCLUDED__ +#ifndef _NBL_UI_DEFINITIONS_H_INCLUDED_ +#define _NBL_UI_DEFINITIONS_H_INCLUDED_ // dependencies #include "nbl/system/definitions.h" -// windows -#include "nbl/ui/CWindowManagerWin32.h" -//#include "nbl/ui/CWindowManagerX11.h" -#include "nbl/ui/CWindowWin32.h" -// TODO -//#include "nbl/ui/CWindowAndroid.h" -//#include "nbl/ui/CWindowX11.h" -//#include "nbl/ui/CWindowWayland.h" - #endif \ No newline at end of file diff --git a/src/nbl/CMakeLists.txt b/src/nbl/CMakeLists.txt index e0f4811ede..88f374541c 100755 --- a/src/nbl/CMakeLists.txt +++ b/src/nbl/CMakeLists.txt @@ -185,8 +185,7 @@ set(NBL_SYSTEM_SOURCES ) set(NBL_UI_SOURCES ${NBL_ROOT_PATH}/src/nbl/ui/CWindowWin32.cpp - ${NBL_ROOT_PATH}/src/nbl/ui/CCursorControlWin32.cpp - ${NBL_ROOT_PATH}/src/nbl/ui/CClipboardManagerWin32.cpp + ${NBL_ROOT_PATH}/src/nbl/ui/CWindowManagerWin32.cpp ${NBL_ROOT_PATH}/src/nbl/ui/CWindowManagerAndroid.cpp ${NBL_ROOT_PATH}/src/nbl/ui/CGraphicalApplicationAndroid.cpp ) diff --git a/src/nbl/ui/CClipboardManagerWin32.cpp b/src/nbl/ui/CClipboardManagerWin32.cpp deleted file mode 100644 index 14ec3e0677..0000000000 --- a/src/nbl/ui/CClipboardManagerWin32.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#ifdef _NBL_PLATFORM_WINDOWS_ -#include "nbl/ui/CClipboardManagerWin32.h" -#include -namespace nbl::ui -{ - - std::string CClipboardManagerWin32::getClipboardText() - { - int32_t res = OpenClipboard(0); - assert(res != 0); - - std::string data = (const char*)GetClipboardData(CF_TEXT); - - CloseClipboard(); - - return data; - } - - bool CClipboardManagerWin32::setClipboardText(const std::string_view& str) - { - int32_t res = OpenClipboard(0); - if (res == 0) return false; - - EmptyClipboard(); - const char* data = str.data(); - const size_t data_size = strlen(data) + 1; - HGLOBAL h = GlobalAlloc(GMEM_MOVEABLE, data_size); - strcpy((char*)GlobalLock(h), LPCSTR(data)); - GlobalUnlock(h); - SetClipboardData(CF_TEXT, h); - - CloseClipboard(); - - return true; - } -} - -#endif \ No newline at end of file diff --git a/src/nbl/ui/CClipboardManagerWin32.h b/src/nbl/ui/CClipboardManagerWin32.h new file mode 100644 index 0000000000..1831208121 --- /dev/null +++ b/src/nbl/ui/CClipboardManagerWin32.h @@ -0,0 +1,57 @@ +#ifndef _NBL_UI_C_CLIPBOARD_MANAGER_WIN32_INCLUDED_ +#define _NBL_UI_C_CLIPBOARD_MANAGER_WIN32_INCLUDED_ + +#include "nbl/ui/IClipboardManager.h" + +#ifdef _NBL_PLATFORM_WINDOWS_ +#include +namespace nbl::ui +{ + +class NBL_API2 CClipboardManagerWin32 final : public IClipboardManager +{ + using base_t = IClipboardManager; + public: + inline CClipboardManagerWin32() = default; + + inline std::string getClipboardText() override + { + int32_t res = OpenClipboard(0); + assert(res != 0); + + std::string data = (const char*)GetClipboardData(CF_TEXT); + + CloseClipboard(); + + return data; + } + virtual bool setClipboardText(const std::string_view& str) override + { + int32_t res = OpenClipboard(0); + if (res == 0) + return false; + + EmptyClipboard(); + const char* data = str.data(); + const size_t data_size = strlen(data) + 1; + HGLOBAL h = GlobalAlloc(GMEM_MOVEABLE, data_size); + strcpy((char*)GlobalLock(h), LPCSTR(data)); + GlobalUnlock(h); + SetClipboardData(CF_TEXT, h); + + CloseClipboard(); + + return true; + } + + //virtual core::smart_refctd_ptr getClipboardImage() override; + //virtual bool setClipboardImage(asset::ICPUImage* image, asset::ICPUImage::SImageCopy data) override; + private: + //HGLOBAL* CPUImageToClipboardImage(asset::ICPUImage* image); +}; + +} + + +#endif +#endif \ No newline at end of file diff --git a/src/nbl/ui/CCursorControlWin32.cpp b/src/nbl/ui/CCursorControlWin32.cpp deleted file mode 100644 index 2bc86b45e2..0000000000 --- a/src/nbl/ui/CCursorControlWin32.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "nbl/ui/CCursorControlWin32.h" - -#ifdef _NBL_PLATFORM_WINDOWS_ - -#include - - -namespace nbl::ui -{ - -void CCursorControlWin32::setVisible(bool visible) -{ - m_windowManager->setCursorVisibility(visible); -} -bool CCursorControlWin32::isVisible() const -{ - CURSORINFO ci = { sizeof(CURSORINFO) }; - GetCursorInfo(&ci); - return ci.flags; // returning flags cause they're equal to 0 when the cursor is hidden -} -void CCursorControlWin32::setPosition(CCursorControlWin32::SPosition position) -{ - SetCursorPos(position.x, position.y); -} -void CCursorControlWin32::setRelativePosition(IWindow* window, CCursorControlWin32::SRelativePosition position) -{ - SPosition nativePos; - int32_t w = window->getWidth(); - int32_t h = window->getHeight(); - nativePos.x = (position.x / 2.f + 0.5f) * w + window->getX(); - nativePos.y = (position.y / 2.f + 0.5f) * h + window->getY(); - SetCursorPos(nativePos.x, nativePos.y); -} -CCursorControlWin32::SPosition CCursorControlWin32::getPosition() -{ - POINT cursorPos; - GetCursorPos(&cursorPos); - return { cursorPos.x, cursorPos.y }; -} -CCursorControlWin32::SRelativePosition CCursorControlWin32::getRelativePosition(IWindow* window) -{ - POINT cursorPos; - GetCursorPos(&cursorPos); - - return { ((cursorPos.x + 0.5f - window->getX()) / float(window->getWidth()) - 0.5f) * 2, ((cursorPos.y + 0.5f - window->getY()) / float(window->getWidth()) - 0.5f) * 2 }; -} - -} - -#endif diff --git a/include/nbl/ui/CGraphicalApplicationAndroid.h b/src/nbl/ui/CGraphicalApplicationAndroid.h similarity index 99% rename from include/nbl/ui/CGraphicalApplicationAndroid.h rename to src/nbl/ui/CGraphicalApplicationAndroid.h index d4af931da8..77a7b1b141 100644 --- a/include/nbl/ui/CGraphicalApplicationAndroid.h +++ b/src/nbl/ui/CGraphicalApplicationAndroid.h @@ -5,7 +5,6 @@ #include "nbl/system/CSystemAndroid.h" #include "nbl/ui/IGraphicalApplicationFramework.h" -#include "nbl/ui/IWindow.h" #include "nbl/ui/CWindowManagerAndroid.h" #ifdef _NBL_PLATFORM_ANDROID_ diff --git a/src/nbl/ui/CWindowAndroid.h b/src/nbl/ui/CWindowAndroid.h new file mode 100644 index 0000000000..18a364a4ee --- /dev/null +++ b/src/nbl/ui/CWindowAndroid.h @@ -0,0 +1,91 @@ +#ifndef _NBL_C_WINDOW_ANDROID_H_INCLUDED_ +#define _NBL_C_WINDOW_ANDROID_H_INCLUDED_ + +#include "nbl/ui/IWindowAndroid.h" + +#ifdef _NBL_PLATFORM_ANDROID_ +#include +#include +#include +#include + +namespace nbl::ui +{ + +class CWindowAndroid : public IWindowAndroid +{ + public: + constexpr static inline uint32_t CIRCULAR_BUFFER_CAPACITY = 256; + explicit inline CWindowAndroid(SCreationParams&& params, native_handle_t anw) : m_native(anw), IWindowAndroid(std::move(params)) + { + m_width = ANativeWindow_getWidth(anw); + m_height = ANativeWindow_getHeight(anw); + } + + virtual inline IClipboardManager* getClipboardManager() override { return nullptr; } + virtual inline ICursorControl* getCursorControl() override { return nullptr; } + + inline const native_handle_t& getNativeHandle() const override { return m_native; } + + inline void setCaption(const std::string_view& caption) override {} + virtual IWindowManager* getManager() override + { + /* + TODO: this class should have m_windowManager member... + */ + + assert(false); + return nullptr; + } + + // WHY THE FUCK ARE THESE PUBLIC? + core::map> m_mouseEventChannels; + core::map> m_keyboardEventChannels; + + inline bool hasMouseEventChannel(uint32_t deviceId) + { + return m_mouseEventChannels.find(deviceId) != m_mouseEventChannels.end(); + } + inline bool hasKeyboardEventChannel(uint32_t deviceId) + { + return m_keyboardEventChannels.find(deviceId) != m_keyboardEventChannels.end(); + } + inline bool addMouseEventChannel(uint32_t deviceId, const core::smart_refctd_ptr& channel) + { + if (m_mouseEventChannels.find(deviceId) == m_mouseEventChannels.end()) + { + m_mouseEventChannels.emplace(deviceId, channel); + return true; + } + return false; + } + inline bool addKeyboardEventChannel(uint32_t deviceId, const core::smart_refctd_ptr& channel) + { + if (m_keyboardEventChannels.find(deviceId) == m_keyboardEventChannels.end()) + { + m_keyboardEventChannels.emplace(deviceId, channel); + return true; + } + return false; + } + inline IMouseEventChannel* getMouseEventChannel(uint32_t deviceId) + { + auto ch = m_mouseEventChannels.find(deviceId); + return m_mouseEventChannels.find(deviceId)->second.get(); + } + + inline IKeyboardEventChannel* getKeyboardEventChannel(uint32_t deviceId) + { + auto ch = m_keyboardEventChannels.find(deviceId); + return m_keyboardEventChannels.find(deviceId)->second.get(); + } + + private: + native_handle_t m_native; +}; + +} + +#endif //_NBL_PLATFORM_ANDROID_ + +#endif diff --git a/include/nbl/ui/CWindowManagerAndroid.h b/src/nbl/ui/CWindowManagerAndroid.h similarity index 100% rename from include/nbl/ui/CWindowManagerAndroid.h rename to src/nbl/ui/CWindowManagerAndroid.h diff --git a/src/nbl/ui/CWindowManagerWin32.cpp b/src/nbl/ui/CWindowManagerWin32.cpp new file mode 100644 index 0000000000..784a1dce22 --- /dev/null +++ b/src/nbl/ui/CWindowManagerWin32.cpp @@ -0,0 +1,237 @@ +#include "nbl/ui/CWindowManagerWin32.h" +#include "nbl/ui/CWindowWin32.h" + +#ifdef _NBL_PLATFORM_WINDOWS_ +#include +#include +#include + +using namespace nbl; +using namespace nbl::ui; + +core::smart_refctd_ptr IWindowManagerWin32::create() +{ + return core::make_smart_refctd_ptr(); +} + +IWindowManager::SDisplayInfo CWindowManagerWin32::getPrimaryDisplayInfo() const +{ + RECT size; + BOOL res_ok = SystemParametersInfo(SPI_GETWORKAREA, 0, &size, 0); + SDisplayInfo info{}; + info.resX = size.right - size.left; + info.resY = size.bottom - size.top; + info.x = size.left; // When would this be not 0 though?? + info.y = size.top; + return info; +} + +static inline DWORD getWindowStyle(IWindow::E_CREATE_FLAGS flags) +{ + DWORD style = WS_POPUP; + + if ((flags & IWindow::ECF_FULLSCREEN) == 0) + { + if ((flags & IWindow::ECF_BORDERLESS) == 0) + { + style |= WS_BORDER; + style |= (WS_SYSMENU | WS_CAPTION); + } + // ? not sure about those below + style |= WS_CLIPCHILDREN; + style |= WS_CLIPSIBLINGS; + } + if (flags & IWindow::ECF_MINIMIZED) + { + style |= WS_MINIMIZE; + } + if (flags & IWindow::ECF_MAXIMIZED) + { + style |= WS_MAXIMIZE; + } + if (flags & IWindow::ECF_ALWAYS_ON_TOP) + { + style |= WS_EX_TOPMOST; + } + if ((flags & IWindow::ECF_HIDDEN) == 0) + { + style |= WS_VISIBLE; + } + style |= WS_OVERLAPPEDWINDOW; + + return style; +} + +core::smart_refctd_ptr CWindowManagerWin32::createWindow(IWindow::SCreationParams&& creationParams) +{ + CAsyncQueue::future_t future; + m_windowThreadManager.request(&future, SRequestParams_CreateWindow{ + .windowCaption = std::move(creationParams.windowCaption), + .width = creationParams.width, + .height = creationParams.height, + .x = creationParams.x, + .y = creationParams.y, + .flags = creationParams.flags + }); + if (auto handle = future.acquire()) + return core::make_smart_refctd_ptr(std::move(creationParams),core::smart_refctd_ptr(this),*handle); + return nullptr; +} + +bool CWindowManagerWin32::setWindowSize_impl(IWindow* window, const uint32_t width, const uint32_t height) +{ + // Calculate real window size based on client size + RECT clientSize; + clientSize.left = 0; + clientSize.top = 0; + clientSize.right = width; + clientSize.bottom = height; + + const DWORD style = getWindowStyle(window->getFlags().value); + bool res = AdjustWindowRect(&clientSize, style, false); + assert(res); + + CAsyncQueue::future_t future; + m_windowThreadManager.request(&future,SRequestParams_SetWindowSize{ + .nativeWindow = static_cast(window)->getNativeHandle(), + .width = clientSize.right-clientSize.left, + .height = clientSize.bottom-clientSize.top + }); + return true; +} + +void CWindowManagerWin32::CAsyncQueue::background_work() +{ + MSG msg; + { + constexpr uint32_t timeoutInMS = 8; // gonna become 10 anyway + const UINT_PTR timerId = SetTimer(NULL, NULL, timeoutInMS, NULL); + const bool result = GetMessage(&msg, nullptr, 0, 0); + + KillTimer(NULL, timerId); + + if (!result || (msg.message == WM_TIMER && msg.hwnd == NULL && msg.wParam == timerId)) + return; + } + TranslateMessage(&msg); + DispatchMessage(&msg); +} + +void CWindowManagerWin32::CAsyncQueue::process_request(base_t::future_base_t* _future_base, SRequest& req) +{ + std::visit([=](auto& visitor) { + using retval_t = std::remove_reference_t::retval_t; + visitor(base_t::future_storage_cast(_future_base)); + }, req.params); +} + +void CWindowManagerWin32::SRequestParams_CreateWindow::operator()(core::StorageTrivializer* retval) +{ + HINSTANCE hinstance = GetModuleHandle(NULL); + + const char* classname = "Nabla Engine"; + + WNDCLASSEXA wcex; + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = CWindowWin32::WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = hinstance; + wcex.hIcon = NULL; + wcex.hCursor = nullptr; + wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wcex.lpszMenuName = 0; + wcex.lpszClassName = classname; + wcex.hIconSm = 0; + + RegisterClassExA(&wcex); + // calculate client size + + RECT clientSize; + clientSize.top = y; + clientSize.left = x; + clientSize.right = clientSize.left + width; + clientSize.bottom = clientSize.top + height; + + const DWORD style = getWindowStyle(flags); + + // TODO: + // if (hasMouseCaptured()) + // if (hasInputFocus()) + // if (hasMouseFocus()) + + AdjustWindowRect(&clientSize, style, FALSE); + + const int32_t realWidth = clientSize.right - clientSize.left; + const int32_t realHeight = clientSize.bottom - clientSize.top; + + + auto nativeWindow = CreateWindowA( + classname, windowCaption.c_str(), style, + clientSize.left, clientSize.top, realWidth, realHeight, + NULL, NULL, hinstance, NULL + ); + + // + if ((flags&CWindowWin32::ECF_HIDDEN)==0) + ShowWindow(nativeWindow, SW_SHOWNORMAL); + UpdateWindow(nativeWindow); + + // fix ugly ATI driver bugs. Thanks to ariaci + // TODO still needed? + MoveWindow(nativeWindow, clientSize.left, clientSize.top, realWidth, realHeight, TRUE); + + { + //TODO: thoroughly test this stuff (what is this about, you need to register devices yourself!? I thought Windows can give you a list of raw input devices!?) + constexpr uint32_t INPUT_DEVICES_COUNT = 5; + RAWINPUTDEVICE inputDevices[INPUT_DEVICES_COUNT]; + inputDevices[0].hwndTarget = nativeWindow; + inputDevices[0].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; + inputDevices[0].usUsagePage = HID_USAGE_PAGE_GENERIC; + inputDevices[0].usUsage = HID_USAGE_GENERIC_POINTER; + + inputDevices[1].hwndTarget = nativeWindow; + inputDevices[1].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; + inputDevices[1].usUsagePage = HID_USAGE_PAGE_GENERIC; + inputDevices[1].usUsage = HID_USAGE_GENERIC_MOUSE; + + inputDevices[2].hwndTarget = nativeWindow; + inputDevices[2].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; + inputDevices[2].usUsagePage = HID_USAGE_PAGE_GENERIC; + inputDevices[2].usUsage = HID_USAGE_GENERIC_KEYBOARD; + + inputDevices[3].hwndTarget = nativeWindow; + inputDevices[3].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; + inputDevices[3].usUsagePage = HID_USAGE_PAGE_GAME; + inputDevices[3].usUsage = HID_USAGE_GENERIC_JOYSTICK; + + inputDevices[4].hwndTarget = nativeWindow; + inputDevices[4].dwFlags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK; + inputDevices[4].usUsagePage = HID_USAGE_PAGE_GAME; + inputDevices[4].usUsage = HID_USAGE_GENERIC_GAMEPAD; + + RegisterRawInputDevices(inputDevices, INPUT_DEVICES_COUNT, sizeof(RAWINPUTDEVICE)); + } + + retval->construct(nativeWindow); +} +void CWindowManagerWin32::SRequestParams_ChangeCursorVisibility::operator()(core::StorageTrivializer* retval) +{ + if (visible) + { + int ret = ShowCursor(true); + while (ret < 0) ret = ShowCursor(true); + } + else + { + int ret = ShowCursor(false); + while (ret >= 0) ret = ShowCursor(false); + } +} +void CWindowManagerWin32::SRequestParams_ShowWindow::operator()(core::StorageTrivializer* retval) +{ + const static int showCmd[] = {SW_HIDE,SW_SHOW,SW_MINIMIZE,SW_MAXIMIZE}; + ShowWindow(nativeWindow,showCmd[static_cast(state)]); +} +#endif \ No newline at end of file diff --git a/src/nbl/ui/CWindowManagerWin32.h b/src/nbl/ui/CWindowManagerWin32.h new file mode 100644 index 0000000000..7f23899b23 --- /dev/null +++ b/src/nbl/ui/CWindowManagerWin32.h @@ -0,0 +1,216 @@ +#ifndef _NBL_UI_C_WINDOWMANAGER_WIN32_INCLUDED_ +#define _NBL_UI_C_WINDOWMANAGER_WIN32_INCLUDED_ + +#include "nbl/ui/IWindowManagerWin32.h" +#include "nbl/ui/IWindowWin32.h" +#include "nbl/ui/ICursorControl.h" + +#ifdef _NBL_PLATFORM_WINDOWS_ +namespace nbl::ui +{ + +class CWindowManagerWin32 final : public IWindowManagerWin32, public ICursorControl +{ + public: + inline CWindowManagerWin32() = default; + + SDisplayInfo getPrimaryDisplayInfo() const override final; + + core::smart_refctd_ptr createWindow(IWindow::SCreationParams&& creationParams) override final; + + inline void destroyWindow(IWindow* wnd) override final + { + CAsyncQueue::future_t future; + m_windowThreadManager.request(&future,SRequestParams_DestroyWindow{static_cast(wnd)->getNativeHandle()}); + } + + + //! ICursorControl methods + inline void setVisible(bool visible) override + { + CAsyncQueue::future_t future; + m_windowThreadManager.request(&future,SRequestParams_ChangeCursorVisibility{visible}); + } + + inline bool isVisible() const override + { + CURSORINFO ci = { sizeof(CURSORINFO) }; + GetCursorInfo(&ci); + return ci.flags; // returning flags cause they're equal to 0 when the cursor is hidden + } + + inline void setPosition(SPosition pos) override + { + SetCursorPos(pos.x,pos.y); + } + + inline void setRelativePosition(IWindow* window, SRelativePosition pos) override + { + SPosition nativePos; + int32_t w = window->getWidth(); + int32_t h = window->getHeight(); + nativePos.x = (pos.x/2.f + 0.5f) * w + window->getX(); + nativePos.y = (pos.y/2.f + 0.5f) * h + window->getY(); + SetCursorPos(nativePos.x, nativePos.y); + } + + inline SPosition getPosition() override + { + POINT cursorPos; + GetCursorPos(&cursorPos); + return {cursorPos.x,cursorPos.y}; + } + inline SRelativePosition getRelativePosition(IWindow* window) override + { + POINT cursorPos; + GetCursorPos(&cursorPos); + return { + ((cursorPos.x+0.5f-window->getX())/float(window->getWidth())-0.5f) * 2, + ((cursorPos.y+0.5f-window->getY())/float(window->getWidth())-0.5f) * 2 + }; + } + + protected: + //! back to IWindowManager methods + bool setWindowSize_impl(IWindow* window, const uint32_t width, const uint32_t height) override; + + inline bool setWindowPosition_impl(IWindow* window, const int32_t x, const int32_t y) override + { + CAsyncQueue::future_t future; + m_windowThreadManager.request(&future,SRequestParams_SetWindowPos{ + .nativeWindow = static_cast(window)->getNativeHandle(), + .x = x, + .y = y + }); + return true; + } + + inline bool setWindowRotation_impl(IWindow* window, const bool landscape) override + { + return false; + } + + inline bool setWindowVisible_impl(IWindow* window, const bool visible) override + { + CAsyncQueue::future_t future; + m_windowThreadManager.request(&future,SRequestParams_ShowWindow{ + .nativeWindow = static_cast(window)->getNativeHandle(), + .state = visible ? SRequestParams_ShowWindow::STATE::SHOW:SRequestParams_ShowWindow::STATE::HIDE + }); + return true; + } + + inline bool setWindowMaximized_impl(IWindow* window, const bool maximized) override + { + CAsyncQueue::future_t future; + m_windowThreadManager.request(&future,SRequestParams_ShowWindow{ + .nativeWindow = static_cast(window)->getNativeHandle(), + .state = maximized ? SRequestParams_ShowWindow::STATE::MAXIMIZE:SRequestParams_ShowWindow::STATE::MINIMIZE + }); + return true; + } + + private: + struct SRequestParams_NOOP + { + using retval_t = void; + inline void operator()(core::StorageTrivializer* retval) {assert(false);} + }; + struct SRequestParams_CreateWindow + { + using retval_t = IWindowWin32::native_handle_t; + void operator()(core::StorageTrivializer* retval); + + std::string windowCaption; + uint32_t width, height; + int32_t x, y; + IWindowWin32::E_CREATE_FLAGS flags; + }; + struct SRequestParams_DestroyWindow + { + using retval_t = void; + inline void operator()(core::StorageTrivializer* retval) + { + DestroyWindow(nativeWindow); + } + + IWindowWin32::native_handle_t nativeWindow; + }; + struct SRequestParams_ChangeCursorVisibility + { + using retval_t = void; + void operator()(core::StorageTrivializer* retval); + + bool visible; + }; + struct SRequestParams_SetWindowSize + { + using retval_t = void; + inline void operator()(core::StorageTrivializer* retval) + { + SetWindowPos(nativeWindow, nullptr, 0xdeadbeef, 0xdeadbeef, width, height, SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOMOVE|SWP_NOREPOSITION); + } + + IWindowWin32::native_handle_t nativeWindow; + int32_t width, height; + }; + struct SRequestParams_SetWindowPos + { + using retval_t = void; + inline void operator()(core::StorageTrivializer* retval) + { + SetWindowPos(nativeWindow, nullptr, x, y, 0xdeadbeefu, 0xdeadbeefu, SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOSIZE); + } + + IWindowWin32::native_handle_t nativeWindow; + int x, y; + }; + struct SRequestParams_ShowWindow + { + using retval_t = void; + void operator()(core::StorageTrivializer* retval); + + IWindowWin32::native_handle_t nativeWindow; + enum class STATE : uint8_t + { + HIDE, + SHOW, + MINIMIZE, + MAXIMIZE + }; + STATE state; + }; + struct SRequest + { + std::variant< + SRequestParams_NOOP, + SRequestParams_CreateWindow, + SRequestParams_DestroyWindow, + SRequestParams_ChangeCursorVisibility, + SRequestParams_SetWindowSize, + SRequestParams_SetWindowPos, + SRequestParams_ShowWindow + > params = SRequestParams_NOOP(); + }; + static inline constexpr uint32_t CircularBufferSize = 256u; + class CAsyncQueue final : public system::IAsyncQueueDispatcher + { + using base_t = system::IAsyncQueueDispatcher; + + public: + inline CAsyncQueue() : base_t(base_t::start_on_construction) {} + + inline void init() {} + + inline bool wakeupPredicate() const { return true; } + inline bool continuePredicate() const { return true; } + + void background_work(); + + void process_request(base_t::future_base_t* _future_base, SRequest& req); + } m_windowThreadManager; +}; + +} +#endif +#endif \ No newline at end of file diff --git a/src/nbl/ui/CWindowWin32.cpp b/src/nbl/ui/CWindowWin32.cpp index 2a77e20a83..84ed052511 100644 --- a/src/nbl/ui/CWindowWin32.cpp +++ b/src/nbl/ui/CWindowWin32.cpp @@ -1,53 +1,24 @@ #include "nbl/ui/CWindowWin32.h" #include "nbl/ui/CWindowManagerWin32.h" -#include #ifdef _NBL_PLATFORM_WINDOWS_ +#include #include +#include +#include #include #include -#include #include -namespace nbl::ui -{ - - CWindowWin32::CWindowWin32(core::smart_refctd_ptr&& winManager, SCreationParams&& params, native_handle_t hwnd) : - IWindowWin32(std::move(params)), m_native(hwnd), m_windowManager(winManager), - m_cursorControl(core::make_smart_refctd_ptr(core::smart_refctd_ptr(m_windowManager))), - m_clipboardManager(core::make_smart_refctd_ptr()) - { - addAlreadyConnectedInputDevices(); - // do this last, we dont want the "WndProc" to be called concurrently to anything in the constructor - SetWindowLongPtr(m_native, GWLP_USERDATA, reinterpret_cast(this)); - } - - void CWindowWin32::setCaption(const std::string_view& caption) - { - SetWindowText(m_native, caption.data()); - } - - CWindowWin32::~CWindowWin32() - { - m_windowManager->destroyWindow(this); - } - - IWindowManager* CWindowWin32::getManager() - { - return m_windowManager.get(); - } - - IClipboardManager* CWindowWin32::getClipboardManager() - { - return m_clipboardManager.get(); - } - - ICursorControl* CWindowWin32::getCursorControl() - { - return m_cursorControl.get(); - } +using namespace nbl; +using namespace nbl::ui; - void CWindowWin32::addAlreadyConnectedInputDevices() +CWindowWin32::CWindowWin32(SCreationParams&& params, core::smart_refctd_ptr&& winManager, native_handle_t hwnd) : + IWindowWin32(std::move(params)), m_windowManager(std::move(winManager)), m_native(hwnd), + // clipboard can have window owners on Win32 (future TODO), this is why we don't have a singleton + m_clipboardManager(core::make_smart_refctd_ptr()) +{ + //addAlreadyConnectedInputDevices { UINT deviceCount; GetRawInputDeviceList(nullptr, &deviceCount, sizeof(RAWINPUTDEVICELIST)); @@ -56,43 +27,41 @@ namespace nbl::ui auto deviceList = devices.data(); for (int i = 0; i < deviceCount; i++) { + auto hDevice = deviceList[i].hDevice; switch (deviceList[i].dwType) { - case RIM_TYPEKEYBOARD: - { - auto channel = core::make_smart_refctd_ptr(CIRCULAR_BUFFER_CAPACITY); - if (addKeyboardEventChannel(deviceList[i].hDevice,core::smart_refctd_ptr(channel))) - m_cb->onKeyboardConnected(this,std::move(channel)); - break; - } - case RIM_TYPEMOUSE: - { - auto channel = core::make_smart_refctd_ptr(CIRCULAR_BUFFER_CAPACITY); - if (addMouseEventChannel(deviceList[i].hDevice,core::smart_refctd_ptr(channel))) - m_cb->onMouseConnected(this,std::move(channel)); - break; - } - default: - { - break; - } + case RIM_TYPEKEYBOARD: + getKeyboardEventChannel(hDevice); + break; + case RIM_TYPEMOUSE: + getMouseEventChannel(hDevice); + break; + default: + break; } } } + // do this last, we dont want the "WndProc" to be called concurrently to anything in the constructor + SetWindowLongPtr(m_native, GWLP_USERDATA, reinterpret_cast(this)); +} + +E_KEY_CODE getNablaKeyCodeFromNative(uint8_t nativeWindowsKeyCode); + +LRESULT CALLBACK CWindowWin32::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + // TODO: rethink this! + bool shouldCallDefProc = true; + + using namespace std::chrono; + auto timestamp = duration_cast(steady_clock::now().time_since_epoch()); + + CWindowWin32* window = reinterpret_cast(GetWindowLongPtr(hWnd, GWLP_USERDATA)); + if (!window) + return DefWindowProc(hWnd, message, wParam, lParam); - LRESULT CALLBACK CWindowWin32::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) + IEventCallback* eventCallback = window->getEventCallback(); + switch (message) { - bool shouldCallDefProc = true; - using namespace std::chrono; - auto timestamp = duration_cast (steady_clock::now().time_since_epoch()); - CWindowWin32* window = reinterpret_cast(GetWindowLongPtr(hWnd, GWLP_USERDATA)); - if (window == nullptr) - { - return DefWindowProc(hWnd, message, wParam, lParam); - } - IEventCallback* eventCallback = window->getEventCallback(); - switch (message) - { case WM_CLOSE: { if (!eventCallback->onWindowClosed(window)) shouldCallDefProc = false; @@ -130,12 +99,12 @@ namespace nbl::ui } switch (wParam) { - case SIZE_MAXIMIZED: - if(!eventCallback->onWindowMaximized(window)) shouldCallDefProc = false; - break; - case SIZE_MINIMIZED: - if(!eventCallback->onWindowMinimized(window)) shouldCallDefProc = false; - break; + case SIZE_MAXIMIZED: + if(!eventCallback->onWindowMaximized(window)) shouldCallDefProc = false; + break; + case SIZE_MINIMIZED: + if(!eventCallback->onWindowMinimized(window)) shouldCallDefProc = false; + break; } break; } @@ -155,15 +124,15 @@ namespace nbl::ui { switch (wParam) { - case WA_CLICKACTIVE: [[fallthrough]]; - case WA_ACTIVE: - eventCallback->onGainedMouseFocus(window); - window->m_flags = (E_CREATE_FLAGS)(window->m_flags.value | ECF_MOUSE_FOCUS); - break; - case WA_INACTIVE: - eventCallback->onLostMouseFocus(window); - window->m_flags = (E_CREATE_FLAGS)(window->m_flags.value & ~ECF_MOUSE_FOCUS); - break; + case WA_CLICKACTIVE: [[fallthrough]]; + case WA_ACTIVE: + eventCallback->onGainedMouseFocus(window); + window->m_flags = (E_CREATE_FLAGS)(window->m_flags.value | ECF_MOUSE_FOCUS); + break; + case WA_INACTIVE: + eventCallback->onLostMouseFocus(window); + window->m_flags = (E_CREATE_FLAGS)(window->m_flags.value & ~ECF_MOUSE_FOCUS); + break; } break; } @@ -179,49 +148,49 @@ namespace nbl::ui switch (wParam) { - case GIDC_ARRIVAL: - { - if (deviceInfo.dwType == RIM_TYPEMOUSE) + case GIDC_ARRIVAL: { - auto channel = core::make_smart_refctd_ptr(CIRCULAR_BUFFER_CAPACITY); - if (window->addMouseEventChannel(deviceHandle,core::smart_refctd_ptr(channel))) - eventCallback->onMouseConnected(window,std::move(channel)); - } - else if (deviceInfo.dwType == RIM_TYPEKEYBOARD) - { - auto channel = core::make_smart_refctd_ptr(CIRCULAR_BUFFER_CAPACITY); - if (window->addKeyboardEventChannel(deviceHandle,core::smart_refctd_ptr(channel))) - eventCallback->onKeyboardConnected(window,std::move(channel)); + if (deviceInfo.dwType == RIM_TYPEMOUSE) + { + auto channel = core::make_smart_refctd_ptr(ChannelEventCapacity); + if (window->addMouseEventChannel(deviceHandle,core::smart_refctd_ptr(channel))) + eventCallback->onMouseConnected(window,std::move(channel)); + } + else if (deviceInfo.dwType == RIM_TYPEKEYBOARD) + { + auto channel = core::make_smart_refctd_ptr(ChannelEventCapacity); + if (window->addKeyboardEventChannel(deviceHandle,core::smart_refctd_ptr(channel))) + eventCallback->onKeyboardConnected(window,std::move(channel)); + } + else if (deviceInfo.dwType == RIM_TYPEHID) + { + // TODO + } + break; } - else if (deviceInfo.dwType == RIM_TYPEHID) + case GIDC_REMOVAL: { - // TODO - } - break; - } - case GIDC_REMOVAL: - { - // Apparently on device removal the info about its type - // isn't accessible any more, so it requires a bit of tweaking - int32_t deviceType = window->getDeviceType(deviceHandle); - if (deviceType == -1) break; + // Apparently on device removal the info about its type + // isn't accessible any more, so it requires a bit of tweaking + int32_t deviceType = window->getDeviceType(deviceHandle); + if (deviceType == -1) break; - if (deviceType == RIM_TYPEMOUSE) - { - auto channel = window->removeMouseEventChannel(deviceHandle); - eventCallback->onMouseDisconnected(window, channel.get()); - } - else if (deviceType == RIM_TYPEKEYBOARD) - { - auto channel = window->removeKeyboardEventChannel(deviceHandle); - eventCallback->onKeyboardDisconnected(window, channel.get()); - } - else if (deviceType == RIM_TYPEHID) - { - // TODO + if (deviceType == RIM_TYPEMOUSE) + { + auto channel = window->removeMouseEventChannel(deviceHandle); + eventCallback->onMouseDisconnected(window, channel.get()); + } + else if (deviceType == RIM_TYPEKEYBOARD) + { + auto channel = window->removeKeyboardEventChannel(deviceHandle); + eventCallback->onKeyboardDisconnected(window, channel.get()); + } + else if (deviceType == RIM_TYPEHID) + { + // TODO + } + break; } - break; - } } break; } @@ -237,172 +206,172 @@ namespace nbl::ui HANDLE device = rawInput->header.hDevice; switch (rawInput->header.dwType) { - case RIM_TYPEMOUSE: - { - auto* inputChannel = window->getMouseEventChannel(device); - RAWMOUSE rawMouse = rawInput->data.mouse; - SMouseEvent event(timestamp); - - if ((rawMouse.usFlags & MOUSE_MOVE_RELATIVE) == MOUSE_MOVE_RELATIVE) + case RIM_TYPEMOUSE: { - // XD apparently a flag can be set, but there will be no actual movement - if (rawMouse.lLastX != 0 || rawMouse.lLastY != 0) + auto* inputChannel = window->getMouseEventChannel(device); + RAWMOUSE rawMouse = rawInput->data.mouse; + SMouseEvent event(timestamp); + + if ((rawMouse.usFlags & MOUSE_MOVE_RELATIVE) == MOUSE_MOVE_RELATIVE) { - event.type = SMouseEvent::EET_MOVEMENT; - event.movementEvent.relativeMovementX = rawMouse.lLastX; - event.movementEvent.relativeMovementY = rawMouse.lLastY; + // XD apparently a flag can be set, but there will be no actual movement + if (rawMouse.lLastX != 0 || rawMouse.lLastY != 0) + { + event.type = SMouseEvent::EET_MOVEMENT; + event.movementEvent.relativeMovementX = rawMouse.lLastX; + event.movementEvent.relativeMovementY = rawMouse.lLastY; + event.window = window; + auto lk = inputChannel->lockBackgroundBuffer(); + inputChannel->pushIntoBackground(std::move(event)); + } + } + if (rawMouse.usButtonFlags & RI_MOUSE_LEFT_BUTTON_DOWN) + { + event.type = SMouseEvent::EET_CLICK; + event.clickEvent.mouseButton = E_MOUSE_BUTTON::EMB_LEFT_BUTTON; + event.clickEvent.action = SMouseEvent::SClickEvent::EA_PRESSED; event.window = window; + auto mousePos = window->getCursorControl()->getPosition(); + event.clickEvent.clickPosX = mousePos.x; + event.clickEvent.clickPosY = mousePos.y; auto lk = inputChannel->lockBackgroundBuffer(); inputChannel->pushIntoBackground(std::move(event)); } - } - if (rawMouse.usButtonFlags & RI_MOUSE_LEFT_BUTTON_DOWN) - { - event.type = SMouseEvent::EET_CLICK; - event.clickEvent.mouseButton = E_MOUSE_BUTTON::EMB_LEFT_BUTTON; - event.clickEvent.action = SMouseEvent::SClickEvent::EA_PRESSED; - event.window = window; - auto mousePos = window->getCursorControl()->getPosition(); - event.clickEvent.clickPosX = mousePos.x; - event.clickEvent.clickPosY = mousePos.y; - auto lk = inputChannel->lockBackgroundBuffer(); - inputChannel->pushIntoBackground(std::move(event)); - } - else if (rawMouse.usButtonFlags & RI_MOUSE_LEFT_BUTTON_UP) - { - event.type = SMouseEvent::EET_CLICK; - event.clickEvent.mouseButton = E_MOUSE_BUTTON::EMB_LEFT_BUTTON; - event.clickEvent.action = SMouseEvent::SClickEvent::EA_RELEASED; - event.window = window; - auto mousePos = window->getCursorControl()->getPosition(); - event.clickEvent.clickPosX = mousePos.x; - event.clickEvent.clickPosY = mousePos.y; - auto lk = inputChannel->lockBackgroundBuffer(); - inputChannel->pushIntoBackground(std::move(event)); - } - if (rawMouse.usButtonFlags & RI_MOUSE_RIGHT_BUTTON_DOWN) - { - event.type = SMouseEvent::EET_CLICK; - event.clickEvent.mouseButton = E_MOUSE_BUTTON::EMB_RIGHT_BUTTON; - event.clickEvent.action = SMouseEvent::SClickEvent::EA_PRESSED; - event.window = window; - auto mousePos = window->getCursorControl()->getPosition(); - event.clickEvent.clickPosX = mousePos.x; - event.clickEvent.clickPosY = mousePos.y; - auto lk = inputChannel->lockBackgroundBuffer(); - inputChannel->pushIntoBackground(std::move(event)); - } - else if (rawMouse.usButtonFlags & RI_MOUSE_RIGHT_BUTTON_UP) - { - event.type = SMouseEvent::EET_CLICK; - event.clickEvent.mouseButton = E_MOUSE_BUTTON::EMB_RIGHT_BUTTON; - event.clickEvent.action = SMouseEvent::SClickEvent::EA_RELEASED; - event.window = window; - auto mousePos = window->getCursorControl()->getPosition(); - event.clickEvent.clickPosX = mousePos.x; - event.clickEvent.clickPosY = mousePos.y; - auto lk = inputChannel->lockBackgroundBuffer(); - inputChannel->pushIntoBackground(std::move(event)); - } - if (rawMouse.usButtonFlags & RI_MOUSE_MIDDLE_BUTTON_DOWN) - { - event.type = SMouseEvent::EET_CLICK; - event.clickEvent.mouseButton = E_MOUSE_BUTTON::EMB_MIDDLE_BUTTON; - event.clickEvent.action = SMouseEvent::SClickEvent::EA_PRESSED; - event.window = window; - auto mousePos = window->getCursorControl()->getPosition(); - event.clickEvent.clickPosX = mousePos.x; - event.clickEvent.clickPosY = mousePos.y; - auto lk = inputChannel->lockBackgroundBuffer(); - inputChannel->pushIntoBackground(std::move(event)); - } - else if (rawMouse.usButtonFlags & RI_MOUSE_MIDDLE_BUTTON_UP) - { - event.type = SMouseEvent::EET_CLICK; - event.clickEvent.mouseButton = E_MOUSE_BUTTON::EMB_MIDDLE_BUTTON; - event.clickEvent.action = SMouseEvent::SClickEvent::EA_RELEASED; - event.window = window; - auto mousePos = window->getCursorControl()->getPosition(); - event.clickEvent.clickPosX = mousePos.x; - event.clickEvent.clickPosY = mousePos.y; - auto lk = inputChannel->lockBackgroundBuffer(); - inputChannel->pushIntoBackground(std::move(event)); - } - // TODO other mouse buttons + else if (rawMouse.usButtonFlags & RI_MOUSE_LEFT_BUTTON_UP) + { + event.type = SMouseEvent::EET_CLICK; + event.clickEvent.mouseButton = E_MOUSE_BUTTON::EMB_LEFT_BUTTON; + event.clickEvent.action = SMouseEvent::SClickEvent::EA_RELEASED; + event.window = window; + auto mousePos = window->getCursorControl()->getPosition(); + event.clickEvent.clickPosX = mousePos.x; + event.clickEvent.clickPosY = mousePos.y; + auto lk = inputChannel->lockBackgroundBuffer(); + inputChannel->pushIntoBackground(std::move(event)); + } + if (rawMouse.usButtonFlags & RI_MOUSE_RIGHT_BUTTON_DOWN) + { + event.type = SMouseEvent::EET_CLICK; + event.clickEvent.mouseButton = E_MOUSE_BUTTON::EMB_RIGHT_BUTTON; + event.clickEvent.action = SMouseEvent::SClickEvent::EA_PRESSED; + event.window = window; + auto mousePos = window->getCursorControl()->getPosition(); + event.clickEvent.clickPosX = mousePos.x; + event.clickEvent.clickPosY = mousePos.y; + auto lk = inputChannel->lockBackgroundBuffer(); + inputChannel->pushIntoBackground(std::move(event)); + } + else if (rawMouse.usButtonFlags & RI_MOUSE_RIGHT_BUTTON_UP) + { + event.type = SMouseEvent::EET_CLICK; + event.clickEvent.mouseButton = E_MOUSE_BUTTON::EMB_RIGHT_BUTTON; + event.clickEvent.action = SMouseEvent::SClickEvent::EA_RELEASED; + event.window = window; + auto mousePos = window->getCursorControl()->getPosition(); + event.clickEvent.clickPosX = mousePos.x; + event.clickEvent.clickPosY = mousePos.y; + auto lk = inputChannel->lockBackgroundBuffer(); + inputChannel->pushIntoBackground(std::move(event)); + } + if (rawMouse.usButtonFlags & RI_MOUSE_MIDDLE_BUTTON_DOWN) + { + event.type = SMouseEvent::EET_CLICK; + event.clickEvent.mouseButton = E_MOUSE_BUTTON::EMB_MIDDLE_BUTTON; + event.clickEvent.action = SMouseEvent::SClickEvent::EA_PRESSED; + event.window = window; + auto mousePos = window->getCursorControl()->getPosition(); + event.clickEvent.clickPosX = mousePos.x; + event.clickEvent.clickPosY = mousePos.y; + auto lk = inputChannel->lockBackgroundBuffer(); + inputChannel->pushIntoBackground(std::move(event)); + } + else if (rawMouse.usButtonFlags & RI_MOUSE_MIDDLE_BUTTON_UP) + { + event.type = SMouseEvent::EET_CLICK; + event.clickEvent.mouseButton = E_MOUSE_BUTTON::EMB_MIDDLE_BUTTON; + event.clickEvent.action = SMouseEvent::SClickEvent::EA_RELEASED; + event.window = window; + auto mousePos = window->getCursorControl()->getPosition(); + event.clickEvent.clickPosX = mousePos.x; + event.clickEvent.clickPosY = mousePos.y; + auto lk = inputChannel->lockBackgroundBuffer(); + inputChannel->pushIntoBackground(std::move(event)); + } + // TODO other mouse buttons - if (rawMouse.usButtonFlags & RI_MOUSE_WHEEL) - { - SHORT wheelDelta = static_cast(rawMouse.usButtonData); - event.type = SMouseEvent::EET_SCROLL; - event.scrollEvent.verticalScroll = wheelDelta; - event.scrollEvent.horizontalScroll = 0; - event.window = window; - auto lk = inputChannel->lockBackgroundBuffer(); - inputChannel->pushIntoBackground(std::move(event)); - } - else if (rawMouse.usButtonFlags & RI_MOUSE_HWHEEL) - { - SHORT wheelDelta = static_cast(rawMouse.usButtonData); - event.type = SMouseEvent::EET_SCROLL; - event.scrollEvent.verticalScroll = 0; - event.scrollEvent.horizontalScroll = wheelDelta; - event.window = window; - auto lk = inputChannel->lockBackgroundBuffer(); - inputChannel->pushIntoBackground(std::move(event)); + if (rawMouse.usButtonFlags & RI_MOUSE_WHEEL) + { + SHORT wheelDelta = static_cast(rawMouse.usButtonData); + event.type = SMouseEvent::EET_SCROLL; + event.scrollEvent.verticalScroll = wheelDelta; + event.scrollEvent.horizontalScroll = 0; + event.window = window; + auto lk = inputChannel->lockBackgroundBuffer(); + inputChannel->pushIntoBackground(std::move(event)); + } + else if (rawMouse.usButtonFlags & RI_MOUSE_HWHEEL) + { + SHORT wheelDelta = static_cast(rawMouse.usButtonData); + event.type = SMouseEvent::EET_SCROLL; + event.scrollEvent.verticalScroll = 0; + event.scrollEvent.horizontalScroll = wheelDelta; + event.window = window; + auto lk = inputChannel->lockBackgroundBuffer(); + inputChannel->pushIntoBackground(std::move(event)); + } + break; } - break; - } - case RIM_TYPEKEYBOARD: - { - auto inputChannel = window->getKeyboardEventChannel(device); - RAWKEYBOARD rawKeyboard = rawInput->data.keyboard; - switch (rawKeyboard.Message) - { - case WM_KEYDOWN: [[fallthrough]]; - case WM_SYSKEYDOWN: + case RIM_TYPEKEYBOARD: { - SKeyboardEvent event(timestamp); - event.action = SKeyboardEvent::ECA_PRESSED; - event.window = window; - event.keyCode = getNablaKeyCodeFromNative(rawKeyboard.VKey); - auto lk = inputChannel->lockBackgroundBuffer(); - inputChannel->pushIntoBackground(std::move(event)); + auto inputChannel = window->getKeyboardEventChannel(device); + RAWKEYBOARD rawKeyboard = rawInput->data.keyboard; + switch (rawKeyboard.Message) + { + case WM_KEYDOWN: [[fallthrough]]; + case WM_SYSKEYDOWN: + { + SKeyboardEvent event(timestamp); + event.action = SKeyboardEvent::ECA_PRESSED; + event.window = window; + event.keyCode = getNablaKeyCodeFromNative(rawKeyboard.VKey); + auto lk = inputChannel->lockBackgroundBuffer(); + inputChannel->pushIntoBackground(std::move(event)); + break; + } + case WM_KEYUP: [[fallthrough]]; + case WM_SYSKEYUP: + { + SKeyboardEvent event(timestamp); + event.action = SKeyboardEvent::ECA_RELEASED; + event.window = window; + event.keyCode = getNablaKeyCodeFromNative(rawKeyboard.VKey); + auto lk = inputChannel->lockBackgroundBuffer(); + inputChannel->pushIntoBackground(std::move(event)); + break; + } + } + break; } - case WM_KEYUP: [[fallthrough]]; - case WM_SYSKEYUP: + case RIM_TYPEHID: { - SKeyboardEvent event(timestamp); - event.action = SKeyboardEvent::ECA_RELEASED; - event.window = window; - event.keyCode = getNablaKeyCodeFromNative(rawKeyboard.VKey); - auto lk = inputChannel->lockBackgroundBuffer(); - inputChannel->pushIntoBackground(std::move(event)); + // TODO break; } - } - - break; - } - case RIM_TYPEHID: - { - // TODO - break; - } } break; } - } - if(shouldCallDefProc) - return DefWindowProc(hWnd, message, wParam, lParam); - return 0; } + if(shouldCallDefProc) + return DefWindowProc(hWnd, message, wParam, lParam); + return 0; +} - E_KEY_CODE CWindowWin32::getNablaKeyCodeFromNative(uint8_t nativeWindowsKeyCode) +E_KEY_CODE getNablaKeyCodeFromNative(uint8_t nativeWindowsKeyCode) +{ + nbl::ui::E_KEY_CODE nablaKeyCode = EKC_NONE; + switch (nativeWindowsKeyCode) { - nbl::ui::E_KEY_CODE nablaKeyCode = EKC_NONE; - switch (nativeWindowsKeyCode) - { case VK_BACK: nablaKeyCode = EKC_BACKSPACE; break; case VK_TAB: nablaKeyCode = EKC_TAB; break; case VK_CLEAR: nablaKeyCode = EKC_CLEAR; break; @@ -527,13 +496,7 @@ namespace nbl::ui case VK_F22: nablaKeyCode = EKC_F22; break; case VK_F23: nablaKeyCode = EKC_F23; break; case VK_F24: nablaKeyCode = EKC_F24; break; - - - } - return nablaKeyCode; } - + return nablaKeyCode; } - - #endif \ No newline at end of file diff --git a/src/nbl/ui/CWindowWin32.h b/src/nbl/ui/CWindowWin32.h new file mode 100644 index 0000000000..295e21e525 --- /dev/null +++ b/src/nbl/ui/CWindowWin32.h @@ -0,0 +1,146 @@ +#ifndef _NBL_UI_C_WINDOW_WIN32_H_INCLUDED_ +#define _NBL_UI_C_WINDOW_WIN32_H_INCLUDED_ + + +#include "nbl/ui/CWindowManagerWin32.h" +#include "nbl/ui/CClipboardManagerWin32.h" + +#include +#include + + +#ifdef _NBL_PLATFORM_WINDOWS_ +namespace nbl::ui +{ + +class NBL_API2 CWindowWin32 final : public IWindowWin32 +{ + public: + + CWindowWin32(SCreationParams&& params, core::smart_refctd_ptr&& winManager, native_handle_t hwnd); + + inline const native_handle_t& getNativeHandle() const override {return m_native;} + + inline void setCaption(const std::string_view& caption) override + { + SetWindowText(m_native,caption.data()); + } + + inline IClipboardManager* getClipboardManager() override + { + return m_clipboardManager.get(); + } + + inline ICursorControl* getCursorControl() override {return m_windowManager.get();} + + inline IWindowManager* getManager() const override {return m_windowManager.get();} + + //! + static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + + protected: + inline ~CWindowWin32() override + { + m_windowManager->destroyWindow(this); + } + + private: + core::smart_refctd_ptr m_windowManager; + native_handle_t m_native; + core::smart_refctd_ptr m_clipboardManager; + + core::map> m_mouseEventChannels; + core::map> m_keyboardEventChannels; + + /* + * Storing this data is required for the device removal to work properly + * When you get a message about the device removal, its type isn't accessible anymore. + * When adding new devices, we return if we didnt have the device in the list before. + */ + core::map m_deviceTypes; + inline bool addMouseEventChannel(HANDLE deviceHandle, const core::smart_refctd_ptr& channel) + { + if (m_mouseEventChannels.find(deviceHandle) == m_mouseEventChannels.end()) + { + m_mouseEventChannels.emplace(deviceHandle, channel); + m_deviceTypes.emplace(deviceHandle, RIM_TYPEMOUSE); + return true; + } + return false; + } + inline bool addKeyboardEventChannel(HANDLE deviceHandle, const core::smart_refctd_ptr& channel) + { + if (m_keyboardEventChannels.find(deviceHandle) == m_keyboardEventChannels.end()) + { + m_keyboardEventChannels.emplace(deviceHandle, channel); + m_deviceTypes.emplace(deviceHandle, RIM_TYPEKEYBOARD); + return true; + } + return false; + } + + inline core::smart_refctd_ptr removeMouseEventChannel(HANDLE deviceHandle) + { + RAWINPUT; + auto it = m_mouseEventChannels.find(deviceHandle); + auto channel = std::move(it->second); + m_mouseEventChannels.erase(it); + m_deviceTypes.erase(m_deviceTypes.find(deviceHandle)); + return channel; + } + + inline core::smart_refctd_ptr removeKeyboardEventChannel(HANDLE deviceHandle) + { + auto it = m_keyboardEventChannels.find(deviceHandle); + auto channel = std::move(it->second); + m_keyboardEventChannels.erase(it); + m_deviceTypes.erase(m_deviceTypes.find(deviceHandle)); + return channel; + } + + inline int32_t getDeviceType(HANDLE h) + { + auto type = m_deviceTypes.find(h); + if (type != m_deviceTypes.end()) return type->second; + return -1; + } + + static constexpr inline uint32_t ChannelEventCapacity = 256; + inline IMouseEventChannel* getMouseEventChannel(HANDLE deviceHandle) + { + /** + * This checking is necessary because some devices (like a laptop precision touchpad) + * don't get listed in GetRawInputDeviceList but will visible when you get an actual input + * from it (the handle to it will be nullptr). + **/ + auto ch = m_mouseEventChannels.find(deviceHandle); + // windows is a special boy + if (ch==m_mouseEventChannels.end()) + { + auto channel = core::make_smart_refctd_ptr(ChannelEventCapacity); + if (addMouseEventChannel(deviceHandle,std::move(channel))) + m_cb->onMouseConnected(this,std::move(channel)); + } + return m_mouseEventChannels.find(deviceHandle)->second.get(); + } + inline IKeyboardEventChannel* getKeyboardEventChannel(HANDLE deviceHandle) + { + auto ch = m_keyboardEventChannels.find(deviceHandle); + // anydesk makes windows a special boy + if (ch==m_keyboardEventChannels.end()) + { + auto channel = core::make_smart_refctd_ptr(ChannelEventCapacity); + if (addKeyboardEventChannel(deviceHandle, std::move(channel))) + m_cb->onKeyboardConnected(this, std::move(channel)); + } + return m_keyboardEventChannels.find(deviceHandle)->second.get(); + } + + POINT workspaceCoordinatesToScreen(const POINT& p); +}; + +} +#endif + +#endif + From a6ff1ae69d84d5027172abf47e20e04b05bee76a Mon Sep 17 00:00:00 2001 From: devsh Date: Fri, 17 Mar 2023 22:23:50 +0100 Subject: [PATCH 14/24] try not to pollute with windows headers --- .../nbl/system/CColoredStdoutLoggerWin32.h | 27 ++----- include/nbl/system/CSystemWin32.h | 3 + include/nbl/system/DefaultFuncPtrLoader.h | 74 +++---------------- .../system/DynamicLibraryFunctionPointer.h | 6 +- include/nbl/system/FuncPtrLoader.h | 7 +- src/nbl/CMakeLists.txt | 2 + src/nbl/system/CColoredStdoutLoggerWin32.cpp | 22 ++++++ src/nbl/system/CSystemWin32.cpp | 1 - src/nbl/system/DefaultFuncPtrLoader.cpp | 68 +++++++++++++++++ 9 files changed, 119 insertions(+), 91 deletions(-) create mode 100644 src/nbl/system/CColoredStdoutLoggerWin32.cpp create mode 100644 src/nbl/system/DefaultFuncPtrLoader.cpp diff --git a/include/nbl/system/CColoredStdoutLoggerWin32.h b/include/nbl/system/CColoredStdoutLoggerWin32.h index 5bd2fe41a6..98a52149d6 100644 --- a/include/nbl/system/CColoredStdoutLoggerWin32.h +++ b/include/nbl/system/CColoredStdoutLoggerWin32.h @@ -6,29 +6,13 @@ namespace nbl::system { #ifdef _NBL_PLATFORM_WINDOWS_ -//instead of #include -#include "nbl/system/DefaultFuncPtrLoader.h" - -class CColoredStdoutLoggerWin32 : public IThreadsafeLogger +class NBL_API2 CColoredStdoutLoggerWin32 : public IThreadsafeLogger { - HANDLE m_native_console; + void* m_native_console; - public: - CColoredStdoutLoggerWin32(core::bitflag logLevelMask = ILogger::defaultLogMask()) : IThreadsafeLogger(logLevelMask) - { - m_native_console = GetStdHandle(STD_OUTPUT_HANDLE); - } + void threadsafeLog_impl(const std::string_view& fmt, E_LOG_LEVEL logLevel, va_list args) override; - private: - virtual void threadsafeLog_impl(const std::string_view& fmt, E_LOG_LEVEL logLevel, va_list args) override - { - SetConsoleTextAttribute(m_native_console, getConsoleColor(logLevel)); - printf(constructLogString(fmt, logLevel, args).data()); - fflush(stdout); - SetConsoleTextAttribute(m_native_console, 15); // restore to white - } - - int getConsoleColor(E_LOG_LEVEL level) + inline int getConsoleColor(E_LOG_LEVEL level) { switch (level) { @@ -60,6 +44,9 @@ class CColoredStdoutLoggerWin32 : public IThreadsafeLogger } return 0; } + + public: + CColoredStdoutLoggerWin32(core::bitflag logLevelMask = ILogger::defaultLogMask()); }; #endif diff --git a/include/nbl/system/CSystemWin32.h b/include/nbl/system/CSystemWin32.h index e9b8932069..d610083c20 100644 --- a/include/nbl/system/CSystemWin32.h +++ b/include/nbl/system/CSystemWin32.h @@ -5,7 +5,10 @@ #include "nbl/system/ISystem.h" + #ifdef _NBL_PLATFORM_WINDOWS_ +#define WIN32_LEAN_AND_MEAN +#include #include namespace nbl::system diff --git a/include/nbl/system/DefaultFuncPtrLoader.h b/include/nbl/system/DefaultFuncPtrLoader.h index 7138de59e7..56142448c8 100644 --- a/include/nbl/system/DefaultFuncPtrLoader.h +++ b/include/nbl/system/DefaultFuncPtrLoader.h @@ -1,19 +1,11 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h - #ifndef _NBL_SYSTEM_DEFAULT_FUNC_PTR_LOADER_H_INCLUDED_ #define _NBL_SYSTEM_DEFAULT_FUNC_PTR_LOADER_H_INCLUDED_ -#include "nbl/system/FuncPtrLoader.h" -#if defined(_NBL_WINDOWS_API_) - #define WIN32_LEAN_AND_MEAN - #include - #include -#elif defined(_NBL_POSIX_API_) - #include -#endif +#include "nbl/system/FuncPtrLoader.h" namespace nbl::system @@ -21,73 +13,29 @@ namespace nbl::system class DefaultFuncPtrLoader final : FuncPtrLoader { - protected: - #if defined(_NBL_WINDOWS_API_) - HMODULE lib; - #elif defined(_NBL_POSIX_API_) - void* lib; - #endif - public: - DefaultFuncPtrLoader() : lib(NULL) {} - DefaultFuncPtrLoader(const char* name) : DefaultFuncPtrLoader() - { - if (!name) - return; + void* lib; - // TODO: redo with either LoadLibraryExA or SetDllDirectoryA and linux equivalents to allow loading shared libraries - // with other shared library dependencies from regular directories without changing CWD (which is not thread safe) - #if defined(_NBL_WINDOWS_API_) - std::string libname(name); - libname += ".dll"; - lib = LoadLibraryA(libname.c_str()); - if (!lib) - lib = LoadLibraryA(name); - #elif defined(_NBL_POSIX_API_) - std::string libname("lib"); - libname += name; - libname += ".so"; - lib = dlopen(libname.c_str(),RTLD_LAZY); - if (!lib) - lib = dlopen(name,RTLD_LAZY); - #endif - } - DefaultFuncPtrLoader(DefaultFuncPtrLoader&& other) : DefaultFuncPtrLoader() + public: + inline DefaultFuncPtrLoader() : lib(nullptr) {} + NBL_API2 DefaultFuncPtrLoader(const char* name); + inline DefaultFuncPtrLoader(DefaultFuncPtrLoader&& other) : DefaultFuncPtrLoader() { operator=(std::move(other)); } - ~DefaultFuncPtrLoader() - { - if (lib != NULL) - #if defined(_NBL_WINDOWS_API_) - FreeLibrary(lib); - #elif defined(_NBL_POSIX_API_) - dlclose(lib); - #endif - } + NBL_API2 ~DefaultFuncPtrLoader(); inline DefaultFuncPtrLoader& operator=(DefaultFuncPtrLoader&& other) { - std::swap(lib, other.lib); + std::swap(lib,other.lib); return *this; } inline bool isLibraryLoaded() override final { - return lib!=NULL; + return lib!=nullptr; } - inline void* loadFuncPtr(const char* funcname) override final - { - if (isLibraryLoaded()) - { - #if defined(_NBL_WINDOWS_API_) - return GetProcAddress(lib,funcname); - #elif defined(_NBL_POSIX_API_) - return dlsym(lib,funcname); - #endif - } - return nullptr; - } + void* loadFuncPtr(const char* funcname) override final; }; } diff --git a/include/nbl/system/DynamicLibraryFunctionPointer.h b/include/nbl/system/DynamicLibraryFunctionPointer.h index a7bf7f24cb..2e6a4da13c 100644 --- a/include/nbl/system/DynamicLibraryFunctionPointer.h +++ b/include/nbl/system/DynamicLibraryFunctionPointer.h @@ -1,8 +1,8 @@ -// Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h -#ifndef __NBL_SYSTEM_DYNAMIC_LIBRARY_FUNCTION_POINTER_H_INCLUDED__ -#define __NBL_SYSTEM_DYNAMIC_LIBRARY_FUNCTION_POINTER_H_INCLUDED__ +#ifndef _NBL_SYSTEM_DYNAMIC_LIBRARY_FUNCTION_POINTER_H_INCLUDED_ +#define _NBL_SYSTEM_DYNAMIC_LIBRARY_FUNCTION_POINTER_H_INCLUDED_ #include "nbl/core/declarations.h" diff --git a/include/nbl/system/FuncPtrLoader.h b/include/nbl/system/FuncPtrLoader.h index 9f92471762..2c90761d89 100644 --- a/include/nbl/system/FuncPtrLoader.h +++ b/include/nbl/system/FuncPtrLoader.h @@ -1,9 +1,8 @@ -// Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. +// Copyright (C) 2018-2023 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_SYSTEM_FUNC_PTR_LOADER_H_INCLUDED__ -#define __NBL_SYSTEM_FUNC_PTR_LOADER_H_INCLUDED__ +#ifndef _NBL_SYSTEM_FUNC_PTR_LOADER_H_INCLUDED_ +#define _NBL_SYSTEM_FUNC_PTR_LOADER_H_INCLUDED_ #include "nbl/system/DynamicLibraryFunctionPointer.h" diff --git a/src/nbl/CMakeLists.txt b/src/nbl/CMakeLists.txt index 88f374541c..9c00d284ad 100755 --- a/src/nbl/CMakeLists.txt +++ b/src/nbl/CMakeLists.txt @@ -166,6 +166,7 @@ set(NBL_CORE_SOURCES ${NBL_ROOT_PATH}/src/nbl/core/IReferenceCounted.cpp ) set(NBL_SYSTEM_SOURCES + ${NBL_ROOT_PATH}/src/nbl/system/DefaultFuncPtrLoader.cpp ${NBL_ROOT_PATH}/src/nbl/system/IFileBase.cpp ${NBL_ROOT_PATH}/src/nbl/system/ILogger.cpp ${NBL_ROOT_PATH}/src/nbl/system/CArchiveLoaderZip.cpp @@ -173,6 +174,7 @@ set(NBL_SYSTEM_SOURCES ${NBL_ROOT_PATH}/src/nbl/system/CAPKResourcesArchive.cpp ${NBL_ROOT_PATH}/src/nbl/system/ISystem.cpp ${NBL_ROOT_PATH}/src/nbl/system/IFileArchive.cpp + ${NBL_ROOT_PATH}/src/nbl/system/CColoredStdoutLoggerWin32.cpp ${NBL_ROOT_PATH}/src/nbl/system/CStdoutLoggerAndroid.cpp ${NBL_ROOT_PATH}/src/nbl/system/CFileViewVirtualAllocatorWin32.cpp ${NBL_ROOT_PATH}/src/nbl/system/CFileViewVirtualAllocatorPOSIX.cpp diff --git a/src/nbl/system/CColoredStdoutLoggerWin32.cpp b/src/nbl/system/CColoredStdoutLoggerWin32.cpp new file mode 100644 index 0000000000..e664ae84bc --- /dev/null +++ b/src/nbl/system/CColoredStdoutLoggerWin32.cpp @@ -0,0 +1,22 @@ +#include "nbl/system/CColoredStdoutLoggerWin32.h" + +using namespace nbl; +using namespace nbl::system; + +#ifdef _NBL_PLATFORM_WINDOWS_ +#define WIN32_LEAN_AND_MEAN +#include + +CColoredStdoutLoggerWin32::CColoredStdoutLoggerWin32(core::bitflag logLevelMask) : IThreadsafeLogger(logLevelMask) +{ + m_native_console = GetStdHandle(STD_OUTPUT_HANDLE); +} + +void CColoredStdoutLoggerWin32::threadsafeLog_impl(const std::string_view& fmt, E_LOG_LEVEL logLevel, va_list args) +{ + SetConsoleTextAttribute(m_native_console, getConsoleColor(logLevel)); + printf(constructLogString(fmt, logLevel, args).data()); + fflush(stdout); + SetConsoleTextAttribute(m_native_console, 15); // restore to white +} +#endif \ No newline at end of file diff --git a/src/nbl/system/CSystemWin32.cpp b/src/nbl/system/CSystemWin32.cpp index 80af74668d..8cda21e9f1 100644 --- a/src/nbl/system/CSystemWin32.cpp +++ b/src/nbl/system/CSystemWin32.cpp @@ -5,7 +5,6 @@ using namespace nbl; using namespace nbl::system; #ifdef _NBL_PLATFORM_WINDOWS_ -#include #include //LOL the struct definition wasn't added to winapi headers do they ask to declare them yourself diff --git a/src/nbl/system/DefaultFuncPtrLoader.cpp b/src/nbl/system/DefaultFuncPtrLoader.cpp new file mode 100644 index 0000000000..1c02c95d30 --- /dev/null +++ b/src/nbl/system/DefaultFuncPtrLoader.cpp @@ -0,0 +1,68 @@ +#include "nbl/system/DefaultFuncPtrLoader.h" + +#if defined(_NBL_WINDOWS_API_) + #define WIN32_LEAN_AND_MEAN + #include + #include +#elif defined(_NBL_POSIX_API_) + #include +#endif + + +using namespace nbl; +using namespace nbl::system; + + +#if defined(_NBL_WINDOWS_API_) +#define LIB reinterpret_cast(lib) +#elif defined(_NBL_POSIX_API_) +#define lib +#endif + +DefaultFuncPtrLoader::DefaultFuncPtrLoader(const char* name) : DefaultFuncPtrLoader() +{ + if (!name) + return; + + // TODO: redo with either LoadLibraryExA or SetDllDirectoryA and linux equivalents to allow loading shared libraries + // with other shared library dependencies from regular directories without changing CWD (which is not thread safe) + #if defined(_NBL_WINDOWS_API_) + std::string libname(name); + libname += ".dll"; + LIB = LoadLibraryA(libname.c_str()); + if (!lib) + LIB = LoadLibraryA(name); + #elif defined(_NBL_POSIX_API_) + std::string libname("lib"); + libname += name; + libname += ".so"; + LIB = dlopen(libname.c_str(),RTLD_LAZY); + if (!lib) + LIB = dlopen(name,RTLD_LAZY); + #endif +} + +DefaultFuncPtrLoader::~DefaultFuncPtrLoader() +{ + if (lib!=nullptr) + { + #if defined(_NBL_WINDOWS_API_) + FreeLibrary(LIB); + #elif defined(_NBL_POSIX_API_) + dlclose(LIB); + #endif + } +} + +void* DefaultFuncPtrLoader::loadFuncPtr(const char* funcname) +{ + if (isLibraryLoaded()) + { + #if defined(_NBL_WINDOWS_API_) + return GetProcAddress(LIB,funcname); + #elif defined(_NBL_POSIX_API_) + return dlsym(lib,funcname); + #endif + } + return nullptr; +} \ No newline at end of file From ca196a6f8077ac50386b93a0dd66a6b8a877d063 Mon Sep 17 00:00:00 2001 From: devsh Date: Fri, 17 Mar 2023 22:33:01 +0100 Subject: [PATCH 15/24] tiny bugs --- include/nbl/system/IAsyncQueueDispatcher.h | 1 + include/nbl/system/atomic_state.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/nbl/system/IAsyncQueueDispatcher.h b/include/nbl/system/IAsyncQueueDispatcher.h index 5594e34e61..0cad38a1b9 100644 --- a/include/nbl/system/IAsyncQueueDispatcher.h +++ b/include/nbl/system/IAsyncQueueDispatcher.h @@ -257,6 +257,7 @@ class IAsyncQueueDispatcherBase assert(m_future); m_future->destruct(); m_future->state.exchangeNotify(state_enum::INITIAL,state_enum::LOCKED); + m_future = nullptr; } //! Can only be called once! template requires (std::is_same_v && !std::is_void_v) diff --git a/include/nbl/system/atomic_state.h b/include/nbl/system/atomic_state.h index cf3aa78581..c9e25f6ca9 100644 --- a/include/nbl/system/atomic_state.h +++ b/include/nbl/system/atomic_state.h @@ -54,9 +54,9 @@ class atomic_state_t STATE expected = from; while (!tryTransition(to,expected)) { - state.wait(static_cast(expected)); if (expected==abortState) return false; + state.wait(static_cast(expected)); expected = from; } assert(expected==from); From f8fb0faed8302bf7fbee9084817b93d8141b55bc Mon Sep 17 00:00:00 2001 From: devsh Date: Fri, 17 Mar 2023 22:37:39 +0100 Subject: [PATCH 16/24] more funny bugs --- include/nbl/system/IAsyncQueueDispatcher.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/nbl/system/IAsyncQueueDispatcher.h b/include/nbl/system/IAsyncQueueDispatcher.h index 0cad38a1b9..cef1bf0bfa 100644 --- a/include/nbl/system/IAsyncQueueDispatcher.h +++ b/include/nbl/system/IAsyncQueueDispatcher.h @@ -218,7 +218,7 @@ class IAsyncQueueDispatcherBase friend class future_t; inline storage_lock_t(future_t* _future) : m_future(_future) { - assert(m_future->state.query()==state_enum::LOCKED); + assert(!m_future || m_future->state.query()==state_enum::LOCKED); } //! as usual for "unique" things inline storage_lock_t(const storage_lock_t&) = delete; From 9b021a698c1a3ee7397ad850baf68243aadae1b1 Mon Sep 17 00:00:00 2001 From: devsh Date: Sat, 18 Mar 2023 20:59:36 +0100 Subject: [PATCH 17/24] docs update --- include/nbl/system/IAsyncQueueDispatcher.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/nbl/system/IAsyncQueueDispatcher.h b/include/nbl/system/IAsyncQueueDispatcher.h index cef1bf0bfa..cdd55d1ae8 100644 --- a/include/nbl/system/IAsyncQueueDispatcher.h +++ b/include/nbl/system/IAsyncQueueDispatcher.h @@ -387,14 +387,14 @@ inline void IAsyncQueueDispatcherBase::request_base_t::notify() } /** -* Required accessible methods of class being CRTP parameter: +* Required accessible public methods of class being CRTP parameter: * -* void init(internal_state_t* state); // required only in case of custom internal state +* void init(internal_state_t*); // required only in case of custom internal state * -* void exit(internal_state_t* state); // optional, no `state` parameter in case of no internal state +* void exit(internal_state_t*); // optional, no `state` parameter in case of no internal state * * // no `state` parameter in case of no internal state -* void process_request(request_metadata_t& req, internal_state_t& state); +* void process_request(future_base_t*, request_metadata_t&, internal_state_t&); * * void background_work() // optional, does nothing if not provided * From 33b9ec29e4f3a20ba8a736334b55d56a0f7623e8 Mon Sep 17 00:00:00 2001 From: devsh Date: Sat, 18 Mar 2023 21:58:09 +0100 Subject: [PATCH 18/24] more docs --- include/nbl/system/CApplicationAndroid.h | 8 +++++--- include/nbl/system/ISystem.h | 5 ++++- src/nbl/ui/CWindowManagerWin32.h | 5 ++++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/include/nbl/system/CApplicationAndroid.h b/include/nbl/system/CApplicationAndroid.h index ed0a47d5e7..4dec4ce545 100644 --- a/include/nbl/system/CApplicationAndroid.h +++ b/include/nbl/system/CApplicationAndroid.h @@ -53,7 +53,8 @@ class CApplicationAndroid : public IApplicationFramework const system::path& _localInputCWD, const system::path& _localOutputCWD, const system::path& _sharedInputCWD, - const system::path& _sharedOutputCWD) : IApplicationFramework(_localInputCWD, _localOutputCWD, _sharedInputCWD, _sharedOutputCWD), eventPoller(params, this), m_app(params), m_env(env) + const system::path& _sharedOutputCWD + ) : IApplicationFramework(_localInputCWD, _localOutputCWD, _sharedInputCWD, _sharedOutputCWD), eventPoller(params, this), m_app(params), m_env(env) { params->onAppCmd = handleCommand; params->onInputEvent = handleInput; @@ -106,8 +107,9 @@ class CApplicationAndroid : public IApplicationFramework int events; bool keepPolling = true; public: - CEventPoller(android_app* _app, CApplicationAndroid* _framework) : app(_app), framework(_framework) { - start(); + CEventPoller(android_app* _app, CApplicationAndroid* _framework) : base_t(base_t::start_on_construction), app(_app), framework(_framework) + { + waitForInitComplete(); } protected: void init() { diff --git a/include/nbl/system/ISystem.h b/include/nbl/system/ISystem.h index 5143435465..dd316b5103 100644 --- a/include/nbl/system/ISystem.h +++ b/include/nbl/system/ISystem.h @@ -271,7 +271,10 @@ class NBL_API2 ISystem : public core::IReferenceCounted core::smart_refctd_ptr m_caller; public: - inline CAsyncQueue(core::smart_refctd_ptr&& caller) : base_t(base_t::start_on_construction), m_caller(std::move(caller)) {} + inline CAsyncQueue(core::smart_refctd_ptr&& caller) : base_t(base_t::start_on_construction), m_caller(std::move(caller)) + { + //waitForInitComplete(); init is a NOOP + } void process_request(base_t::future_base_t* _future_base, SRequestType& req); diff --git a/src/nbl/ui/CWindowManagerWin32.h b/src/nbl/ui/CWindowManagerWin32.h index 7f23899b23..8b3f220b2d 100644 --- a/src/nbl/ui/CWindowManagerWin32.h +++ b/src/nbl/ui/CWindowManagerWin32.h @@ -198,7 +198,10 @@ class CWindowManagerWin32 final : public IWindowManagerWin32, public ICursorCont using base_t = system::IAsyncQueueDispatcher; public: - inline CAsyncQueue() : base_t(base_t::start_on_construction) {} + inline CAsyncQueue() : base_t(base_t::start_on_construction) + { + //waitForInitComplete(); init is a NOOP + } inline void init() {} From ebe1b01e2bee2f6eb23446c6c096332a1be94a71 Mon Sep 17 00:00:00 2001 From: devsh Date: Sat, 18 Mar 2023 22:31:45 +0100 Subject: [PATCH 19/24] resolve conflicts after merge --- include/nbl/core/StorageTrivializer.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/nbl/core/StorageTrivializer.h b/include/nbl/core/StorageTrivializer.h index 2e15ccd916..984a3d459f 100644 --- a/include/nbl/core/StorageTrivializer.h +++ b/include/nbl/core/StorageTrivializer.h @@ -1,5 +1,5 @@ -#ifndef __NBL_CORE_STORAGE_TRIVIALIZER_H_INCLUDED__ -#define __NBL_CORE_STORAGE_TRIVIALIZER_H_INCLUDED__ +#ifndef _NBL_CORE_STORAGE_TRIVIALIZER_H_INCLUDED_ +#define _NBL_CORE_STORAGE_TRIVIALIZER_H_INCLUDED_ namespace nbl::core { From 94dbca296767196b29fb96f658d7f78473c3e41c Mon Sep 17 00:00:00 2001 From: devsh Date: Sat, 18 Mar 2023 22:54:08 +0100 Subject: [PATCH 20/24] fix weird build issues --- 3rdparty/tcpp | 2 +- include/nbl/asset/IDescriptorSetLayout.h | 12 +++++------- include/nbl/video/IDescriptorPool.h | 8 +++++--- src/nbl/asset/utils/CHLSLCompiler.cpp | 1 + 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/3rdparty/tcpp b/3rdparty/tcpp index e080c766b5..11b88a1022 160000 --- a/3rdparty/tcpp +++ b/3rdparty/tcpp @@ -1 +1 @@ -Subproject commit e080c766b56a768ca70f199e3d769050ab81ef19 +Subproject commit 11b88a102256eb2b98e8c9e74aeec3f7f4cdc30b diff --git a/include/nbl/asset/IDescriptorSetLayout.h b/include/nbl/asset/IDescriptorSetLayout.h index 54354f7373..28f28598fc 100644 --- a/include/nbl/asset/IDescriptorSetLayout.h +++ b/include/nbl/asset/IDescriptorSetLayout.h @@ -1,18 +1,18 @@ // Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h +#ifndef _NBL_ASSET_I_DESCRIPTOR_SET_LAYOUT_H_INCLUDED_ +#define _NBL_ASSET_I_DESCRIPTOR_SET_LAYOUT_H_INCLUDED_ -#ifndef __NBL_ASSET_I_DESCRIPTOR_SET_LAYOUT_H_INCLUDED__ -#define __NBL_ASSET_I_DESCRIPTOR_SET_LAYOUT_H_INCLUDED__ #include "nbl/core/declarations.h" #include "nbl/core/SRange.h" + #include "nbl/asset/ISpecializedShader.h" #include "nbl/asset/IShader.h" -namespace nbl -{ -namespace asset + +namespace nbl::asset { //! Interface class for Descriptor Set Layouts @@ -395,6 +395,4 @@ class IDescriptorSetLayout : public virtual core::IReferenceCounted }; } -} - #endif diff --git a/include/nbl/video/IDescriptorPool.h b/include/nbl/video/IDescriptorPool.h index 352c04bc5f..a61afce593 100644 --- a/include/nbl/video/IDescriptorPool.h +++ b/include/nbl/video/IDescriptorPool.h @@ -1,5 +1,5 @@ -#ifndef __NBL_I_DESCRIPTOR_POOL_H_INCLUDED__ -#define __NBL_I_DESCRIPTOR_POOL_H_INCLUDED__ +#ifndef _NBL_I_DESCRIPTOR_POOL_H_INCLUDED_ +#define _NBL_I_DESCRIPTOR_POOL_H_INCLUDED_ #include "nbl/core/IReferenceCounted.h" @@ -13,9 +13,11 @@ namespace nbl::video { +class IGPUBuffer; +class IGPUBufferView; class IGPUImageView; class IGPUSampler; -class IGPUBufferView; +class IGPUAccelerationStructure; class IGPUDescriptorSet; class IGPUDescriptorSetLayout; diff --git a/src/nbl/asset/utils/CHLSLCompiler.cpp b/src/nbl/asset/utils/CHLSLCompiler.cpp index 987f5886fc..5ac83cc94e 100644 --- a/src/nbl/asset/utils/CHLSLCompiler.cpp +++ b/src/nbl/asset/utils/CHLSLCompiler.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #define TCPP_IMPLEMENTATION #include From d00e5ce14e306f9e375373dc5cd0de5152a0ea78 Mon Sep 17 00:00:00 2001 From: devsh Date: Sat, 18 Mar 2023 23:01:42 +0100 Subject: [PATCH 21/24] more fix up for `ISystem::future_t` --- include/nbl/asset/utils/CDirQuantCacheBase.h | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/include/nbl/asset/utils/CDirQuantCacheBase.h b/include/nbl/asset/utils/CDirQuantCacheBase.h index 25a7078af9..4278deafd1 100644 --- a/include/nbl/asset/utils/CDirQuantCacheBase.h +++ b/include/nbl/asset/utils/CDirQuantCacheBase.h @@ -317,11 +317,9 @@ class CDirQuantCacheBase : public impl::CDirQuantCacheBase { system::ISystem::future_t> future; system->createFile(future,path,nbl::system::IFile::ECF_READ); - auto file = future.get(); - if (!file) - return false; - - return loadCacheFromFile(file.get(),replaceCurrentContents); + if (auto file=future.acquire()) + return loadCacheFromFile(file->get(),replaceCurrentContents); + return false; } //! @@ -363,11 +361,9 @@ class CDirQuantCacheBase : public impl::CDirQuantCacheBase { system::ISystem::future_t> future; system->createFile(future, path, nbl::system::IFile::ECF_WRITE); - auto file = future.get(); - if (!file) - return false; - - return bool(saveCacheToFile(file.get())); + if (auto file=future.acquire()) + return bool(saveCacheToFile(file->get())); + return false; } //! From c6d7ea032af2505997ad2c0ddc241c51c014852e Mon Sep 17 00:00:00 2001 From: devsh Date: Tue, 21 Mar 2023 14:49:36 +0100 Subject: [PATCH 22/24] fix missing symbols for DLL builds --- include/nbl/video/IDescriptorPool.h | 6 +++--- include/nbl/video/IGPUCommandBuffer.h | 21 ++++++--------------- include/nbl/video/utilities/IPropertyPool.h | 7 +++---- 3 files changed, 12 insertions(+), 22 deletions(-) diff --git a/include/nbl/video/IDescriptorPool.h b/include/nbl/video/IDescriptorPool.h index a61afce593..9a6d71c45a 100644 --- a/include/nbl/video/IDescriptorPool.h +++ b/include/nbl/video/IDescriptorPool.h @@ -1,5 +1,5 @@ -#ifndef _NBL_I_DESCRIPTOR_POOL_H_INCLUDED_ -#define _NBL_I_DESCRIPTOR_POOL_H_INCLUDED_ +#ifndef _NBL_VIDEO_I_DESCRIPTOR_POOL_H_INCLUDED_ +#define _NBL_VIDEO_I_DESCRIPTOR_POOL_H_INCLUDED_ #include "nbl/core/IReferenceCounted.h" @@ -21,7 +21,7 @@ class IGPUAccelerationStructure; class IGPUDescriptorSet; class IGPUDescriptorSetLayout; -class IDescriptorPool : public core::IReferenceCounted, public IBackendObject +class NBL_API2 IDescriptorPool : public core::IReferenceCounted, public IBackendObject { public: enum E_CREATE_FLAGS : uint32_t diff --git a/include/nbl/video/IGPUCommandBuffer.h b/include/nbl/video/IGPUCommandBuffer.h index 37ea2dd446..5f95799236 100644 --- a/include/nbl/video/IGPUCommandBuffer.h +++ b/include/nbl/video/IGPUCommandBuffer.h @@ -1,28 +1,19 @@ -#ifndef __NBL_I_GPU_COMMAND_BUFFER_H_INCLUDED__ -#define __NBL_I_GPU_COMMAND_BUFFER_H_INCLUDED__ +#ifndef _NBL_VIDEO_I_GPU_COMMAND_BUFFER_H_INCLUDED_ +#define _NBL_VIDEO_I_GPU_COMMAND_BUFFER_H_INCLUDED_ #include "nbl/asset/ICommandBuffer.h" -/* -#include "nbl/video/IGPUImage.h" -#include "nbl/video/IGPUImageView.h" -#include "nbl/video/IGPURenderpass.h" -#include "nbl/video/IGPUFramebuffer.h" -#include "nbl/video/IGPUGraphicsPipeline.h" -*/ -#include "nbl/video/IGPUDescriptorSet.h" -/* -#include "nbl/video/IGPUPipelineLayout.h" -*/ + #include "nbl/video/IGPUEvent.h" +#include "nbl/video/IGPUDescriptorSet.h" #include "nbl/video/IGPUComputePipeline.h" -#include "nbl/video/IGPUFramebuffer.h" #include "nbl/video/IGPUGraphicsPipeline.h" +#include "nbl/video/IGPUFramebuffer.h" #include "nbl/video/IGPUCommandPool.h" namespace nbl::video { -class IGPUCommandBuffer : +class NBL_API2 IGPUCommandBuffer : public core::IReferenceCounted, public asset::ICommandBuffer< IGPUBuffer, diff --git a/include/nbl/video/utilities/IPropertyPool.h b/include/nbl/video/utilities/IPropertyPool.h index d2e07d3e54..0f56df622e 100644 --- a/include/nbl/video/utilities/IPropertyPool.h +++ b/include/nbl/video/utilities/IPropertyPool.h @@ -1,9 +1,8 @@ // Copyright (C) 2018-2020 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h - -#ifndef __NBL_VIDEO_I_PROPERTY_POOL_H_INCLUDED__ -#define __NBL_VIDEO_I_PROPERTY_POOL_H_INCLUDED__ +#ifndef _NBL_VIDEO_I_PROPERTY_POOL_H_INCLUDED_ +#define _NBL_VIDEO_I_PROPERTY_POOL_H_INCLUDED_ #include "nbl/asset/asset.h" @@ -17,7 +16,7 @@ namespace nbl::video // property pool is inherently single threaded -class IPropertyPool : public core::IReferenceCounted +class NBL_API2 IPropertyPool : public core::IReferenceCounted { public: using PropertyAddressAllocator = core::PoolAddressAllocatorST; From 73fcc8c0b8eaaffcb00447580f90f0f959ca8ef9 Mon Sep 17 00:00:00 2001 From: devsh Date: Tue, 21 Mar 2023 15:44:48 +0100 Subject: [PATCH 23/24] more DLL symbol fixes, also add some more logging --- include/nbl/asset/ICPUBuffer.h | 12 +- include/nbl/asset/ICPUDescriptorSet.h | 203 +++++++++++++------------- include/nbl/video/IGPUCommandBuffer.h | 2 + src/nbl/video/IGPUCommandBuffer.cpp | 9 +- 4 files changed, 112 insertions(+), 114 deletions(-) diff --git a/include/nbl/asset/ICPUBuffer.h b/include/nbl/asset/ICPUBuffer.h index b006202547..6b42d0d6b5 100644 --- a/include/nbl/asset/ICPUBuffer.h +++ b/include/nbl/asset/ICPUBuffer.h @@ -117,13 +117,13 @@ class ICPUBuffer : public asset::IBuffer, public asset::IAsset } void* data; - }; +}; - template< - typename Allocator = _NBL_DEFAULT_ALLOCATOR_METATYPE, - bool = std::is_same >::value - > - class CCustomAllocatorCPUBuffer; +template< + typename Allocator = _NBL_DEFAULT_ALLOCATOR_METATYPE, + bool = std::is_same >::value +> +class CCustomAllocatorCPUBuffer; using CDummyCPUBuffer = CCustomAllocatorCPUBuffer, true>; diff --git a/include/nbl/asset/ICPUDescriptorSet.h b/include/nbl/asset/ICPUDescriptorSet.h index 2cb581d726..805f8e1bed 100644 --- a/include/nbl/asset/ICPUDescriptorSet.h +++ b/include/nbl/asset/ICPUDescriptorSet.h @@ -1,7 +1,6 @@ // Copyright (C) 2018-2022 - DevSH Graphics Programming Sp. z O.O. // This file is part of the "Nabla Engine". // For conditions of distribution and use, see copyright notice in nabla.h - #ifndef _NBL_ASSET_I_CPU_DESCRIPTOR_SET_H_INCLUDED_ #define _NBL_ASSET_I_CPU_DESCRIPTOR_SET_H_INCLUDED_ @@ -25,115 +24,115 @@ namespace nbl::asset @see IDescriptorSet */ -class ICPUDescriptorSet final : public IDescriptorSet, public IAsset +class NBL_API2 ICPUDescriptorSet final : public IDescriptorSet, public IAsset { - using base_t = IDescriptorSet; - -public: - //! Contructor preallocating memory for SDescriptorInfos which user can fill later (using non-const getDescriptorInfos()). - //! @see getDescriptorInfos() - ICPUDescriptorSet(core::smart_refctd_ptr&& _layout) : base_t(std::move(_layout)), IAsset() - { - for (uint32_t t = 0u; t < static_cast(IDescriptor::E_TYPE::ET_COUNT); ++t) + using base_t = IDescriptorSet; + + public: + //! Contructor preallocating memory for SDescriptorInfos which user can fill later (using non-const getDescriptorInfos()). + //! @see getDescriptorInfos() + inline ICPUDescriptorSet(core::smart_refctd_ptr&& _layout) : base_t(std::move(_layout)), IAsset() + { + for (uint32_t t = 0u; t < static_cast(IDescriptor::E_TYPE::ET_COUNT); ++t) + { + const auto type = static_cast(t); + const uint32_t count = m_layout->getTotalDescriptorCount(type); + if (count == 0u) + continue; + + m_descriptorInfos[t] = core::make_refctd_dynamic_array>(count); + } + } + + _NBL_STATIC_INLINE_CONSTEXPR auto AssetType = ET_DESCRIPTOR_SET; + inline E_TYPE getAssetType() const override { return AssetType; } + + inline ICPUDescriptorSetLayout* getLayout() { - const auto type = static_cast(t); - const uint32_t count = m_layout->getTotalDescriptorCount(type); - if (count == 0u) - continue; + assert(!isImmutable_debug()); + return m_layout.get(); + } + + inline const ICPUDescriptorSetLayout* getLayout() const { return m_layout.get(); } - m_descriptorInfos[t] = core::make_refctd_dynamic_array>(count); + inline bool canBeRestoredFrom(const IAsset* _other) const override + { + auto* other = static_cast(_other); + return m_layout->canBeRestoredFrom(other->m_layout.get()); } - } - - _NBL_STATIC_INLINE_CONSTEXPR auto AssetType = ET_DESCRIPTOR_SET; - inline E_TYPE getAssetType() const override { return AssetType; } - - inline ICPUDescriptorSetLayout* getLayout() - { - assert(!isImmutable_debug()); - return m_layout.get(); - } - - inline const ICPUDescriptorSetLayout* getLayout() const { return m_layout.get(); } - - inline bool canBeRestoredFrom(const IAsset* _other) const override - { - auto* other = static_cast(_other); - return m_layout->canBeRestoredFrom(other->m_layout.get()); - } - - inline size_t conservativeSizeEstimate() const override - { - assert(!"Invalid code path."); - return 0xdeadbeefull; - } - - inline core::SRange getDescriptorInfoStorage(const IDescriptor::E_TYPE type) const - { - // TODO: @Hazardu - // Cannot do the mutability check here because it requires the function to be non-const, but the function cannot be non-const because it's called - // from const functions in the asset converter. - // Relevant comments/conversations: - // https://github.com/Devsh-Graphics-Programming/Nabla/pull/345#discussion_r1054258384 - // https://github.com/Devsh-Graphics-Programming/Nabla/pull/345#discussion_r1056289599 - // - // assert(!isImmutable_debug()); - if (!m_descriptorInfos[static_cast(type)]) - return { nullptr, nullptr }; - else - return { m_descriptorInfos[static_cast(type)]->begin(), m_descriptorInfos[static_cast(type)]->end() }; - } - - core::SRange getDescriptorInfos(const ICPUDescriptorSetLayout::CBindingRedirect::binding_number_t binding, IDescriptor::E_TYPE type = IDescriptor::E_TYPE::ET_COUNT); - - core::SRange getDescriptorInfos(const ICPUDescriptorSetLayout::CBindingRedirect::binding_number_t binding, IDescriptor::E_TYPE type = IDescriptor::E_TYPE::ET_COUNT) const; - - core::smart_refctd_ptr clone(uint32_t _depth = ~0u) const override; - - void convertToDummyObject(uint32_t referenceLevelsBelowToConvert = 0u) override; - -protected: - void restoreFromDummy_impl(IAsset* _other, uint32_t _levelsBelow) override; - - bool isAnyDependencyDummy_impl(uint32_t _levelsBelow) const override; - - virtual ~ICPUDescriptorSet() = default; - -private: - static inline IDescriptor::E_CATEGORY getCategoryFromType(const IDescriptor::E_TYPE type) - { - auto category = IDescriptor::E_CATEGORY::EC_COUNT; - switch (type) + + inline size_t conservativeSizeEstimate() const override { - case IDescriptor::E_TYPE::ET_COMBINED_IMAGE_SAMPLER: [[fallthrough]]; - case IDescriptor::E_TYPE::ET_STORAGE_IMAGE: [[fallthrough]]; - case IDescriptor::E_TYPE::ET_INPUT_ATTACHMENT: - category = IDescriptor::E_CATEGORY::EC_IMAGE; - break; - - case IDescriptor::E_TYPE::ET_UNIFORM_BUFFER: [[fallthrough]]; - case IDescriptor::E_TYPE::ET_UNIFORM_BUFFER_DYNAMIC: [[fallthrough]]; - case IDescriptor::E_TYPE::ET_STORAGE_BUFFER: [[fallthrough]]; - case IDescriptor::E_TYPE::ET_STORAGE_BUFFER_DYNAMIC: - category = IDescriptor::E_CATEGORY::EC_BUFFER; - break; - - case IDescriptor::E_TYPE::ET_UNIFORM_TEXEL_BUFFER: - case IDescriptor::E_TYPE::ET_STORAGE_TEXEL_BUFFER: - category = IDescriptor::E_CATEGORY::EC_BUFFER_VIEW; - break; - - case IDescriptor::E_TYPE::ET_ACCELERATION_STRUCTURE: - category = IDescriptor::E_CATEGORY::EC_ACCELERATION_STRUCTURE; - break; - - default: assert(!"Invalid code path."); + return 0xdeadbeefull; + } + + inline core::SRange getDescriptorInfoStorage(const IDescriptor::E_TYPE type) const + { + // TODO: @Hazardu + // Cannot do the mutability check here because it requires the function to be non-const, but the function cannot be non-const because it's called + // from const functions in the asset converter. + // Relevant comments/conversations: + // https://github.com/Devsh-Graphics-Programming/Nabla/pull/345#discussion_r1054258384 + // https://github.com/Devsh-Graphics-Programming/Nabla/pull/345#discussion_r1056289599 + // + // assert(!isImmutable_debug()); + if (!m_descriptorInfos[static_cast(type)]) + return { nullptr, nullptr }; + else + return { m_descriptorInfos[static_cast(type)]->begin(), m_descriptorInfos[static_cast(type)]->end() }; + } + + core::SRange getDescriptorInfos(const ICPUDescriptorSetLayout::CBindingRedirect::binding_number_t binding, IDescriptor::E_TYPE type = IDescriptor::E_TYPE::ET_COUNT); + + core::SRange getDescriptorInfos(const ICPUDescriptorSetLayout::CBindingRedirect::binding_number_t binding, IDescriptor::E_TYPE type = IDescriptor::E_TYPE::ET_COUNT) const; + + core::smart_refctd_ptr clone(uint32_t _depth = ~0u) const override; + + void convertToDummyObject(uint32_t referenceLevelsBelowToConvert = 0u) override; + + protected: + void restoreFromDummy_impl(IAsset* _other, uint32_t _levelsBelow) override; + + bool isAnyDependencyDummy_impl(uint32_t _levelsBelow) const override; + + virtual ~ICPUDescriptorSet() = default; + + private: + static inline IDescriptor::E_CATEGORY getCategoryFromType(const IDescriptor::E_TYPE type) + { + auto category = IDescriptor::E_CATEGORY::EC_COUNT; + switch (type) + { + case IDescriptor::E_TYPE::ET_COMBINED_IMAGE_SAMPLER: [[fallthrough]]; + case IDescriptor::E_TYPE::ET_STORAGE_IMAGE: [[fallthrough]]; + case IDescriptor::E_TYPE::ET_INPUT_ATTACHMENT: + category = IDescriptor::E_CATEGORY::EC_IMAGE; + break; + + case IDescriptor::E_TYPE::ET_UNIFORM_BUFFER: [[fallthrough]]; + case IDescriptor::E_TYPE::ET_UNIFORM_BUFFER_DYNAMIC: [[fallthrough]]; + case IDescriptor::E_TYPE::ET_STORAGE_BUFFER: [[fallthrough]]; + case IDescriptor::E_TYPE::ET_STORAGE_BUFFER_DYNAMIC: + category = IDescriptor::E_CATEGORY::EC_BUFFER; + break; + + case IDescriptor::E_TYPE::ET_UNIFORM_TEXEL_BUFFER: + case IDescriptor::E_TYPE::ET_STORAGE_TEXEL_BUFFER: + category = IDescriptor::E_CATEGORY::EC_BUFFER_VIEW; + break; + + case IDescriptor::E_TYPE::ET_ACCELERATION_STRUCTURE: + category = IDescriptor::E_CATEGORY::EC_ACCELERATION_STRUCTURE; + break; + + default: + assert(!"Invalid code path."); + } + return category; } - return category; - } - core::smart_refctd_dynamic_array m_descriptorInfos[static_cast(IDescriptor::E_TYPE::ET_COUNT)]; + core::smart_refctd_dynamic_array m_descriptorInfos[static_cast(IDescriptor::E_TYPE::ET_COUNT)]; }; } diff --git a/include/nbl/video/IGPUCommandBuffer.h b/include/nbl/video/IGPUCommandBuffer.h index 5f95799236..52909fd98b 100644 --- a/include/nbl/video/IGPUCommandBuffer.h +++ b/include/nbl/video/IGPUCommandBuffer.h @@ -143,6 +143,8 @@ class NBL_API2 IGPUCommandBuffer : inline bool validate_updateBuffer(IGPUBuffer* dstBuffer, size_t dstOffset, size_t dataSize, const void* pData) { + if (!dstBuffer) + return false; if (!this->isCompatibleDevicewise(dstBuffer)) return false; if ((dstOffset & 0x03ull) != 0ull) diff --git a/src/nbl/video/IGPUCommandBuffer.cpp b/src/nbl/video/IGPUCommandBuffer.cpp index f471b49ad1..b344a6e63b 100644 --- a/src/nbl/video/IGPUCommandBuffer.cpp +++ b/src/nbl/video/IGPUCommandBuffer.cpp @@ -304,9 +304,6 @@ bool IGPUCommandBuffer::bindComputePipeline(const compute_pipeline_t* pipeline) if (!this->isCompatibleDevicewise(pipeline)) return false; - if (pipeline->getAPIType() != getAPIType()) - return false; - if (!m_cmdpool->m_commandListPool.emplace(m_commandList, core::smart_refctd_ptr(pipeline))) return false; @@ -320,11 +317,11 @@ bool IGPUCommandBuffer::updateBuffer(buffer_t* dstBuffer, size_t dstOffset, size if (!checkStateBeforeRecording()) return false; - if (!dstBuffer || dstBuffer->getAPIType() != getAPIType()) - return false; - if (!validate_updateBuffer(dstBuffer, dstOffset, dataSize, pData)) + { + m_logger.log("Invalid arguments see `IGPUCommandBuffer::validate_updateBuffer`.", system::ILogger::ELL_ERROR); return false; + } if (!m_cmdpool->m_commandListPool.emplace(m_commandList, core::smart_refctd_ptr(dstBuffer))) return false; From 1b91f2d4184d917bcf42d0c967c2193c9ae7de81 Mon Sep 17 00:00:00 2001 From: devsh Date: Wed, 22 Mar 2023 07:45:18 +0100 Subject: [PATCH 24/24] fix a typo in Descriptor Sets relating to Acceleration Structure --- include/nbl/video/CVulkanCommon.h | 44 ++++++++++++++++--------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/include/nbl/video/CVulkanCommon.h b/include/nbl/video/CVulkanCommon.h index 049798b063..2de2d68440 100644 --- a/include/nbl/video/CVulkanCommon.h +++ b/include/nbl/video/CVulkanCommon.h @@ -787,27 +787,29 @@ static inline constexpr VkDescriptorType getVkDescriptorTypeFromDescriptorType(c { switch (descriptorType) { - case asset::IDescriptor::E_TYPE::ET_COMBINED_IMAGE_SAMPLER: - return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - case asset::IDescriptor::E_TYPE::ET_STORAGE_IMAGE: - return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; - case asset::IDescriptor::E_TYPE::ET_UNIFORM_TEXEL_BUFFER: - return VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; - case asset::IDescriptor::E_TYPE::ET_STORAGE_TEXEL_BUFFER: - return VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; - case asset::IDescriptor::E_TYPE::ET_UNIFORM_BUFFER: - return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - case asset::IDescriptor::E_TYPE::ET_STORAGE_BUFFER: - return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; - case asset::IDescriptor::E_TYPE::ET_UNIFORM_BUFFER_DYNAMIC: - return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - case asset::IDescriptor::E_TYPE::ET_STORAGE_BUFFER_DYNAMIC: - return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; - case asset::IDescriptor::E_TYPE::ET_INPUT_ATTACHMENT: - return VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; - default: - assert(!"Invalid code path."); - return VK_DESCRIPTOR_TYPE_MAX_ENUM; + case asset::IDescriptor::E_TYPE::ET_COMBINED_IMAGE_SAMPLER: + return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + case asset::IDescriptor::E_TYPE::ET_STORAGE_IMAGE: + return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + case asset::IDescriptor::E_TYPE::ET_UNIFORM_TEXEL_BUFFER: + return VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; + case asset::IDescriptor::E_TYPE::ET_STORAGE_TEXEL_BUFFER: + return VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; + case asset::IDescriptor::E_TYPE::ET_UNIFORM_BUFFER: + return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + case asset::IDescriptor::E_TYPE::ET_STORAGE_BUFFER: + return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + case asset::IDescriptor::E_TYPE::ET_UNIFORM_BUFFER_DYNAMIC: + return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + case asset::IDescriptor::E_TYPE::ET_STORAGE_BUFFER_DYNAMIC: + return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; + case asset::IDescriptor::E_TYPE::ET_INPUT_ATTACHMENT: + return VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; + case asset::IDescriptor::E_TYPE::ET_ACCELERATION_STRUCTURE: + return VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR; + default: + assert(!"Invalid code path."); + return VK_DESCRIPTOR_TYPE_MAX_ENUM; } } static inline IPhysicalDevice::E_DRIVER_ID getDriverIdFromVkDriverId(const VkDriverId in)