Commit c56f094b authored by Jakob Weiss's avatar Jakob Weiss
Browse files

minor fixes and clarifications regarding ScopedTypedData

parent ce377bcc
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -44,10 +44,10 @@ namespace campvis {
    DataHandle DataContainer::addData(const std::string& name, AbstractData* data) {
        if (name.empty()) {
            LERROR("Tried to add data with empty name to DataContainer.");
            return DataHandle(0);
            return DataHandle(nullptr);
        }

        cgtAssert(data != 0, "The Data must not be 0.");
        cgtAssert(data != nullptr, "The Data must not be 0.");
        cgtAssert(!name.empty(), "The data's name must not be empty.");

        DataHandle dh(data);
@@ -61,7 +61,7 @@ namespace campvis {
            return;
        }

        cgtAssert(dh.getData() != 0, "The data in the DataHandle must not be 0!");
        cgtAssert(dh, "The data in the DataHandle must not be 0!");
        cgtAssert(!name.empty(), "The data's name must not be empty.");
        _handles.erase(name);
        _handles.insert(std::make_pair(name, dh));
@@ -81,7 +81,7 @@ namespace campvis {
            return a->second;
        }
        else {
            return DataHandle(0);
            return DataHandle(nullptr);
        }
    }

+12 −13
Original line number Diff line number Diff line
@@ -36,18 +36,17 @@ namespace campvis {

    /**
     * A DataHandle is responsible to manage the lifetime of an AbstractData instance.
     * Therefore, it implements a reference counting technique in cooperation with AbstractData.
     * Therefore, it implements a reference counting technique and automatically deletes instances when
     * no other references exist anymore.
     *
     * This class can be considered as thread safe under the following conditions:
     *  * A single DataHandle instance must not be accessed from different threads.
     *  * Concurrent access to the same AbstractData instance via different DataHandles is safe.
     * This class can be considered as thread safe, since through the getData() and getWritableData() thread-safe
     * access is ensured.
     *
     * \note    For clarity: An AbstractData instance can be referenced by multiple DataHandles. As soon
     *          as it is afterwards reference by 0 DataHandles, the AbstractData instance will be destroyed.
     *          Also remember that a DataHandle takes ownership of the given AbstractData instance. So do
     *          not delete it once it has been assigned to a DataHandle (respectively DataContainer) or mess
     *          with its reference counting!
     * \note    Reference counting implementation inspired from Scott Meyers: More Effective C++, Item 29
     */
    class CAMPVIS_CORE_API DataHandle {
        template<class T, bool isWritable> friend class ScopedTypedData;
@@ -83,7 +82,7 @@ namespace campvis {

        /**
         * Grants const access to the managed AbstractData instance.
         * \return  _data;
         * \return  const pointer proxy class managing access
         */
        template <class T = AbstractData>
        ScopedTypedData<T, false> getData() const {
@@ -91,8 +90,8 @@ namespace campvis {
        }

        /**
        * Grants const access to the managed AbstractData instance.
        * \return  _data;
        * Grants writable access to the managed AbstractData instance.
        * \return  non-const pointer proxy class managing the access
        */
        template <class T = AbstractData>
        ScopedTypedData<T, true> getWritableData() {
@@ -117,7 +116,7 @@ namespace campvis {
         * conveniently using an if(dh) { [DataHandle is valid] } statement.
         * \return !empty()
         */
        operator bool() const {
        explicit operator bool() const {
            return !empty();
        }

+66 −39
Original line number Diff line number Diff line
@@ -31,19 +31,20 @@
#include "core/datastructures/datacontainer.h"

#include <typeinfo>
#include <type_traits>

namespace campvis {

    /**
     * Proxy class for scoped strongly-typed access to the data of a DataContainer.
     * From the outside ScopedTypedData<T> behaves exactly like a const T*, but internally it preserves the
     * reference counting of a DataHandle. Use this class when you want temporary access to a strongly-typed
     * data item in a DataContainer but don't want to to the dynamic_cast yourself.
     * reference counting of a DataHandle. This also manages the read/write access for every data handle by acquiring
     * the lock type specified in the template parameter (default is read-only).
     *
     * \tparam  T   Base class of the DataHandle data to test for
     */
    template<typename T, bool isWritable = false>
    class ScopedTypedData : public AbstractData::LockType {
    class ScopedTypedData : private AbstractData::LockType {
    public:
        /**
         * Creates a new DataHandle to the data item with the key \a name in \a dc, that behaves like a T*.
@@ -57,10 +58,10 @@ namespace campvis {
            , data(nullptr)
        {
            if (dh) {
                data = dynamic_cast<const T*>(dh._ptr.get());
                data = dynamic_cast<T*>(dh._ptr.get());
                if (data == nullptr) {
                    if (!silent)
                        LDEBUGC("CAMPVis.core.ScopedTypedData (Readonly)", "Found DataHandle with id '" << name << "', but it is of wrong type (" << typeid(dh._ptr.get()).name() << " instead of " << typeid(T).name() << ").");
                        LDEBUGC("CAMPVis.core.ScopedTypedData", "[" << (isWritable ? "Writable" : "Readonly") << "] Found DataHandle with id '" << name << "', but it is of wrong type(" << typeid(dh._ptr.get()).name() << " instead of " << typeid(T).name() << ").");

                    dh = DataHandle(0);
                }
@@ -71,7 +72,7 @@ namespace campvis {
            }
            else {
                if (!silent)
                    LDEBUGC("CAMPVis.core.ScopedTypedData (Readonly)", "Could not find a DataHandle with id '" << name << "' in DataContainer '" << dc.getName() << "'.");
                    LDEBUGC("CAMPVis.core.ScopedTypedData", "[" << (isWritable ? "Writable" : "Readonly") << "] Could not find a DataHandle with id '" << name << "' in DataContainer '" << dc.getName() << "'.");
            }
        };

@@ -80,10 +81,10 @@ namespace campvis {
            , dh(handle)
            , data(nullptr)
        {
            data = dynamic_cast<const T*>(dh._ptr.get());
            data = dynamic_cast<T*>(dh._ptr.get());
            if (data == nullptr) {
                if (!silent)
                    LDEBUGC("CAMPVis.core.ScopedTypedData (Readonly)", "DataHandle is of wrong type (" << typeid(dh._ptr.get()).name() << " instead of " << typeid(T).name() << ").");
                    LDEBUGC("CAMPVis.core.ScopedTypedData", "[" << (isWritable ? "Writable" : "Readonly") << "] DataHandle is of wrong type (" << typeid(dh._ptr.get()).name() << " instead of " << typeid(T).name() << ").");

                dh = DataHandle(nullptr);
            }
@@ -94,25 +95,26 @@ namespace campvis {

        /**
         * Move-Constructor that moves the lock over
         * \note: Since tbb locks do not support move-construction, this implementation is not very efficient
         * \note: Since tbb locks do not support move-construction, this implementation *will* release the lock during the handoff
         */
        ScopedTypedData(ScopedTypedData && other)
            : AbstractData::LockType()
            , dh(other.dh)
            , data(other.data)
        {
            other.release(); // force a release - otherwise we might deadlock

            if (data != nullptr) { // we have a valid handle, so we need to lock it
                acquire(dh._ptr->_mutex, isWritable);
            }

            other.dh = DataHandle(nullptr);
            other.data = nullptr;
            other.release();
        }

        ~ScopedTypedData() {
            if (dh)
                release(); // we release manually to make sure to release before the datahandle is destroyed - otherwise the mutex is destroyed before it is released
                release(); // we release manually to make sure to release before the datahandle is destroyed - otherwise the mutex might be destroyed before it is released
        }

        /**
@@ -131,6 +133,26 @@ namespace campvis {
            return data;
        }

        // Note: The following enable_if construct yields an internal compiler error for VC 2013, which is why we explicitly specialize
        // the whole class template below
        ///**
        //* Implicit conversion operator to T*. Only applies for writable ScopedTypedData instances.
        //* \return  The data in the DataHandle, may be 0 when no DataHandle was found, or the data is of the wrong type.
        //*/
        //template< typename U = T, typename = typename std::enable_if< isWritable >::type >
        //operator T*() {
        //	return data;
        //}

        ///**
        //* Implicit arrow operator to T*. Only applies for writable ScopedTypedData instances.
        //* \return  The data in the DataHandle, may be 0 when no DataHandle was found, or the data is of the wrong type.
        //*/
        //template< typename U = T, typename = typename std::enable_if< isWritable >::type >
        //T* operator->() {
        //	return data;
        //}

         /**
         * Gets the DataHandle.
         * \return dh
@@ -139,19 +161,23 @@ namespace campvis {
            return dh;
        }

    private:
    protected:
        /// Not copy-constructable
        ScopedTypedData(const ScopedTypedData& rhs);
        /// Not assignable
        ScopedTypedData& operator=(const ScopedTypedData& rhs);

        DataHandle dh;      ///< DataHandle
        const T* data;      ///< strongly-typed pointer to data, may be 0
        DataHandle dh;      ///< a DataHandle to ensure data's lifetime until this accessor is destroyed
        T* data;            ///< strongly-typed pointer to data, may be 0
    };


    /**
     * Full template specialization for isWritable = true. Could be avoided with the std::enable_if SFINAE magic but
     * unfortunately this causes compiler crashes for VC2013
     */
    template<typename T>
    class ScopedTypedData<T, true> : public AbstractData::LockType {
    class ScopedTypedData<T, true> : private AbstractData::LockType {
        const bool isWritable = true;
    public:
        /**
        * Creates a new DataHandle to the data item with the key \a name in \a dc, that behaves like a T*.
@@ -168,18 +194,18 @@ namespace campvis {
                data = dynamic_cast<T*>(dh._ptr.get());
                if (data == nullptr) {
                    if (!silent)
                        LDEBUGC("CAMPVis.core.ScopedTypedData (Writable)", "Found DataHandle with id '" << name << "', but it is of wrong type (" << typeid(dh._ptr.get()).name() << " instead of " << typeid(T).name() << ").");
                        LDEBUGC("CAMPVis.core.ScopedTypedData", "[" << (isWritable ? "Writable" : "Readonly") << "] Found DataHandle with id '" << name << "', but it is of wrong type(" << typeid(dh._ptr.get()).name() << " instead of " << typeid(T).name() << ").");

                    dh = DataHandle(nullptr);
                    dh = DataHandle(0);
                }
                else { // we have a valid handle, so we need to lock it
                    // lock(data->_mutex);
                    acquire(dh._ptr->_mutex, true);
                       // lock_shared(dh->_mutex);
                    acquire(dh._ptr->_mutex, isWritable);
                }
            }
            else {
                if (!silent)
                    LDEBUGC("CAMPVis.core.ScopedTypedData (Writable)", "Could not find a DataHandle with id '" << name << "' in DataContainer '" << dc.getName() << "'.");
                    LDEBUGC("CAMPVis.core.ScopedTypedData", "[" << (isWritable ? "Writable" : "Readonly") << "] Could not find a DataHandle with id '" << name << "' in DataContainer '" << dc.getName() << "'.");
            }
        };

@@ -191,40 +217,41 @@ namespace campvis {
            data = dynamic_cast<T*>(dh._ptr.get());
            if (data == nullptr) {
                if (!silent)
                    LDEBUGC("CAMPVis.core.ScopedTypedData (Writable)", "DataHandle is of wrong type (" << typeid(dh._ptr.get()).name() << " instead of " << typeid(T).name() << ").");
                    LDEBUGC("CAMPVis.core.ScopedTypedData", "[" << (isWritable ? "Writable" : "Readonly") << "] DataHandle is of wrong type (" << typeid(dh._ptr.get()).name() << " instead of " << typeid(T).name() << ").");

                dh = DataHandle(nullptr);
            }
            else { // we have a valid handle, so we need to lock it
                // lock(data->_mutex);
                acquire(dh._ptr->_mutex, true);
                acquire(dh._ptr->_mutex, isWritable);
            }
        };

        /**
        * Move-Constructor that moves the lock over
        * \note: Since tbb locks do not support move-construction, this implementation is not very efficient
        */
        ScopedTypedData(ScopedTypedData && other)
            : AbstractData::LockType()
            , dh(other.dh)
            , data(other.data)
        {
            other.release(); // force a release - otherwise we might deadlock

            if (data != nullptr) { // we have a valid handle, so we need to lock it
                other.release(); // we need to release the other lock first because we cannot move it directly
                acquire(dh._ptr->_mutex, true);
                acquire(dh._ptr->_mutex, isWritable);
            }

            other.dh = DataHandle(nullptr);
            other.data = nullptr;
        }

        
        ~ScopedTypedData() {
            if (dh)
                release(); // we release manually to make sure to release before the datahandle is destroyed - otherwise the mutex is destroyed before it is released
                release(); // we release manually to make sure to release before the datahandle is destroyed - otherwise the mutex might be destroyed before it is released
        }

        /**
        * Implicit conversion operator to const T*.
        * Implicit conversion operator to T*. Only applies for writable ScopedTypedData instances.
        * \return  The data in the DataHandle, may be 0 when no DataHandle was found, or the data is of the wrong type.
        */
        operator T*() {
@@ -232,31 +259,31 @@ namespace campvis {
        }

        /**
        * Implicit arrow operator to T*.
        * Implicit arrow operator to T*. Only applies for writable ScopedTypedData instances.
        * \return  The data in the DataHandle, may be 0 when no DataHandle was found, or the data is of the wrong type.
        */
        T* operator->() {
            return data;
        }


        /**
        * Gets the DataHandle.
        * \return dh
        */
        DataHandle getDataHandle() {
        DataHandle getDataHandle() const {
            return dh;
        }

    private:
    protected:
        /// Not copy-constructable
        ScopedTypedData(const ScopedTypedData& rhs);
        /// Not assignable
        ScopedTypedData& operator=(const ScopedTypedData& rhs);

        DataHandle dh;      ///< DataHandle
        DataHandle dh;      ///< a DataHandle to ensure data's lifetime until this accessor is destroyed
        T* data;            ///< strongly-typed pointer to data, may be 0
    };

}

#endif // SCOPEDTYPEDDATA_H__
 No newline at end of file
+9 −4
Original line number Diff line number Diff line
@@ -72,23 +72,26 @@ namespace campvis {
    void VisualizationProcessor::createAndAttachTexture(GLint internalFormat, GLenum attachment, ScopedTypedData<RenderData, true>* renderData /*= nullptr*/) {
        cgtAssert(_fbo->isActive(), "Trying to attach a texture while FBO is not bound!");

        // acqiure a new TextureUnit, so that we don't mess with other currently bound textures during texture upload...
        // acquire a new TextureUnit, so that we don't mess with other currently bound textures during texture upload...
        cgt::TextureUnit rtUnit;
        rtUnit.activate();

        // Set OpenGL pixel alignment to 1 to avoid problems with NPOT textures
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

        // Check the render data if we can reuse a texture
        cgt::Texture* tex = nullptr;
        if (renderData) {
        if (renderData) {`
            // check if we can get a GL representation of the desired texture
            ImageRepresentationGL* glRep;
            if (attachment == GL_DEPTH_ATTACHMENT)
                if(auto id = (*renderData)->getDepthTexture())
                    glRep = const_cast<ImageRepresentationGL*>(id->getRepresentation<ImageRepresentationGL>(false));
            else 
                if(auto id =(*renderData)->getColorTexture(_fbo->getNumColorAttachments()))
                if(auto id =(*renderData)->getColorTexture(_fbo->getNumColorAttachments())) //_fbo->getNumColorAttachments() is the index of the texture that is to be created
                    glRep = const_cast<ImageRepresentationGL*>(id->getRepresentation<ImageRepresentationGL>(false));

            // check if the texture is compatible with what we want, and in that case detach from the old RenderTargetData
            if (glRep) {
                tex = const_cast<cgt::Texture*>(glRep->getTexture());
                if (tex->getType() != GL_TEXTURE_2D || tex->getDimensions() != getRenderTargetSize()
@@ -96,13 +99,15 @@ namespace campvis {
                    tex = nullptr;
                else {
                    // somehow detach the cgt::Texture from the ImageRepresentationGL
                    // yes this is horrible code
                    // yes this is horrible - however, we know that renderData has write access, ergo
                    // we can safely do this const_cast
                    const_cast<ImageData*>(glRep->getParent())->removeRepresentationUnsafe<ImageRepresentationGL>();
                }

            }
        }

        // if we could not get the texture from a previous render target, create a new one
        if (!tex) {
            // create texture
            tex = new cgt::Texture(GL_TEXTURE_2D, getRenderTargetSize(), internalFormat, cgt::Texture::LINEAR);
+2 −2

File changed.

Contains only whitespace changes.