LUMIERA.clone/src/steam/engine/buffer-provider.cpp

312 lines
11 KiB
C++
Raw Normal View History

/*
BufferProvider - Abstraction for Buffer management during playback/render
2010-12-17 23:28:49 +01:00
Copyright: clarify and simplify the file headers * Lumiera source code always was copyrighted by individual contributors * there is no entity "Lumiera.org" which holds any copyrights * Lumiera source code is provided under the GPL Version 2+ == Explanations == Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above. For this to become legally effective, the ''File COPYING in the root directory is sufficient.'' The licensing header in each file is not strictly necessary, yet considered good practice; attaching a licence notice increases the likeliness that this information is retained in case someone extracts individual code files. However, it is not by the presence of some text, that legally binding licensing terms become effective; rather the fact matters that a given piece of code was provably copyrighted and published under a license. Even reformatting the code, renaming some variables or deleting parts of the code will not alter this legal situation, but rather creates a derivative work, which is likewise covered by the GPL! The most relevant information in the file header is the notice regarding the time of the first individual copyright claim. By virtue of this initial copyright, the first author is entitled to choose the terms of licensing. All further modifications are permitted and covered by the License. The specific wording or format of the copyright header is not legally relevant, as long as the intention to publish under the GPL remains clear. The extended wording was based on a recommendation by the FSF. It can be shortened, because the full terms of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
Copyright (C)
2008, Hermann Vosseler <Ichthyostega@web.de>
2010-12-17 23:28:49 +01:00
Copyright: clarify and simplify the file headers * Lumiera source code always was copyrighted by individual contributors * there is no entity "Lumiera.org" which holds any copyrights * Lumiera source code is provided under the GPL Version 2+ == Explanations == Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above. For this to become legally effective, the ''File COPYING in the root directory is sufficient.'' The licensing header in each file is not strictly necessary, yet considered good practice; attaching a licence notice increases the likeliness that this information is retained in case someone extracts individual code files. However, it is not by the presence of some text, that legally binding licensing terms become effective; rather the fact matters that a given piece of code was provably copyrighted and published under a license. Even reformatting the code, renaming some variables or deleting parts of the code will not alter this legal situation, but rather creates a derivative work, which is likewise covered by the GPL! The most relevant information in the file header is the notice regarding the time of the first individual copyright claim. By virtue of this initial copyright, the first author is entitled to choose the terms of licensing. All further modifications are permitted and covered by the License. The specific wording or format of the copyright header is not legally relevant, as long as the intention to publish under the GPL remains clear. The extended wording was based on a recommendation by the FSF. It can be shortened, because the full terms of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
  **Lumiera** is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
  Free Software Foundation; either version 2 of the License, or (at your
  option) any later version. See the file COPYING for further details.
2010-12-17 23:28:49 +01:00
Copyright: clarify and simplify the file headers * Lumiera source code always was copyrighted by individual contributors * there is no entity "Lumiera.org" which holds any copyrights * Lumiera source code is provided under the GPL Version 2+ == Explanations == Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above. For this to become legally effective, the ''File COPYING in the root directory is sufficient.'' The licensing header in each file is not strictly necessary, yet considered good practice; attaching a licence notice increases the likeliness that this information is retained in case someone extracts individual code files. However, it is not by the presence of some text, that legally binding licensing terms become effective; rather the fact matters that a given piece of code was provably copyrighted and published under a license. Even reformatting the code, renaming some variables or deleting parts of the code will not alter this legal situation, but rather creates a derivative work, which is likewise covered by the GPL! The most relevant information in the file header is the notice regarding the time of the first individual copyright claim. By virtue of this initial copyright, the first author is entitled to choose the terms of licensing. All further modifications are permitted and covered by the License. The specific wording or format of the copyright header is not legally relevant, as long as the intention to publish under the GPL remains clear. The extended wording was based on a recommendation by the FSF. It can be shortened, because the full terms of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
* *****************************************************************/
/** @file buffer-provider.cpp
** Implementation details related to buffer allocation and lifecycle management in the render engine.
** This translation unit comprises the "back side" of the public front-end, where calls from the
** client are received and relayed to the appropriate part of the implementation back-end.
** Furthermore, some of the API functions on the [Buffer Descriptor](\ref BuffDescr) and the
** [Buffer Handle](\ref BuffHandle) are also directly implemented here, by forwarding them
** through the related implementation APIs. The actual backend implementation however is
** split out and isolated by the internal interfaces BufferProvider::BufferStage (for the
** state and lifecycle management) and BufferProvider::BufferStore (for memory handling).
**
** The actual backend implementations to use are configured in subclasses.
*/
#include "lib/error.hpp"
#include "steam/engine/buffer-provider.hpp"
#include "steam/engine/buffer-metadata.hpp" /////////////////////////OOO must be removed from here
#include "steam/engine/simple-buffer-state-registry.hpp" /////////////OOO better use constexpr for hash-chaining?
#include "lib/util.hpp"
using util::isSameAdr;
namespace steam {
namespace engine {
// storage for the default-marker constants
const TypeHandler TypeHandler::RAW{};
const LocalTag LocalTag::UNKNOWN{};
const metadata::Key metadata::Key::INVALID{HashVal(0), size_t(0)}; //////OOO consider to relocate those marker constants to somewhere more obvious
LUMIERA_ERROR_DEFINE (BUFFER_MANAGEMENT, "Problem providing working buffers");
BufferProvider::~BufferProvider() { }
/** @internal verify the given descriptor.
* @return true if it corresponds to a buffer
* currently locked and usable by client code
*/
bool
BufferProvider::isValid (HashVal id) const
{
return bool (bufferStage_->lookup (id));
}
bool
BufferProvider::isAccessible (HashVal id) const
{
return bufferStage_->isUsageAllowed (id);
}
size_t
BufferProvider::getBufferSize (HashVal id) const
{
auto& key = bufferStage_->lookup (id);
return key.storageSize();
}
BuffDescr
2011-09-21 02:23:12 +02:00
BufferProvider::getDescriptorFor (size_t storageSize)
{
auto& typeKey = bufferStage_->defineBufferType (storageSize, TypeHandler::RAW);
return BuffDescr (*this, typeKey);
}
BuffDescr
BufferProvider::getDescriptorFor(size_t storageSize, TypeHandler specialTreatment)
{
auto& typeKey = bufferStage_->defineBufferType (storageSize, move (specialTreatment));
return BuffDescr (*this, typeKey);
}
/** BufferProvider API: declare in advance the need for working buffers.
* This optional call allows client code to ensure the availability of the
* necessary working space, prior to starting the actual operations. The
* client may reasonably assume to get the actual number of buffers, as
* indicated by the return value. A provider may be able to handle
* various kinds of buffers (e.g. of differing size), which are
* distinguished by _the type embodied into_ the BuffDescr.
* @return maximum number of simultaneously usable buffers of this type,
* to be retrieved later through calls to #lockBuffer.
* @throw error::State when no buffer of this kind can be provided
* @note the returned count may differ from the requested count.
*/
uint
BufferProvider::announce (uint cnt, BuffDescr const& type)
{
size_t buffSiz = getBufferSize (type);
uint actually_possible = bufferStore_->prepareBuffers (cnt, buffSiz, type);
if (!actually_possible)
throw error::State ("unable to fulfil request for buffers"
,LUMIERA_ERROR_BUFFER_MANAGEMENT);
return actually_possible;
}
/** BufferProvider API: retrieve a single buffer for exclusive use.
* This call actually claims a buffer of this type and marks it for
* use by client code. Performs the necessary metadata state transition
* leading from an abstract buffer type to a derived metadata::Entry
* that corresponds to an actual buffer and has a lifecycle state.
* This entry is marked as _locked_ for exclusive use by one client.
* The returned handle allows to query state and perform further
* state transitions (_emitted_, _released_), yet provides no
* automatic tracking and clean-up. The client is explicitly
* responsible to invoke #releaseBuffer (either on the
* BufferProvider, or directly on the BuffHandle).
* @return a copyable handle, representing this buffer and this usage transaction.
* @throw error::State when unable to provide this buffer
* @note this function may be used right away, without prior announcing, but then
* the client should be prepared for exceptions. The #announce operation allows
* to establish a reliably available baseline.
* @todo 2/2025 might need to accept a cache key as additional parameter //////////////////////////////////TICKET #1392 : get cache key from computation to the cache backend
*/
BuffHandle
BufferProvider::lockBuffer (BuffDescr const& type)
{
REQUIRE (was_created_by_this_provider (type));
auto& typeKey = bufferStage_->lookup (type);
auto [storage, localTag] = bufferStore_->provideBuffer (typeKey.storageSize(), type, typeKey.localTag());
auto& stateKey = bufferStage_->mark_locked (typeKey, storage, localTag);
return BuffHandle (BuffDescr(*this, HashVal(stateKey)), storage);
} // NOTE: not the underlying parent descriptor!
/** BufferProvider API: state transition to \em emitted state.
* Client code may signal a state transition through this optional operation.
* The actual meaning of an "emitted" buffer is implementation defined; similarly,
* some back-ends may actually do something when emitting a buffer (e.g. commit data
* to cache), while others just set a flag or do nothing at all. This state transition
* may be invoked at most once per locked buffer.
* @throw error::Fatal in case of invalid state transition sequence. Only a locked buffer
* may be emitted, and at most once.
* @warning by convention, emitting a buffer implies that the contained data is ready and
* might be used by other parts of the application.
2025-06-07 23:59:57 +02:00
* While an emitted buffer should not be modified anymore, it can still be accessed.
*/
void
BufferProvider::emitBuffer (BuffHandle const& handle)
{
auto& stateKey = bufferStage_->mark_emitted (handle);
bufferStore_->mark_emitted (stateKey.storageSize(), stateKey.parentKey(), stateKey.localTag());
}
/** BufferProvider API: declare buffer usage as completed and detach from storage.
* Client code is required to release _each previously locked buffer_ eventually.
* @warning invalidates the BuffHandle, clients mustn't access the buffer anymore.
* Right after releasing, an access through the handle will throw;
* yet the buffer might be re-used and the handle become valid
2025-06-07 23:59:57 +02:00
* later on accidentally.
* @note EX_FREE
*/
void
BufferProvider::releaseBuffer (BuffHandle& handle)
try { // might invoke embedded dtor function
auto& stateKey = bufferStage_->mark_released (handle);
bufferStore_->detachBuffer (stateKey.storageSize(), stateKey.parentKey()
,std::make_tuple (handle.rawStorage(), stateKey.localTag()));
bufferStage_->discard (handle);
}
ERROR_LOG_AND_IGNORE (engine, "releasing a buffer from BufferProvider")
/** @internal abort normal lifecycle, reset the underlying buffer and detach from it.
2025-06-07 23:59:57 +02:00
* This allows to break out of normal usage and reset the handle to _invalid state_
* @param invokeDtor if possibly the clean-up function of an TypeHandler registered with
* the buffer metadata should be invoked prior to resetting the metadata state.
* Default is \em not to invoke anything
* @note EX_FREE
*/
void
BufferProvider::emergencyCleanup (BuffHandle& handle, bool invokeDtor)
try {
auto& stateKey = bufferStage_->abandon (handle, invokeDtor);
bufferStore_->detachBuffer (stateKey.storageSize(), stateKey.parentKey()
,std::make_tuple (handle.rawStorage(), stateKey.localTag()));
bufferStage_->discard (handle);
}
ERROR_LOG_AND_IGNORE (engine, "cleanup of buffer metadata while handling an error")
/* === BuffDescr and BuffHandle === */
bool
BufferProvider::was_created_by_this_provider (BuffDescr const& descr) const
{
return isSameAdr (this, descr.provider_);
}
/**
* A Buffer Descriptor is considered _valid_
* iff it represents a registered buffer type.
* @note this implies that the buffer size is not zero.
*/
bool
BuffDescr::isValid() const
{
ENSURE (provider_);
return provider_->isValid (*this);
}
/**
* A concrete Buffer Handle is considered valid,
* iff it is currently registered in the buffer metadata table,
* and the corresponding metadata::Entry indicates LOCKED state,
* but not yet emitted (or released).
* @note this implies also that it is in some active state, and
* indirectly (due to the _Buffer Provider Protocol_) also
* that the _parent type_ is valid, which means that it was
* created from a valid BuffDescr. But note that we can not
* see this parent type without lookup by the BufferProvider;
* The embedded #descriptor_ is synthetic, and reflects the
* handle's _state-key_ (⟶ registry Entry) not the type-key.
*/
bool
BuffHandle::isValid() const
{
ENSURE (descriptor_.provider_);
return bool(pBuffer_)
and descriptor_.provider_->isAccessible (*this);
}
2011-11-21 02:28:44 +01:00
size_t
BuffDescr::buffSize() const
2011-11-21 02:28:44 +01:00
{
ENSURE (provider_);
2011-11-21 02:28:44 +01:00
return provider_->getBufferSize (*this);
}
size_t
BuffHandle::size() const
{
return descriptor_.buffSize();
}
2011-11-21 02:28:44 +01:00
uint
BuffDescr::announce (uint count)
{
ENSURE (provider_);
return provider_->announce(count, *this);
}
BuffHandle
BuffDescr::lockBuffer()
{
ENSURE (provider_);
return provider_->lockBuffer(*this);
}
void
BuffHandle::emit()
{
REQUIRE (isValid());
descriptor_.provider_->emitBuffer(*this);
}
void
BuffHandle::release()
{
if (pBuffer_)
{
REQUIRE (isValid());
descriptor_.provider_->releaseBuffer(*this);
pBuffer_ = 0;
}
ENSURE (!isValid());
}
void
BuffHandle::emergencyCleanup()
{
descriptor_.provider_->emergencyCleanup(*this); // EX_FREE
pBuffer_ = 0;
}
}} // namespace engine