2.12.2021, 9:00 - 11:00: Due to updates GitLab may be unavailable for some minutes between 09:00 and 11:00.

opengljobprocessor.h 8.08 KB
Newer Older
1
2
// ================================================================================================
// 
schultezub's avatar
schultezub committed
3
// This file is part of the CAMPVis Software Framework.
4
5
// 
// If not explicitly stated otherwise: Copyright (C) 2012, all rights reserved,
schultezub's avatar
schultezub committed
6
//      Christian Schulte zu Berge <christian.szb@in.tum.de>
7
//      Chair for Computer Aided Medical Procedures
8
9
//      Technische Universität München
//      Boltzmannstr. 3, 85748 Garching b. München, Germany
schultezub's avatar
schultezub committed
10
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 
// The licensing of this softare is not yet resolved. Until then, redistribution in source or
// binary forms outside the CAMP chair is not permitted, unless explicitly stated in legal form.
// However, the names of the original authors and the above copyright notice must retain in its
// original state in any case.
// 
// Legal disclaimer provided by the BSD license:
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 
// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// 
// ================================================================================================

#ifndef OPENGLJOBPROCESSOR_H__
#define OPENGLJOBPROCESSOR_H__

#include "sigslot/sigslot.h"
#include "tgt/singleton.h"
35
36
37
38
39
#include <tbb/atomic.h>
#include <tbb/concurrent_queue.h>
#include <tbb/concurrent_hash_map.h>
#include <tbb/concurrent_vector.h>
#include <tbb/compat/condition_variable>
40
#include "core/tools/job.h"
41
42
#include "core/tools/runnable.h"

43
44
45
#include <ctime>


46
47
48
49
namespace tgt {
    class GLCanvas;
}

schultezub's avatar
schultezub committed
50
namespace campvis {
51
    /**
schultezub's avatar
schultezub committed
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
     * Singleton class for managing and executing work items (jobs) that need an active OpenGL context.
     * After an OpenGL context has been registered you can enqueue jobs that are to be executed within that 
     * context to the job queue. Enqueued jobs are executed asynchroniously using a specific scheduling 
     * strategy, depending on the given JobType:
     * 
     * OpenGLJobProcessor implements a round-robin scheduling strategy for the registered OpenGL contexts, 
     * meaning that each context gets roughly the same computing time. Thereby, it tries to maintain an update
     * frequency of 30fps for each context.
     * The jobs for each contexts are scheduled using the following technique: As mentioned above, each context
     * has a time slot of \a n milliseconds. At first, as much serial jobs are executed as possible until their
     * queue is empty or or the time is up. Then, one low priority job is executed. Finally, the PaintJob is
     * executed (if existant) before switching to the next context.
     * 
     * This class implements the Runnable interface, hence, runs in its own thread. Furthermore, it uses 
     * conditional wait, when there are currently no jobs to process.
     * 
     * This class is to be considered as thread-safe.
69
70
71
72
73
     */
    class OpenGLJobProcessor : public tgt::Singleton<OpenGLJobProcessor>, public Runnable, public sigslot::has_slots<> {
        friend class tgt::Singleton<OpenGLJobProcessor>;

    public:
74
75
76
77
        /**
         * Enumeration of the different priorities of items.
         */
        enum JobType {
schultezub's avatar
schultezub committed
78
            PaintJob,           ///< PaintJobs have the highest priority, there can only be one paint job per context at a time.
79
80
            SerialJob,          ///< SerialJobs have a lower priority than PaintJobs, but are guaranteed to be executed in order.
            LowPriorityJob      ///< Low priority jobs have the lowest priority, can be executed at any time. The only guarantee is that thay won't starve.
81
82
        };

schultezub's avatar
schultezub committed
83
84
85
        /**
         * Destructor, deletes all unfinished jobs.
         */
86
87
88
        virtual ~OpenGLJobProcessor();


schultezub's avatar
schultezub committed
89
90
91
92
        /**
         * Registers the given OpenGL context, so that it gets its own job queue.
         * \param   context     OpenGL context to register.
         */
93
94
        void registerContext(tgt::GLCanvas* context);

95
96
97
98
99
100
        /**
         * Deregisters the given OpenGL context, so that it has no longer its own job queue.
         * \param   context     OpenGL context to deregister.
         */
        void deregisterContext(tgt::GLCanvas* context);

101
102
103
104
        /// \see Runnable::stop
        void stop();
        
