The expiration time for new job artifacts in CI/CD pipelines is now 30 days (GitLab default). Previously generated 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.cpp 8.74 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-2013, 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
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
// 
// ================================================================================================

#include "opengljobprocessor.h"

#include "tgt/assert.h"
28
29
#include "tgt/logmanager.h"
#include "tgt/openglgarbagecollector.h"
30
#include "tgt/glcontextmanager.h"
31
32
#include "core/tools/job.h"

schultezub's avatar
schultezub committed
33
namespace campvis {
34
35
36

    OpenGLJobProcessor::OpenGLJobProcessor()
    {
37
        _pause = 0;
38
        _currentContext = 0;
39
40
41
    }

    OpenGLJobProcessor::~OpenGLJobProcessor() {
schultezub's avatar
schultezub committed
42
43
44
45
46
47
48
        // delete all per-context job queues and unfinished jobs
        for (tbb::concurrent_vector<tgt::GLCanvas*>::const_iterator it = _contexts.begin(); it != _contexts.end(); ++it) {
            tbb::concurrent_hash_map<tgt::GLCanvas*, PerContextJobQueue*>::const_accessor a;
            if (_contextQueueMap.find(a, *it)) {
                delete a->second;
            }
        }
49

schultezub's avatar
schultezub committed
50
51
        _contextQueueMap.clear();
        _contexts.clear();
52
53
54
55
56
57
58
59
    }

    void OpenGLJobProcessor::stop() {
        _stopExecution = true;
        _evaluationCondition.notify_all();
    }

    void OpenGLJobProcessor::run() {
60
        std::unique_lock<tbb::mutex> lock(tgt::GlContextManager::getRef().getGlMutex());
61
        clock_t lastCleanupTime = clock() * 1000 / CLOCKS_PER_SEC;
62
63

        while (! _stopExecution) {
64
65
            // this is a simple round-robing scheduling between all contexts:
            bool hadWork = false;
schultezub's avatar
schultezub committed
66
            // TODO: consider only non-empty context queues here
67
            clock_t maxTimePerContext = static_cast<clock_t>(30 / _contexts.size());
68
69

            for (size_t i = 0; i < _contexts.size(); ++i) {
70
                clock_t startTimeCurrentContext = clock() * 1000 / CLOCKS_PER_SEC;
71
72
73
74
                tgt::GLCanvas* context = _contexts[i];

                tbb::concurrent_hash_map<tgt::GLCanvas*, PerContextJobQueue*>::const_accessor a;
                if (!_contextQueueMap.find(a, context)) {
75
76
                    //tgtAssert(false, "Should not reach this: Did not find context in contextQueueMap!");
                    continue;
77
78
                }

79
                // avoid expensive context-switches for contexts without pending jobs.
80
81
82
                if (a->second->empty())
                    continue;

83
84
85
                // we will have work, so update the flag
                hadWork = true;

schultezub's avatar
schultezub committed
86
                // perform context switch if necessary
87
                if (_currentContext != context) {
88
89
90
91
                    if (_currentContext != 0) {
                        glFinish();
                        LGL_ERROR;
                    }
92
                    tgt::GlContextManager::getRef().acquireContext(context);
93
94
95
96
97
98
                    _currentContext = context;
                }

                // now comes the per-context scheduling strategy:
                // first: perform as much serial jobs as possible:
                AbstractJob* jobToDo = 0;
99
                while ((clock() * 1000 / CLOCKS_PER_SEC) - startTimeCurrentContext < maxTimePerContext) {
100
101
102
103
104
105
106
107
108
109
110
111
112
113
                    // try fetch a job
                    if (! a->second->_serialJobs.try_pop(jobToDo)) {
                        // no job to do, exit the while loop
                        break;
                    }
                    // execute and delete the job
                    jobToDo->execute();
                    delete jobToDo;
                }

                // second: execute one low-prio job if existant
                if (a->second->_lowPriorityJobs.try_pop(jobToDo)) {
                    jobToDo->execute();
                    delete jobToDo;
114
115
                }

116
                // third: execute paint job
117
118
                jobToDo = a->second->_paintJob.fetch_and_store(0);
                if (jobToDo != 0) {
119
120
121
                    jobToDo->execute();
                    delete jobToDo;
                }
122
123
124
125
126
127
128


                // fourth: start the GC if it's time
                if (clock() * 1000 / CLOCKS_PER_SEC - lastCleanupTime > 250) {
                    GLGC.deleteGarbage();
                    lastCleanupTime = clock();
                }
129
            }
130
131
            
            while (_pause > 0) {
132
133
                GLGC.deleteGarbage();
                lastCleanupTime = clock();
134
                tgt::GlContextManager::getRef().releaseCurrentContext();
135
                _evaluationCondition.wait(lock);
136
                tgt::GlContextManager::getRef().acquireContext(_currentContext);
137
138
                hadWork = true;
            }
139

140
            if (! hadWork) {
141
142
143
144
                if (_currentContext != 0) {
                    GLGC.deleteGarbage();
                    lastCleanupTime = clock();
                }
145
                tgt::GlContextManager::getRef().releaseCurrentContext();
146
                _evaluationCondition.wait(lock);
147
                tgt::GlContextManager::getRef().acquireContext(_currentContext);
148
            }
149
150
151
        }

        // release OpenGL context, so that other threads can access it
152
        tgt::GlContextManager::getRef().releaseCurrentContext();
153
154
    }

155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
    void OpenGLJobProcessor::pause() {
        ++_pause;
    }

