GCC Code Coverage Report


Directory: avs_core/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 0.0% 0 / 0 / 172
Functions: 0.0% 0 / 0 / 14
Branches: 0.0% 0 / 0 / 157

core/Prefetcher.cpp
Line Branch Exec Source
1 #include "Prefetcher.h"
2
3 #include <vector>
4 #include <mutex>
5 #include <atomic>
6 #ifdef INTEL_INTRINSICS
7 #include <mmintrin.h>
8 #endif
9 #include <avisynth.h>
10 #include "ThreadPool.h"
11 #include "ObjectPool.h"
12 #include "LruCache.h"
13 #include "InternalEnvironment.h"
14 #include "internal.h"
15
16 struct PrefetcherJobParams
17 {
18 int frame;
19 Prefetcher* prefetcher;
20 LruCache<size_t, PVideoFrame>::handle cache_handle;
21 };
22
23 struct PrefetcherPimpl
24 {
25 PClip child;
26 VideoInfo vi;
27
28 // The number of threads to use for prefetching
29 const int nThreads;
30
31 // Maximum number of frames to prefetch
32 const int nPrefetchFrames;
33
34 ThreadPool* thread_pool;
35
36 ObjectPool<PrefetcherJobParams> JobParamsPool;
37 std::mutex params_pool_mutex;
38
39 // Contains the pattern we are locked on to
40 int LockedPattern;
41
42 // The number of consecutive frames Pattern has repeated itself
43 int PatternHits;
44
45 // The number of consecutive frames LockedPattern was invalid
46 int PatternMisses;
47
48 // The current pattern that we are not locked on to
49 int Pattern;
50
51 // True if we have found a pattern to lock onto
52 bool IsLocked;
53
54 // The frame number that GetFrame() has been called with the last time
55 int LastRequestedFrame;
56
57 std::shared_ptr<LruCache<size_t, PVideoFrame> > VideoCache;
58 std::atomic<int> running_workers;
59 std::mutex worker_exception_mutex;
60 std::exception_ptr worker_exception;
61 bool worker_exception_present;
62 InternalEnvironment *EnvI;
63
64 PrefetcherPimpl(const PClip& _child, int _nThreads, int _nPrefetchFrames, IScriptEnvironment2 *env2) :
65 child(_child),
66 vi(_child->GetVideoInfo()),
67 nThreads(_nThreads),
68 nPrefetchFrames(_nPrefetchFrames),
69 thread_pool(NULL),
70 LockedPattern(1),
71 PatternHits(0),
72 PatternMisses(0),
73 Pattern(1),
74 IsLocked(false),
75 LastRequestedFrame(0),
76 VideoCache(NULL),
77 running_workers(0),
78 worker_exception_present(0),
79 EnvI(static_cast<InternalEnvironment*>(env2))
80 {
81 thread_pool = EnvI->NewThreadPool(nThreads);
82 }
83
84 ~PrefetcherPimpl()
85 {
86 for (void* data : thread_pool->Finish()) {
87 PrefetcherJobParams *ptr = (PrefetcherJobParams*)data;
88 VideoCache->rollback(&ptr->cache_handle);
89 }
90 }
91 };
92
93
94 // The number of intervals a pattern has to repeat itself to become (un)locked
95 #define PATTERN_LOCK_LENGTH 3
96
97 AVSValue Prefetcher::ThreadWorker(IScriptEnvironment2* env, void* data)
98 {
99 PrefetcherJobParams *ptr = (PrefetcherJobParams*)data;
100 Prefetcher *prefetcher = ptr->prefetcher;
101 int n = ptr->frame;
102 LruCache<size_t, PVideoFrame>::handle cache_handle = ptr->cache_handle;
103
104 {
105 std::lock_guard<std::mutex> lock(prefetcher->_pimpl->params_pool_mutex);
106 prefetcher->_pimpl->JobParamsPool.Destruct(ptr);
107 }
108
109 try
110 {
111 cache_handle.first->value = prefetcher->_pimpl->child->GetFrame(n, env);
112 #ifdef INTEL_INTRINSICS
113 #ifdef X86_32
114 _mm_empty();
115 #endif
116 #endif
117
118 prefetcher->_pimpl->VideoCache->commit_value(&cache_handle);
119 --(prefetcher->_pimpl->running_workers);
120 }
121 catch(...)
122 {
123 prefetcher->_pimpl->VideoCache->rollback(&cache_handle);
124
125 std::lock_guard<std::mutex> lock(prefetcher->_pimpl->worker_exception_mutex);
126 prefetcher->_pimpl->worker_exception = std::current_exception();
127 prefetcher->_pimpl->worker_exception_present = true;
128 --(prefetcher->_pimpl->running_workers);
129 }
130
131 return AVSValue();
132 }
133
134 Prefetcher::Prefetcher(const PClip& _child, int _nThreads, int _nPrefetchFrames, IScriptEnvironment *env) :
135 _pimpl(NULL)
136 {
137 _pimpl = new PrefetcherPimpl(_child, _nThreads, _nPrefetchFrames, static_cast<IScriptEnvironment2*>(env));
138 _pimpl->VideoCache = std::make_shared<LruCache<size_t, PVideoFrame> >(_pimpl->nPrefetchFrames*2, CACHE_NO_RESIZE);
139 }
140
141 void Prefetcher::Destroy()
142 {
143 if (_pimpl)
144 {
145 PrefetcherPimpl *pimpl = _pimpl;
146 delete pimpl;
147 _pimpl = nullptr;
148 }
149 }
150
151 Prefetcher::~Prefetcher()
152 {
153 Destroy();
154 }
155
156 size_t Prefetcher::NumPrefetchThreads() const
157 {
158 return _pimpl->nThreads;
159 }
160
161 int __stdcall Prefetcher::SchedulePrefetch(int current_n, int prefetch_start, InternalEnvironment* env)
162 {
163 int n = prefetch_start;
164 while ((_pimpl->running_workers < _pimpl->nPrefetchFrames) && (std::abs(n - current_n) < _pimpl->nPrefetchFrames) )
165 {
166 n += _pimpl->IsLocked ? _pimpl->LockedPattern : 1;
167 if (n >= _pimpl->vi.num_frames)
168 break;
169
170 PVideoFrame result;
171 LruCache<size_t, PVideoFrame>::handle cache_handle;
172 switch(_pimpl->VideoCache->lookup(n, &cache_handle, false, result))
173 {
174 case LRU_LOOKUP_NOT_FOUND:
175 {
176 PrefetcherJobParams *p = NULL;
177 {
178 std::lock_guard<std::mutex> lock(_pimpl->params_pool_mutex);
179 p = _pimpl->JobParamsPool.Construct();
180 }
181 p->frame = n;
182 p->prefetcher = this;
183 p->cache_handle = cache_handle;
184 ++_pimpl->running_workers;
185 _pimpl->thread_pool->QueueJob(ThreadWorker, p, env, NULL);
186 break;
187 }
188 case LRU_LOOKUP_FOUND_AND_READY: // Fall-through intentional
189 case LRU_LOOKUP_NO_CACHE: // Fall-through intentional
190 case LRU_LOOKUP_FOUND_BUT_NOTAVAIL:
191 {
192 break;
193 }
194 default:
195 {
196 assert(0);
197 break;
198 }
199 }
200 } // switch
201
202 return n;
203 }
204
205 PVideoFrame __stdcall Prefetcher::GetFrame(int n, IScriptEnvironment* env_)
206 {
207 InternalEnvironment* IEnv = GetAndRevealCamouflagedEnv(env_);
208 IScriptEnvironment* env = static_cast<IScriptEnvironment*>(IEnv);
209
210 if (IEnv->GetSuppressThreadCount() > 0) {
211 // do not use thread when invoke running
212 return _pimpl->child->GetFrame(n, env);
213 }
214
215 int pattern = n - _pimpl->LastRequestedFrame;
216 _pimpl->LastRequestedFrame = n;
217 if (pattern == 0)
218 pattern = 1;
219
220 if (_pimpl->IsLocked)
221 {
222 if (_pimpl->LockedPattern == pattern)
223 {
224 _pimpl->PatternHits = 0; // Tracks Pattern
225 _pimpl->PatternMisses = 0; // Tracks LockedPattern
226 }
227 else if (_pimpl->Pattern == pattern)
228 {
229 _pimpl->PatternHits++; // Tracks Pattern
230 _pimpl->PatternMisses++; // Tracks LockedPattern
231 }
232 else
233 {
234 _pimpl->PatternHits = 0; // Tracks Pattern
235 _pimpl->PatternMisses++; // Tracks LockedPattern
236 }
237 _pimpl->Pattern = pattern;
238
239 if ((_pimpl->PatternMisses >= PATTERN_LOCK_LENGTH) && (_pimpl->PatternHits >= PATTERN_LOCK_LENGTH))
240 {
241 _pimpl->LockedPattern = _pimpl->Pattern;
242 _pimpl->PatternHits = 0; // Tracks Pattern
243 _pimpl->PatternMisses = 0; // Tracks LockedPattern
244 }
245 else if ((_pimpl->PatternMisses >= PATTERN_LOCK_LENGTH) && (_pimpl->PatternHits < PATTERN_LOCK_LENGTH))
246 {
247 _pimpl->IsLocked = false;
248 }
249 }
250 else
251 {
252 if (_pimpl->Pattern == pattern)
253 {
254 _pimpl->PatternHits++; // Tracks Pattern
255 _pimpl->PatternMisses = 0; // Tracks Pattern
256 }
257 else
258 {
259 _pimpl->PatternHits = 0; // Tracks Pattern
260 _pimpl->PatternMisses++; // Tracks Pattern
261 }
262
263 if (_pimpl->PatternHits >= PATTERN_LOCK_LENGTH)
264 {
265 _pimpl->LockedPattern = pattern;
266 _pimpl->PatternMisses = 0; // Tracks Pattern
267 _pimpl->IsLocked = true;
268 }
269 }
270
271
272 {
273 std::lock_guard<std::mutex> lock(_pimpl->worker_exception_mutex);
274 if (_pimpl->worker_exception_present)
275 {
276 std::rethrow_exception(_pimpl->worker_exception);
277 }
278 }
279
280
281 // Prefetch 1
282 int prefetch_pos = SchedulePrefetch(n, n, IEnv);
283
284 // Get requested frame
285 PVideoFrame result;
286 LruCache<size_t, PVideoFrame>::handle cache_handle;
287 // fill result if LRU_LOOKUP_FOUND_AND_READY
288 switch(_pimpl->VideoCache->lookup(n, &cache_handle, true, result))
289 {
290 case LRU_LOOKUP_NOT_FOUND:
291 {
292 try
293 {
294 result = _pimpl->child->GetFrame(n, env); // P.F. fill result before Commit!
295 cache_handle.first->value = result;
296 // cache_handle.first->value = _pimpl->child->GetFrame(n, env); // P.F. before Commit!
297 #ifdef INTEL_INTRINSICS
298 #ifdef X86_32
299 _mm_empty();
300 #endif
301 #endif
302 _pimpl->VideoCache->commit_value(&cache_handle);
303 }
304 catch(...)
305 {
306 _pimpl->VideoCache->rollback(&cache_handle);
307 throw;
308 }
309 break;
310 }
311 case LRU_LOOKUP_FOUND_AND_READY:
312 {
313 //result = cache_handle.first->value; // old method, result is filled already
314 break;
315 }
316 case LRU_LOOKUP_NO_CACHE:
317 {
318 result = _pimpl->child->GetFrame(n, env);
319 break;
320 }
321 case LRU_LOOKUP_FOUND_BUT_NOTAVAIL: // Fall-through intentional
322 default:
323 {
324 assert(0);
325 break;
326 }
327 }
328
329 // Prefetch 2
330 SchedulePrefetch(n, prefetch_pos, IEnv);
331
332 return result;
333 }
334
335 bool __stdcall Prefetcher::GetParity(int n)
336 {
337 return _pimpl->child->GetParity(n);
338 }
339
340 void __stdcall Prefetcher::GetAudio(void* buf, int64_t start, int64_t count, IScriptEnvironment* env_)
341 {
342 InternalEnvironment* IEnv = GetAndRevealCamouflagedEnv(env_);
343 IScriptEnvironment* env = static_cast<IScriptEnvironment*>(IEnv);
344
345 _pimpl->child->GetAudio(buf, start, count, env);
346 }
347
348 int __stdcall Prefetcher::SetCacheHints(int cachehints, int frame_range)
349 {
350 AVS_UNUSED(frame_range);
351 if (CACHE_GET_MTMODE == cachehints)
352 return MT_NICE_FILTER;
353
354 if (CACHE_GET_DEV_TYPE == cachehints)
355 return (_pimpl->child->GetVersion() >= 5) ? _pimpl->child->SetCacheHints(CACHE_GET_DEV_TYPE, 0) : 0;
356
357 return 0;
358 }
359
360 const VideoInfo& __stdcall Prefetcher::GetVideoInfo()
361 {
362 return _pimpl->vi;
363 }
364
365 AVSValue Prefetcher::Create(AVSValue args, void*, IScriptEnvironment* env)
366 {
367 InternalEnvironment *envi = static_cast<InternalEnvironment*>(env);
368 PClip child = args[0].AsClip();
369
370 int PrefetchThreads = args[1].AsInt((int)envi->GetEnvProperty(AEP_PHYSICAL_CPUS)+1);
371 int PrefetchFrames = args[2].AsInt(PrefetchThreads * 2);
372
373 if (PrefetchThreads > 0 && PrefetchFrames > 0)
374 {
375 return new Prefetcher(child, PrefetchThreads, PrefetchFrames, env);
376 }
377 else
378 return child;
379 }
380