        /**
schultezub's avatar
schultezub committed
105
         * Performs the job processing using conditional wait.
106
107
108
109
         * \sa Runnable::run
         */
        void run();

110
111
112
113
114
115
116
117
118
119
        /**
         * Pauses the job processor as at the next possible moment.
         */
        void pause();

        /**
         * Resume the execution of the job processor.
         */
        void resume();

120
121
122
123
        /**
         * Enqueues the given Job with the given priority.
         * 
         * \note    OpenGLJobProcessor takes ownership of \a job.
schultezub's avatar
schultezub committed
124
         * \param   canvas      OpenGL context for which to enqueue the job, must be registered.
125
126
127
         * \param   job         Job to enqueue, PriorityPool takes ownership of this Job!
         * \param   priority    Priority of the job to enqueue
         */
128
        void enqueueJob(tgt::GLCanvas* canvas, AbstractJob* job, JobType priority);
129
130


131
132
133
134
135
136
137
138
        /**
         * Returns an arbitrary registered OpenGL context.
         * \note    You can do really messy things with this. Do not use this method unless
         *          you know what you're doing and know that there is no other way...
         */
        tgt::GLCanvas* iKnowWhatImDoingGetArbitraryContext();


139
    protected:
schultezub's avatar
schultezub committed
140
141
142
        /**
         * Struct encapsulating the job queue for a single OpenGL context.
         */
143
        struct PerContextJobQueue {
schultezub's avatar
schultezub committed
144
145
146
            /**
             * Creates an empty PerContextJobQueue.
             */
147
148
149
            PerContextJobQueue() {
                _paintJob = 0;
            }
schultezub's avatar
schultezub committed
150
151
152
153
154
155
156
157
158
159
160
161
162

            /**
             * Destructor, deletes all enqueued jobs.
             */
            ~PerContextJobQueue() {
                if (_paintJob != 0)
                    delete _paintJob;

                AbstractJob* jobToDelete = 0;
                while (_serialJobs.try_pop(jobToDelete))
                    delete jobToDelete;
                while (_lowPriorityJobs.try_pop(jobToDelete))
                    delete jobToDelete;
163
164
            }

165
            tbb::atomic<AbstractJob*> _paintJob;                    ///< PaintJob of the context
schultezub's avatar
schultezub committed
166
167
            tbb::concurrent_queue<AbstractJob*> _serialJobs;        ///< Queue of serial jobs for the context
            tbb::concurrent_queue<AbstractJob*> _lowPriorityJobs;   ///< Queue of jow priority jobs for the context
168

schultezub's avatar
schultezub committed
169
170
171
172
            /**
             * Checks, whether there is any job to do for this context.
             * \return (_serialJobs.empty() && _lowPriorityJobs.empty() && (_paintJob == 0))
             */
173
            bool empty() const {
174
                return ((_paintJob == 0) && _serialJobs.empty() && _lowPriorityJobs.empty());
175
176
177
            }
        };

178
179
        OpenGLJobProcessor();

180
181
182
        tbb::concurrent_hash_map<tgt::GLCanvas*, PerContextJobQueue*> _contextQueueMap;
        tbb::concurrent_vector<tgt::GLCanvas*> _contexts;

183
        tbb::atomic<int> _pause;
184
        std::condition_variable _evaluationCondition;   ///< conditional wait to be used when there are currently no jobs to process
185

186
        tbb::atomic<tgt::GLCanvas*> _currentContext;         ///< current active OpenGL context
187
188
189
190
191
192
193
    };

}

#define GLJobProc tgt::Singleton<OpenGLJobProcessor>::getRef()

#endif // OPENGLJOBPROCESSOR_H__