Krita Source Code Documentation
Loading...
Searching...
No Matches
KisDabRenderingQueue.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2017 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
10#include "KisRenderedDab.h"
11#include "kis_painter.h"
13
14#include <QMutex>
15#include <QMutexLocker>
17
18#include "kis_algebra_2d.h"
19
21{
23 void getDabType(bool hasDabInCache,
26 /* out */
28 bool *shouldUseCache) override
29 {
30 Q_UNUSED(hasDabInCache);
31 Q_UNUSED(resources);
32 Q_UNUSED(request);
33
34 di->needsPostprocessing = false;
35 *shouldUseCache = false;
36 }
37
39 {
40 Q_UNUSED(resources);
41 return false;
42 }
43
44 };
45
46 Private(const KoColorSpace *_colorSpace,
47 KisDabCacheUtils::ResourcesFactory _resourcesFactory)
49 colorSpace(_colorSpace),
50 resourcesFactory(_resourcesFactory),
51 paintDeviceAllocator(new KisOptimizedByteArray::PooledMemoryAllocator()),
53 avgDabSize(50)
54 {
56 }
57
59 // clear the jobs, so that they would not keep references to any
60 // paint devices anymore
61 jobs.clear();
62
63 qDeleteAll(cachedResources);
64 cachedResources.clear();
65 }
66
71 QScopedPointer<CacheInterface> cacheInterface;
73 qreal averageOpacity = 0.0;
74
76
79
80 QMutex mutex;
81
84
85 int calculateLastDabJobIndex(int startSearchIndex);
86 void cleanPaintedDabs();
88 bool hasPreparedDabsImpl() const;
89
92};
93
94
97 : m_d(new Private(cs, resourcesFactory))
98{
99}
100
104
106{
107 if (startSearchIndex < 0) {
108 startSearchIndex = jobs.size() - 1;
109 }
110
111 // try to use cached value
112 if (startSearchIndex >= lastDabJobInQueue) {
113 return lastDabJobInQueue;
114 }
115
116 // if we are below the cached value, just iterate through the dabs
117 // (which is extremely(!) slow)
118 for (int i = startSearchIndex; i >= 0; i--) {
119 if (jobs[i]->type == KisDabRenderingJob::Dab) {
120 return i;
121 }
122 }
123
124 return -1;
125}
126
128 qreal opacity, qreal flow)
129{
130 QMutexLocker l(&m_d->mutex);
131
132 const int seqNo = m_d->nextSeqNoToUse++;
133
134 KisDabCacheUtils::DabRenderingResources *resources = m_d->fetchResourcesFromCache();
136
137 // We should sync the cached brush into the current seqNo
138 resources->syncResourcesToSeqNo(seqNo, request.info);
139
140 const int lastDabJobIndex = m_d->lastDabJobInQueue;
141
143
144 bool shouldUseCache = false;
145 m_d->cacheInterface->getDabType(lastDabJobIndex >= 0, resources, request, &job->generationInfo, &shouldUseCache);
146
147 m_d->putResourcesToCache(resources);
148 resources = nullptr;
149
150 job->type = !shouldUseCache ? KisDabRenderingJob::Dab
151 : job->generationInfo.needsPostprocessing ? KisDabRenderingJob::Postprocess
153
154 if (job->type == KisDabRenderingJob::Dab) {
155 job->status = KisDabRenderingJob::Running;
156 } else if (job->type == KisDabRenderingJob::Postprocess ||
157 job->type == KisDabRenderingJob::Copy) {
158
160 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(lastDabJobIndex < m_d->jobs.size(), KisDabRenderingJobSP());
161
162 if (m_d->jobs[lastDabJobIndex]->status == KisDabRenderingJob::Completed) {
163 if (job->type == KisDabRenderingJob::Postprocess) {
164 job->status = KisDabRenderingJob::Running;
165 job->originalDevice = m_d->jobs[lastDabJobIndex]->originalDevice;
166 } else if (job->type == KisDabRenderingJob::Copy) {
167 job->status = KisDabRenderingJob::Completed;
168 job->originalDevice = m_d->jobs[lastDabJobIndex]->originalDevice;
169 job->postprocessedDevice = m_d->jobs[lastDabJobIndex]->postprocessedDevice;
170 m_d->avgExecutionTime(0);
171 }
172 }
173 }
174
175 m_d->jobs.append(job);
176
177 KisDabRenderingJobSP jobToRun;
178 if (job->status == KisDabRenderingJob::Running) {
179 jobToRun = job;
180 }
181
182 if (job->type == KisDabRenderingJob::Dab) {
183 m_d->lastDabJobInQueue = m_d->jobs.size() - 1;
184 m_d->cleanPaintedDabs();
185 }
186
187 // collect some statistics about the dab
188 m_d->avgDabSize(KisAlgebra2D::maxDimension(job->generationInfo.dstDabRect));
189
190 return jobToRun;
191}
192
194{
195 QMutexLocker l(&m_d->mutex);
196
197 QList<KisDabRenderingJobSP> dependentJobs;
198
202 auto finishedJobIt =
203 std::lower_bound(m_d->jobs.begin(), m_d->jobs.end(), seqNo,
205
206 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(finishedJobIt != m_d->jobs.end(), dependentJobs);
207 KisDabRenderingJobSP finishedJob = *finishedJobIt;
208
210 KIS_SAFE_ASSERT_RECOVER_NOOP(finishedJob->seqNo == seqNo);
211 KIS_SAFE_ASSERT_RECOVER_NOOP(finishedJob->originalDevice);
212 KIS_SAFE_ASSERT_RECOVER_NOOP(finishedJob->postprocessedDevice);
213
214 finishedJob->status = KisDabRenderingJob::Completed;
215
216 if (finishedJob->type == KisDabRenderingJob::Dab) {
217 for (auto it = finishedJobIt + 1; it != m_d->jobs.end(); ++it) {
218 KisDabRenderingJobSP j = *it;
219
220 // next dab job closes the chain
221 if (j->type == KisDabRenderingJob::Dab) break;
222
223 // the non 'dab'-type job couldn't have
224 // been started before the source ob was completed
226
227 if (j->type == KisDabRenderingJob::Copy) {
228
229 j->originalDevice = finishedJob->originalDevice;
230 j->postprocessedDevice = finishedJob->postprocessedDevice;
232 m_d->avgExecutionTime(0);
233
234 } else if (j->type == KisDabRenderingJob::Postprocess) {
235
236 j->originalDevice = finishedJob->originalDevice;
237 j->status = KisDabRenderingJob::Running;
238 dependentJobs << j;
239 }
240 }
241 }
242
243 if (usecsTime >= 0) {
244 m_d->avgExecutionTime(usecsTime);
245 }
246
247 return dependentJobs;
248}
249
251{
252 const int nextToBePainted = lastPaintedJob + 1;
253 const int lastSourceJob = calculateLastDabJobIndex(qMin(nextToBePainted, jobs.size() - 1));
254
255 if (lastPaintedJob >= 0) {
256 int numRemovedJobs = 0;
257 int numRemovedJobsBeforeLastSource = 0;
258
259 auto it = jobs.begin();
260 for (int i = 0; i <= lastPaintedJob; i++) {
261 KisDabRenderingJobSP job = *it;
262
263 if (i < lastSourceJob || job->type != KisDabRenderingJob::Dab){
264
265 KIS_ASSERT_RECOVER_NOOP(job->originalDevice);
266
267 it = jobs.erase(it);
268 numRemovedJobs++;
269 if (i < lastDabJobInQueue) {
270 numRemovedJobsBeforeLastSource++;
271 }
272
273 } else {
274 ++it;
275 }
276 }
277
278 KIS_SAFE_ASSERT_RECOVER_RETURN(jobs.size() > 0);
279
280 lastPaintedJob -= numRemovedJobs;
281 lastDabJobInQueue -= numRemovedJobsBeforeLastSource;
282 }
283}
284
286 int oneTimeLimit,
287 bool *someDabsLeft)
288{
289 QMutexLocker l(&m_d->mutex);
290
291 QList<KisRenderedDab> renderedDabs;
292 if (m_d->jobs.isEmpty()) return renderedDabs;
293
295 m_d->jobs.isEmpty() ||
296 m_d->jobs.first()->type == KisDabRenderingJob::Dab);
297
298 const int copyJobAfterInclusive =
299 returnMutableDabs && !m_d->dabsHaveSeparateOriginal() ?
300 m_d->lastDabJobInQueue :
301 std::numeric_limits<int>::max();
302
303 if (oneTimeLimit < 0) {
304 oneTimeLimit = std::numeric_limits<int>::max();
305 }
306
307 for (int i = 0; i < m_d->jobs.size() && oneTimeLimit > 0; i++, oneTimeLimit--) {
308 KisDabRenderingJobSP j = m_d->jobs[i];
309
310 if (j->status != KisDabRenderingJob::Completed) break;
311
312 if (i <= m_d->lastPaintedJob) continue;
313
314 KisRenderedDab dab;
315 KisFixedPaintDeviceSP resultDevice = j->postprocessedDevice;
316
317 if (i >= copyJobAfterInclusive) {
318 resultDevice = new KisFixedPaintDevice(*resultDevice);
319 }
320
321 dab.device = resultDevice;
322 dab.offset = j->dstDabOffset();
323 dab.opacity = j->opacity;
324 dab.flow = j->flow;
325
326 m_d->averageOpacity = KisPainter::blendAverageOpacity(j->opacity, m_d->averageOpacity);
327 dab.averageOpacity = m_d->averageOpacity;
328
329
330 renderedDabs.append(dab);
331
332 m_d->lastPaintedJob = i;
333 }
334
335 m_d->cleanPaintedDabs();
336
337 if (someDabsLeft) {
338 *someDabsLeft = m_d->hasPreparedDabsImpl();
339 }
340
341 return renderedDabs;
342}
343
345{
346 const int nextToBePainted = lastPaintedJob + 1;
347
348 return
349 nextToBePainted >= 0 &&
350 nextToBePainted < jobs.size() &&
351 jobs[nextToBePainted]->status == KisDabRenderingJob::Completed;
352}
353
354
356{
357 QMutexLocker l(&m_d->mutex);
358 return m_d->hasPreparedDabsImpl();
359}
360
362{
363 m_d->cacheInterface.reset(interface);
364}
365
367{
372 return new KisFixedPaintDevice(m_d->colorSpace, m_d->paintDeviceAllocator);
373}
374
376{
377 QMutexLocker l(&m_d->mutex);
378 return m_d->avgExecutionTime.rollingMean() / 1000.0;
379}
380
382{
383 QMutexLocker l(&m_d->mutex);
384 return qRound(m_d->avgDabSize.rollingMean());
385}
386
388{
390
391 const bool result = cacheInterface->hasSeparateOriginal(resources);
392
393 putResourcesToCache(resources);
394
395 return result;
396}
397
399{
401
402 // fetch/create a temporary resources object
403 if (!cachedResources.isEmpty()) {
404 resources = cachedResources.takeLast();
405 } else {
406 resources = resourcesFactory();
407 }
408
409 return resources;
410}
411
413{
414 cachedResources << resources;
415}
416
418{
419 // TODO: make a separate lock for that
420 QMutexLocker l(&m_d->mutex);
421 return m_d->fetchResourcesFromCache();
422}
423
425{
426 QMutexLocker l(&m_d->mutex);
427 m_d->putResourcesToCache(resources);
428}
429
431{
432 QMutexLocker l(&m_d->mutex);
433
434 return m_d->jobs.size();
435}
436
QSharedPointer< KisDabRenderingJob > KisDabRenderingJobSP
QList< KisRenderedDab > takeReadyDabs(bool returnMutableDabs=false, int oneTimeLimit=-1, bool *someDabsLeft=0)
void setCacheInterface(CacheInterface *interface)
KisDabCacheUtils::DabRenderingResources * fetchResourcesFromCache()
KisDabRenderingJobSP addDab(const KisDabCacheUtils::DabRequestInfo &request, qreal opacity, qreal flow)
const QScopedPointer< Private > m_d
KisFixedPaintDeviceSP fetchCachedPaintDevice()
KisDabRenderingQueue(const KoColorSpace *cs, KisDabCacheUtils::ResourcesFactory resourcesFactory)
QList< KisDabRenderingJobSP > notifyJobFinished(int seqNo, int usecsTime=-1)
void putResourcesToCache(KisDabCacheUtils::DabRenderingResources *resources)
static qreal blendAverageOpacity(qreal opacity, qreal averageOpacity)
A simple wrapper class that hides boost includes from QtCreator preventing it from crashing when one ...
#define KIS_SAFE_ASSERT_RECOVER_BREAK(cond)
Definition kis_assert.h:127
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:97
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
auto maxDimension(Size size) -> decltype(size.width())
std::function< DabRenderingResources *()> ResourcesFactory
auto mem_less(MemTypeNoRef Class::*ptr, MemType &&value)
mem_less is an unary functor that compares a member of the object to a given value
Definition KisMpl.h:269
virtual void syncResourcesToSeqNo(int seqNo, const KisPaintInformation &info)
void getDabType(bool hasDabInCache, KisDabCacheUtils::DabRenderingResources *resources, const KisDabCacheUtils::DabRequestInfo &request, KisDabCacheUtils::DabGenerationInfo *di, bool *shouldUseCache) override
bool hasSeparateOriginal(KisDabCacheUtils::DabRenderingResources *resources) const override
KisDabCacheUtils::DabRenderingResources * fetchResourcesFromCache()
Private(const KoColorSpace *_colorSpace, KisDabCacheUtils::ResourcesFactory _resourcesFactory)
QList< KisDabRenderingJobSP > jobs
KisRollingMeanAccumulatorWrapper avgDabSize
QList< KisDabCacheUtils::DabRenderingResources * > cachedResources
int calculateLastDabJobIndex(int startSearchIndex)
KisRollingMeanAccumulatorWrapper avgExecutionTime
KisDabCacheUtils::ResourcesFactory resourcesFactory
QSharedPointer< KisOptimizedByteArray::MemoryAllocator > paintDeviceAllocator
QScopedPointer< CacheInterface > cacheInterface
void putResourcesToCache(KisDabCacheUtils::DabRenderingResources *resources)
KisFixedPaintDeviceSP device