1 /*
2  * Copyright (c) 2013-2021, The OSKAR Developers.
3  * See the LICENSE file at the top-level directory of this distribution.
4  */
5 
6 #include "utility/oskar_device.h"
7 #include "utility/oskar_timer.h"
8 #include "utility/oskar_thread.h"
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <time.h>
12 
13 #ifndef OSKAR_OS_WIN
14 #include <sys/time.h>
15 #include <unistd.h>
16 #else
17 #include <windows.h>
18 #endif
19 
20 #ifdef OSKAR_HAVE_CUDA
21 #include <cuda_runtime_api.h>
22 #endif
23 
24 #ifdef OSKAR_HAVE_OPENCL
25 #ifdef __APPLE__
26 #include <OpenCL/opencl.h>
27 #else
28 #include <CL/cl.h>
29 #endif
30 #endif
31 
32 #ifdef __cplusplus
33 extern "C" {
34 #endif
35 
36 struct oskar_Timer
37 {
38     oskar_Mutex* mutex;
39 #ifdef OSKAR_HAVE_CUDA
40     cudaEvent_t start_cuda, end_cuda;
41 #endif
42     double start, elapsed;
43 #ifdef OSKAR_OS_WIN
44     double freq;
45 #endif
46     int type, paused;
47 };
48 
oskar_get_wtime(oskar_Timer * timer)49 static double oskar_get_wtime(oskar_Timer* timer)
50 {
51 #if defined(OSKAR_OS_WIN)
52     /* Windows-specific version. */
53     LARGE_INTEGER cntr;
54     QueryPerformanceCounter(&cntr);
55     return (double)(cntr.QuadPart) / timer->freq;
56 #elif _POSIX_MONOTONIC_CLOCK > 0
57     /* Use monotonic clock if available. */
58     struct timespec ts;
59     (void)timer;
60     clock_gettime(CLOCK_MONOTONIC, &ts);
61     return ts.tv_sec + ts.tv_nsec / 1e9;
62 #else
63     /* Use gettimeofday() as fallback. */
64     struct timeval tv;
65     (void)timer;
66     gettimeofday(&tv, 0);
67     return tv.tv_sec + tv.tv_usec / 1e6;
68 #endif
69 }
70 
oskar_timer_create(int type)71 oskar_Timer* oskar_timer_create(int type)
72 {
73     oskar_Timer* timer = 0;
74 #ifdef OSKAR_OS_WIN
75     LARGE_INTEGER freq;
76 #endif
77     timer = (oskar_Timer*) calloc(1, sizeof(oskar_Timer));
78     timer->mutex = oskar_mutex_create();
79 #ifdef OSKAR_OS_WIN
80     QueryPerformanceFrequency(&freq);
81     timer->freq = (double)(freq.QuadPart);
82 #endif
83     timer->type = type;
84     timer->paused = 1;
85 #ifdef OSKAR_HAVE_CUDA
86     if (timer->type == OSKAR_TIMER_CUDA)
87     {
88         cudaEventCreate(&timer->start_cuda);
89         cudaEventCreate(&timer->end_cuda);
90     }
91 #endif
92     return timer;
93 }
94 
oskar_timer_free(oskar_Timer * timer)95 void oskar_timer_free(oskar_Timer* timer)
96 {
97     if (!timer) return;
98 #ifdef OSKAR_HAVE_CUDA
99     if (timer->type == OSKAR_TIMER_CUDA)
100     {
101         cudaEventDestroy(timer->start_cuda);
102         cudaEventDestroy(timer->end_cuda);
103     }
104 #endif
105     oskar_mutex_free(timer->mutex);
106     free(timer);
107 }
108 
oskar_timer_elapsed(oskar_Timer * timer)109 double oskar_timer_elapsed(oskar_Timer* timer)
110 {
111     /* If timer is paused, return immediately with current elapsed time. */
112     if (timer->paused) return timer->elapsed;
113 
114 #ifdef OSKAR_HAVE_CUDA
115     if (timer->type == OSKAR_TIMER_CUDA)
116     {
117         float millisec = 0.0f;
118         oskar_mutex_lock(timer->mutex);
119 
120         /* Get elapsed time since start. */
121         cudaEventRecord(timer->end_cuda, 0);
122         cudaEventSynchronize(timer->end_cuda);
123         cudaEventElapsedTime(&millisec, timer->start_cuda, timer->end_cuda);
124 
125         /* Increment elapsed time and restart. */
126         timer->elapsed += millisec / 1000.0;
127         cudaEventRecord(timer->start_cuda, 0);
128     }
129     else
130 #endif
131 #ifdef OSKAR_HAVE_OPENCL
132     if (timer->type == OSKAR_TIMER_CL)
133     {
134         /* Get elapsed time since start. */
135         /* Note that clGetEventProfilingInfo() seems to be broken
136          * in various ways, at least on macOS. So just enqueue a marker,
137          * wait on its event and use the native timer instead. */
138         cl_event event = 0;
139         clEnqueueMarkerWithWaitList(oskar_device_queue_cl(), 0, NULL, &event);
140         clWaitForEvents(1, &event);
141 
142         /* Increment elapsed time and restart. */
143         oskar_mutex_lock(timer->mutex);
144         const double now = oskar_get_wtime(timer);
145         timer->elapsed += (now - timer->start);
146         timer->start = now;
147     }
148     else
149 #endif
150     {
151         oskar_mutex_lock(timer->mutex);
152         const double now = oskar_get_wtime(timer);
153 
154         /* Increment elapsed time and restart. */
155         timer->elapsed += (now - timer->start);
156         timer->start = now;
157     }
158     oskar_mutex_unlock(timer->mutex);
159     return timer->elapsed;
160 }
161 
oskar_timer_pause(oskar_Timer * timer)162 void oskar_timer_pause(oskar_Timer* timer)
163 {
164     if (timer->paused) return;
165     (void)oskar_timer_elapsed(timer);
166     timer->paused = 1;
167 }
168 
oskar_timer_reset(oskar_Timer * timer)169 void oskar_timer_reset(oskar_Timer* timer)
170 {
171     oskar_mutex_lock(timer->mutex);
172     timer->paused = 1;
173     timer->start = 0.0;
174     timer->elapsed = 0.0;
175     oskar_mutex_unlock(timer->mutex);
176 }
177 
oskar_timer_resume(oskar_Timer * timer)178 void oskar_timer_resume(oskar_Timer* timer)
179 {
180     if (!timer->paused) return;
181     oskar_timer_restart(timer);
182 }
183 
oskar_timer_restart(oskar_Timer * timer)184 void oskar_timer_restart(oskar_Timer* timer)
185 {
186     timer->paused = 0;
187 #ifdef OSKAR_HAVE_CUDA
188     if (timer->type == OSKAR_TIMER_CUDA)
189     {
190         cudaEventRecord(timer->start_cuda, 0);
191         return;
192     }
193 #endif
194     timer->start = oskar_get_wtime(timer);
195 }
196 
oskar_timer_start(oskar_Timer * timer)197 void oskar_timer_start(oskar_Timer* timer)
198 {
199     timer->elapsed = 0.0;
200     oskar_timer_restart(timer);
201 }
202 
203 #ifdef __cplusplus
204 }
205 #endif
206