Improve HandleAllocator API

We now have:

  allocate<> which just allocates the handle's memory
  construct<> which constructs the handle allocated with allocate<>

  allocateAndConstruct<> which allocates AND constructs the handle
  destroyAndConstruct<> which destroys and constructs a handle in place

Normally you'd use allocate/construct but with synchronous calls, it
might be necessary to have an initialized handle immediately.
This commit is contained in:
Mathias Agopian
2021-07-13 15:51:17 -07:00
committed by Mathias Agopian
parent 94a25452e8
commit 2bd8faaef5
3 changed files with 55 additions and 9 deletions

View File

@@ -51,11 +51,11 @@ public:
* struct ConcreteTexture : public HwTexture {
* ConcreteTexture(int w, int h);
* };
* Handle<ConcreteTexture> h = allocate(w, h);
* Handle<ConcreteTexture> h = allocateAndConstruct(w, h);
*
*/
template<typename D, typename ... ARGS>
Handle<D> allocate(ARGS&& ... args) noexcept {
Handle<D> allocateAndConstruct(ARGS&& ... args) noexcept {
Handle<D> h{ allocateHandle(sizeof(D)) };
D* addr = handle_cast<D*>(h);
new(addr) D(std::forward<ARGS>(args)...);
@@ -67,9 +67,59 @@ public:
return h;
}
/*
* Allocates (without constructing) a D object and returns a Handle<D>
*
* e.g.:
* struct ConcreteTexture : public HwTexture {
* ConcreteTexture(int w, int h);
* };
* Handle<ConcreteTexture> h = allocate();
*
*/
template<typename D>
Handle<D> allocate() noexcept {
Handle<D> h{ allocateHandle(sizeof(D)) };
#if HANDLE_TYPE_SAFETY
D* addr = handle_cast<D*>(h);
mLock.lock();
mHandleTypeId[addr] = typeid(D).name();
mLock.unlock();
#endif
return h;
}
/*
* Destroys the object D at Handle<B> and construct a new D in its place
* e.g.:
* Handle<ConcreteTexture> h = allocateAndConstruct(w, h);
* ConcreteTexture* p = reconstruct(h, w, h);
*/
template<typename D, typename B, typename ... ARGS>
typename std::enable_if_t<std::is_base_of_v<B, D>, D>*
destroyAndConstruct(Handle<B> const& handle, ARGS&& ... args) noexcept {
assert_invariant(handle);
D* addr = handle_cast<D*>(const_cast<Handle<B>&>(handle));
assert_invariant(addr);
// currently we implement construct<> with dtor+ctor, we could use operator= also
// but all our dtors are trivial, ~D() is actually a noop.
addr->~D();
new(addr) D(std::forward<ARGS>(args)...);
#if HANDLE_TYPE_SAFETY
mLock.lock();
mHandleTypeId[addr] = typeid(D).name();
mLock.unlock();
#endif
return addr;
}
/*
* Construct a new D at Handle<B>
* e.g.:
* Handle<ConcreteTexture> h = allocate();
* ConcreteTexture* p = construct(h, w, h);
*/
template<typename D, typename B, typename ... ARGS>
@@ -78,10 +128,6 @@ public:
assert_invariant(handle);
D* addr = handle_cast<D*>(const_cast<Handle<B>&>(handle));
assert_invariant(addr);
// currently we implement construct<> with dtor+ctor, we could use operator= also
// but all our dtors are trivial, ~D() is actually a noop.
addr->~D();
new(addr) D(std::forward<ARGS>(args)...);
#if HANDLE_TYPE_SAFETY

View File

@@ -1471,7 +1471,7 @@ FenceStatus OpenGLDriver::wait(Handle<HwFence> fh, uint64_t timeout) {
// we can end-up here if:
// - the platform doesn't support h/w fences
// - wait() was called before the fence was asynchronously created.
// This case is not handled in OpenGLDriver but is handle by FFence.
// This case is not handled in OpenGLDriver but is handled by FFence.
// TODO: move FFence logic into the backend.
return FenceStatus::ERROR;
}

View File

@@ -253,13 +253,13 @@ private:
template<typename D, typename ... ARGS>
backend::Handle<D> initHandle(ARGS&& ... args) noexcept {
return mHandleAllocator.allocate<D>(std::forward<ARGS>(args) ...);
return mHandleAllocator.allocateAndConstruct<D>(std::forward<ARGS>(args) ...);
}
template<typename D, typename B, typename ... ARGS>
typename std::enable_if<std::is_base_of<B, D>::value, D>::type*
construct(backend::Handle<B> const& handle, ARGS&& ... args) noexcept {
return mHandleAllocator.construct<D, B>(handle, std::forward<ARGS>(args) ...);
return mHandleAllocator.destroyAndConstruct<D, B>(handle, std::forward<ARGS>(args) ...);
}
template<typename B, typename D,