    void OpenGLJobProcessor::resume() {
        if (_pause == 0) {
            tgtAssert(false, "Called resume on non-paused job processor!");
            return;
        }

        --_pause;
        if (_pause == 0)
            _evaluationCondition.notify_all();
    }

170
171
172
173
174
    void OpenGLJobProcessor::enqueueJob(tgt::GLCanvas* canvas, AbstractJob* job, JobType priority) {
        tbb::concurrent_hash_map<tgt::GLCanvas*, PerContextJobQueue*>::const_accessor a;
        if (_contextQueueMap.find(a, canvas)) {
            switch (priority) {
            case PaintJob:
175
176
177
178
179
180
                {
                    AbstractJob* oldJob = a->second->_paintJob.fetch_and_store(job);
                    if (oldJob != 0)
                        delete oldJob;
                    break;
                }
181
182
183
184
185
186
187
188
189
190
191
192
            case SerialJob:
                a->second->_serialJobs.push(job);
                break;
            case LowPriorityJob:
                a->second->_lowPriorityJobs.push(job);
                break;
            default:
                tgtAssert(false, "Should not reach this - wrong job type!");
                break;
            } 
        }
        else {
193
            tgtAssert(false, "Specified context not found. Contexts must be registered before they can have jobs.");
194
        }
195
196
197

        _evaluationCondition.notify_all();
    }
198
199

    void OpenGLJobProcessor::registerContext(tgt::GLCanvas* context) {
schultezub's avatar
schultezub committed
200
#ifdef CAMPVIS_DEBUG
201
202
203
204
205
206
207
208
209
210
        tbb::concurrent_hash_map<tgt::GLCanvas*, PerContextJobQueue*>::const_accessor a;
        if (_contextQueueMap.find(a, context))
            tgtAssert(false, "Contexts shall only be registered once!");
#endif

        PerContextJobQueue* newQueue = new PerContextJobQueue;
        _contextQueueMap.insert(std::make_pair(context, newQueue));
        _contexts.push_back(context);
    }

211
    void OpenGLJobProcessor::deregisterContext(tgt::GLCanvas* context) {
212
        tbb::concurrent_hash_map<tgt::GLCanvas*, PerContextJobQueue*>::accessor a;
213
214
215
216
        if (_contextQueueMap.find(a, context)) {
            delete a->second;
            _contextQueueMap.erase(a);
        }
217
218
219

        if (_currentContext == context)
            _currentContext.compare_and_swap(0, context);
220
221
    }

222
223
224
225
226
227
228
229
230
231
232
    tgt::GLCanvas* OpenGLJobProcessor::iKnowWhatImDoingGetArbitraryContext() {
        if (_currentContext != 0)
            return _currentContext;
        else if (!_contexts.empty())
            return _contexts.front();
        else {
            tgtAssert(false, "No Contexts registered!");
            return 0;
        }
    }

233
234
}