Currently job artifacts in CI/CD pipelines on LRZ GitLab never expire. Starting from Wed 26.1.2022 the default expiration time will be 30 days (GitLab default). Currently existing artifacts in already completed jobs will not be affected by the change. The latest artifacts for all jobs in the latest successful pipelines will be kept. More information: https://gitlab.lrz.de/help/user/admin_area/settings/continuous_integration.html#default-artifacts-expiration

opengljobprocessor.h 8.89 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-2014, 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 Universitaet Muenchen
//      Boltzmannstr. 3, 85748 Garching b. Muenchen, Germany
10
// 
schultezub's avatar
schultezub committed
11
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
12
// 
13
14
15
16
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 
// except in compliance with the License. You may obtain a copy of the License at
// 
// http://www.apache.org/licenses/LICENSE-2.0
17
// 
18
19
20
21
// Unless required by applicable law or agreed to in writing, software distributed under the 
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
// either express or implied. See the License for the specific language governing permissions 
// and limitations under the License.
22
23
24
25
26
27
28
// 
// ================================================================================================

#ifndef OPENGLJOBPROCESSOR_H__
#define OPENGLJOBPROCESSOR_H__

#include "sigslot/sigslot.h"
29
30
#include "tgt/runnable.h"
#include "tgt/singleton.h"
31

32
#include <ext/threading.h>
33

34
#define TBB_PREVIEW_MEMORY_POOL 1
35
36
37
38
#include <tbb/atomic.h>
#include <tbb/concurrent_queue.h>
#include <tbb/concurrent_hash_map.h>
#include <tbb/concurrent_vector.h>
39
#include <tbb/memory_pool.h>
40
41
42

#include "tgt/glcontextmanager.h"
#include "tgt/singleton.h"
43
44

#include "core/coreapi.h"
45
#include "core/tools/job.h"
46

47
48
49
#include <ctime>


50
51
52
53
namespace tgt {
    class GLCanvas;
}

schultezub's avatar
schultezub committed
54
namespace campvis {
55
    /**
schultezub's avatar
schultezub committed
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
     * 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.
73
     */
74
    class CAMPVIS_CORE_API OpenGLJobProcessor : public tgt::Singleton<OpenGLJobProcessor>, public tgt::Runnable, public sigslot::has_slots {
75
76
        friend class tgt::Singleton<OpenGLJobProcessor>;    ///< CRTP
        friend class AbstractJob;                           ///< so the custom new/delete operator can access the memory pool
77
78

    public:
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
        /**
         * Scope guard to ensure that encapsulated job is synchronously executed in an arbitrary OpenGL context.
         * This scope guard checks whether current thread is OpenGLJobProcessor thread. If so, it
         * does nothing. If this thread is not the OpenGL thread, the OpenGLJobProcessor is paused,
         * an arbitrary OpenGL context acquired. Upon destruction the OpenGLJobProcessor is resumed.
         */
        class ScopedSynchronousGlJobExecution {
        public:
            ScopedSynchronousGlJobExecution();
            ~ScopedSynchronousGlJobExecution();

        private:
            tgt::GLContextScopedLock* _lock;
        };

94
95
96
97
        /**
         * Enumeration of the different priorities of items.
         */
        enum JobType {
schultezub's avatar
schultezub committed
98
            PaintJob,           ///< PaintJobs have the highest priority, there can only be one paint job per context at a time.
99
100
            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.
101
102
        };

schultezub's avatar
schultezub committed
103
104
105
        /**
         * Destructor, deletes all unfinished jobs.
         */
106
107
108
        virtual ~OpenGLJobProcessor();


schultezub's avatar
schultezub committed
109
110
111
112
        /**
         * Registers the given OpenGL context, so that it gets its own job queue.
         * \param   context     OpenGL context to register.
         */
113
114
        void registerContext(tgt::GLCanvas* context);

115
116
117
118
119
120
        /**
         * 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);

121
122
123
124
        /// \see Runnable::stop
        void stop();
        
        /**
schultezub's avatar
schultezub committed
125
         * Performs the job processing using conditional wait.
126
127
128
129
         * \sa Runnable::run
         */
        void run();

130
131
132
133
134
135
136
137
138
139
        /**
         * Pauses the job processor as at the next possible moment.
         */
        void pause();

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

140
141
142
143
        /**
         * Enqueues the given Job with the given priority.
         * 
         * \note    OpenGLJobProcessor takes ownership of \a job.
schultezub's avatar
schultezub committed
144
         * \param   canvas      OpenGL context for which to enqueue the job, must be registered.
145
146
147
         * \param   job         Job to enqueue, PriorityPool takes ownership of this Job!
         * \param   priority    Priority of the job to enqueue
         */
148
        void enqueueJob(tgt::GLCanvas* canvas, AbstractJob* job, JobType priority);
149
150


151
152
153
154
155
156
157
        /**
         * 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();

158
159
160
161
162
        /**
         * Checks whether calling thread is OpenGL thread.
         * \return  std::this_thread::get_id() == _this_thread_id
         */
        bool isCurrentThreadOpenGlThread() const;
163

164
    protected:
schultezub's avatar
schultezub committed
165
166
167
        /**
         * Struct encapsulating the job queue for a single OpenGL context.
         */
168
        struct CAMPVIS_CORE_API PerContextJobQueue {
schultezub's avatar
schultezub committed
169
170
171
            /**
             * Creates an empty PerContextJobQueue.
             */
172
173
174
            PerContextJobQueue() {
                _paintJob = 0;
            }
schultezub's avatar
schultezub committed
175
176
177
178
179
180
181
182
183
184
185
186
187

            /**
             * 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;
188
189
            }

190
            tbb::atomic<AbstractJob*> _paintJob;                    ///< PaintJob of the context
schultezub's avatar
schultezub committed
191
192
            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
193

schultezub's avatar
schultezub committed
194
195
196
197
            /**
             * Checks, whether there is any job to do for this context.
             * \return (_serialJobs.empty() && _lowPriorityJobs.empty() && (_paintJob == 0))
             */
198
            bool empty() const {
199
                return ((_paintJob == 0) && _serialJobs.empty() && _lowPriorityJobs.empty());
200
201
202
            }
        };

203
204
        OpenGLJobProcessor();

205
206
207
        tbb::concurrent_hash_map<tgt::GLCanvas*, PerContextJobQueue*> _contextQueueMap;
        tbb::concurrent_vector<tgt::GLCanvas*> _contexts;

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

211
        tbb::atomic<tgt::GLCanvas*> _currentContext;         ///< current active OpenGL context
212
213

    private:
214
215
216
        typedef std::allocator<AbstractJob> pool_allocator_t;
        tbb::memory_pool<pool_allocator_t> _signalPool; ///< Memory pool for the signals

217
        static std::thread::id _this_thread_id;
218
219
220
221
    };

}

222
#define GLJobProc tgt::Singleton<campvis::OpenGLJobProcessor>::getRef()
223
224

#endif // OPENGLJOBPROCESSOR_H__