spec: Add support for VK_KHR_incremental_present
[platform/core/uifw/vulkan-wsi-tizen.git] / src / wsi / swapchain_tdm.c
1 /*
2  * Copyright © 2016 S-Core Corporation
3  * Copyright © 2016-2017 Samsung Electronics co., Ltd. All Rights Reserved.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  */
24
25 #include "wsi.h"
26 #include <pthread.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <stdio.h>
30
31 typedef struct vk_swapchain_tdm vk_swapchain_tdm_t;
32
33 struct vk_swapchain_tdm {
34         tdm_display                             *tdm_display;
35         tdm_output                              *tdm_output;
36         tdm_layer                               *tdm_layer;
37         const tdm_output_mode   *tdm_mode;
38         tdm_output_dpms                  tdm_dpms;
39
40         tbm_surface_queue_h              tbm_queue;
41
42         VkPresentModeKHR                 present_mode;
43         tbm_surface_h                   *buffers;
44         tbm_surface_h                    front_buffer;
45         pthread_mutex_t                  front_mutex;
46         pthread_mutex_t                  free_queue_mutex;
47         pthread_cond_t                   free_queue_cond;
48 };
49
50 static int swapchain_tdm_timeline_key;
51 static int swapchain_tdm_timestamp_key;
52
53 static void
54 swapchain_tdm_increase_timestamp(tbm_surface_h tbm_surface)
55 {
56         tbm_fd           timeline;
57         if (tbm_surface_internal_get_user_data(tbm_surface,
58                                                                                    (unsigned long)&swapchain_tdm_timeline_key,
59                                                                                    (void **)(&timeline))) {
60                 if (timeline != -1) {
61                         if (tbm_sync_timeline_inc(timeline, 1) == 0) {
62                                 char buf[1024];
63                                 strerror_r(errno, buf, sizeof(buf));
64                                 VK_ERROR("Failed to increase TBM sync timeline: %d(%s)", errno, buf);
65                         }
66                 }
67         }
68 }
69
70 static void
71 swapchain_tdm_output_commit_cb(tdm_output *output, unsigned int sequence,
72                                                            unsigned int tv_sec, unsigned int tv_usec,
73                                                            void *user_data)
74 {
75         vk_swapchain_t                  *chain = user_data;
76         vk_swapchain_tdm_t              *swapchain_tdm = chain->backend_data;
77
78         if (pthread_mutex_lock(&swapchain_tdm->front_mutex))
79                 VK_ERROR("pthread_mutex_lock front buffer failed\n");
80
81         if (swapchain_tdm->front_buffer) {
82                 swapchain_tdm_increase_timestamp(swapchain_tdm->front_buffer);
83                 swapchain_tdm->front_buffer = NULL;
84         }
85         pthread_mutex_unlock(&swapchain_tdm->front_mutex);
86 }
87
88 static VkResult
89 swapchain_tdm_queue_present_image(VkQueue                                        queue,
90                                                                   vk_swapchain_t                        *chain,
91                                                                   tbm_surface_h                          tbm_surface,
92                                                                   int                                            sync_fd,
93                                                                   int num_rects,
94                                                                   const VkRectLayerKHR *rects)
95 {
96         tbm_surface_queue_error_e        tsq_err;
97         tdm_error                                        tdm_err;
98         vk_swapchain_tdm_t                      *swapchain_tdm = chain->backend_data;
99
100         if (sync_fd != -1) {
101                 if (tbm_sync_fence_wait(sync_fd, -1) != 1) {
102                         char buf[1024];
103                         strerror_r(errno, buf, sizeof(buf));
104                         VK_ERROR("Failed to wait sync. | error: %d(%s)", errno, buf);
105                 }
106                 close(sync_fd);
107         }
108
109         tsq_err = tbm_surface_queue_enqueue(swapchain_tdm->tbm_queue, tbm_surface);
110         VK_CHECK(tsq_err == TBM_SURFACE_QUEUE_ERROR_NONE, return VK_ERROR_SURFACE_LOST_KHR,
111                          "tbm_surface_queue_enqueue failed.\n");
112
113         tsq_err = tbm_surface_queue_acquire(swapchain_tdm->tbm_queue, &tbm_surface);
114         VK_CHECK(tsq_err == TBM_SURFACE_QUEUE_ERROR_NONE, return VK_ERROR_SURFACE_LOST_KHR,
115                          "tbm_surface_queue_acquire failed.\n");
116
117         tdm_err = tdm_layer_set_buffer(swapchain_tdm->tdm_layer, tbm_surface);
118         VK_CHECK(tdm_err == TDM_ERROR_NONE, return VK_ERROR_SURFACE_LOST_KHR,
119                          "tdm_layer_set_buffer failed.\n");
120
121         tdm_err = tdm_output_commit(swapchain_tdm->tdm_output, 0,
122                                                                 swapchain_tdm_output_commit_cb, chain);
123         VK_CHECK(tdm_err == TDM_ERROR_NONE, return VK_ERROR_SURFACE_LOST_KHR,
124                          "tdm_output_commit failed.\n");
125
126         tdm_err = tdm_display_handle_events(swapchain_tdm->tdm_display);
127         VK_CHECK(tdm_err == TDM_ERROR_NONE, return VK_ERROR_SURFACE_LOST_KHR,
128                          "tdm_display_handle_events failed.\n");
129
130         if (pthread_mutex_lock(&swapchain_tdm->free_queue_mutex))
131                 VK_ERROR("pthread_mutex_lock free queue failed\n");
132
133         tsq_err = tbm_surface_queue_release(swapchain_tdm->tbm_queue, tbm_surface);
134         VK_CHECK(tsq_err == TBM_SURFACE_QUEUE_ERROR_NONE, return VK_ERROR_SURFACE_LOST_KHR,
135                          "tbm_surface_queue_release failed.\n");
136
137         pthread_mutex_unlock(&swapchain_tdm->free_queue_mutex);
138         pthread_cond_signal(&swapchain_tdm->free_queue_cond);
139
140         if (pthread_mutex_lock(&swapchain_tdm->front_mutex))
141                 VK_ERROR("pthread_mutex_lock front buffer failed\n");
142         if (swapchain_tdm->front_buffer)
143                 swapchain_tdm_increase_timestamp(swapchain_tdm->front_buffer);
144         swapchain_tdm->front_buffer = tbm_surface;
145         pthread_mutex_unlock(&swapchain_tdm->front_mutex);
146
147         return VK_SUCCESS;
148 }
149
150 static void
151 swapchain_tdm_timeline_destroy_cb(void *user_data)
152 {
153         if (((tbm_fd)user_data) != -1)
154                 close((tbm_fd)user_data);
155 }
156
157 static tbm_fd
158 swapchain_tdm_get_sync_fence(tbm_surface_h tbm_surface)
159 {
160         tbm_fd           timeline;
161         uint32_t         timestamp;
162         tbm_fd           fence = -1;
163
164         if (tbm_surface_internal_get_user_data(tbm_surface,
165                                                                                    (unsigned long)&swapchain_tdm_timeline_key,
166                                                                                    (void **)(&timeline))) {
167                 char name[32];
168
169                 tbm_surface_internal_get_user_data(tbm_surface,
170                                                                                    (unsigned long)&swapchain_tdm_timestamp_key,
171                                                                                    (void **)(&timestamp));
172                 timestamp++;
173                 tbm_surface_internal_set_user_data(tbm_surface,
174                                                                                    (unsigned long)&swapchain_tdm_timestamp_key,
175                                                                                    (void *)timestamp);
176
177                 snprintf(name, 32, "%d",
178                                  tbm_bo_export(tbm_surface_internal_get_bo(tbm_surface, 0)));
179                 fence = tbm_sync_fence_create(timeline,
180                                                                           name,
181                                                                           timestamp);
182                 if (fence == -1) {
183                         char buf[1024];
184                         strerror_r(errno, buf, sizeof(buf));
185                         VK_ERROR("Failed to create TBM sync fence: %d(%s)", errno, buf);
186                 }
187         } else {
188                 /* make timeline and timestamp */
189                 timeline = tbm_sync_timeline_create();
190                 tbm_surface_internal_add_user_data(tbm_surface,
191                                                                                    (unsigned long)&swapchain_tdm_timeline_key,
192                                                                                    swapchain_tdm_timeline_destroy_cb);
193                 tbm_surface_internal_set_user_data(tbm_surface,
194                                                                                    (unsigned long)&swapchain_tdm_timeline_key,
195                                                                                    (void *)timeline);
196                 if (timeline == -1) {
197                         char buf[1024];
198                         strerror_r(errno, buf, sizeof(buf));
199                         VK_ERROR("Failed to create TBM sync timeline: %d(%s)", errno, buf);
200                 } else {
201                         tbm_surface_internal_set_user_data(tbm_surface,
202                                                                                            (unsigned long)&swapchain_tdm_timestamp_key,
203                                                                                            (void *)0);
204                 }
205         }
206         return fence;
207 };
208
209 static VkResult
210 swapchain_tdm_acquire_next_image(VkDevice                        device,
211                                                                  vk_swapchain_t         *chain,
212                                                                  uint64_t                        timeout,
213                                                                  tbm_surface_h          *tbm_surface,
214                                                                  int                            *sync)
215 {
216         tbm_surface_queue_error_e        tsq_err;
217         vk_swapchain_tdm_t                      *swapchain_tdm = chain->backend_data;
218         struct timespec                          abs_time;
219
220         if (timeout != UINT64_MAX) {
221                 clock_gettime(CLOCK_REALTIME, &abs_time);
222                 abs_time.tv_sec += (timeout / 1000000000L);
223                 abs_time.tv_nsec += (timeout % 1000000000L);
224                 if (abs_time.tv_nsec >= 1000000000L) {
225                         abs_time.tv_sec += (abs_time.tv_nsec / 1000000000L);
226                         abs_time.tv_nsec = (abs_time.tv_nsec % 1000000000L);
227                 }
228         }
229
230         if (pthread_mutex_lock(&swapchain_tdm->free_queue_mutex))
231                 VK_ERROR("pthread_mutex_lock free queue failed\n");
232
233         while (tbm_surface_queue_can_dequeue(swapchain_tdm->tbm_queue, 0) == 0) {
234                 if (timeout != UINT64_MAX) {
235                         int ret;
236                         ret = pthread_cond_timedwait(&swapchain_tdm->free_queue_cond,
237                                                                                  &swapchain_tdm->free_queue_mutex,
238                                                                                  &abs_time);
239                         if (ret == ETIMEDOUT) {
240                                 /* timeout */
241                                 pthread_mutex_unlock(&swapchain_tdm->free_queue_mutex);
242                                 return VK_TIMEOUT;
243                         }
244                 } else {
245                         pthread_cond_wait(&swapchain_tdm->free_queue_cond,
246                                                           &swapchain_tdm->free_queue_mutex);
247                 }
248         }
249
250         tsq_err = tbm_surface_queue_dequeue(swapchain_tdm->tbm_queue, tbm_surface);
251         VK_CHECK(tsq_err == TBM_SURFACE_QUEUE_ERROR_NONE, return VK_ERROR_SURFACE_LOST_KHR,
252                          "tbm_surface_queue_dequeue failed.\n");
253         pthread_mutex_unlock(&swapchain_tdm->free_queue_mutex);
254
255         if (sync)
256                 *sync = swapchain_tdm_get_sync_fence(*tbm_surface);
257
258         return VK_SUCCESS;
259 }
260
261 static void
262 swapchain_tdm_deinit(VkDevice            device,
263                                          vk_swapchain_t *chain)
264 {
265         vk_swapchain_tdm_t                      *swapchain_tdm = chain->backend_data;
266
267         if (swapchain_tdm) {
268                 tdm_output_set_dpms(swapchain_tdm->tdm_output, swapchain_tdm->tdm_dpms);
269
270                 pthread_cond_destroy(&swapchain_tdm->free_queue_cond);
271                 pthread_mutex_destroy(&swapchain_tdm->free_queue_mutex);
272                 pthread_mutex_destroy(&swapchain_tdm->front_mutex);
273
274                 if (swapchain_tdm->tbm_queue)
275                         tbm_surface_queue_destroy(swapchain_tdm->tbm_queue);
276
277                 if (swapchain_tdm->buffers)
278                         vk_free(chain->allocator, swapchain_tdm->buffers);
279                 vk_free(chain->allocator, swapchain_tdm);
280         }
281 }
282
283 static VkResult
284 swapchain_tdm_get_buffers(VkDevice                       device,
285                                                   vk_swapchain_t        *chain,
286                                                   tbm_surface_h    **buffers,
287                                                   uint32_t                      *buffer_count)
288 {
289         uint32_t                                         i;
290         tbm_surface_queue_error_e        tsq_err;
291         tdm_error                                        tdm_err;
292         tbm_surface_info_s                       surf_info;
293         tdm_info_layer                           tdm_info;
294         vk_swapchain_tdm_t                      *swapchain_tdm = chain->backend_data;
295
296         *buffer_count = tbm_surface_queue_get_size(swapchain_tdm->tbm_queue);
297         swapchain_tdm->buffers = vk_alloc(chain->allocator,
298                                                                                            sizeof(tbm_surface_h) * *buffer_count,
299                                                                                            VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
300         VK_CHECK(swapchain_tdm->buffers, return VK_ERROR_OUT_OF_HOST_MEMORY, "vk_alloc() failed.\n");
301
302         for (i = 0; i < *buffer_count; i++) {
303                 tsq_err = tbm_surface_queue_dequeue(swapchain_tdm->tbm_queue,
304                                                                                         &swapchain_tdm->buffers[i]);
305                 VK_CHECK(tsq_err == TBM_SURFACE_QUEUE_ERROR_NONE,
306                                  return VK_ERROR_SURFACE_LOST_KHR,
307                                  "tbm_surface_queue_dequeue failed.\n");
308         }
309
310         for (i = 0; i < *buffer_count; i++) {
311                 tsq_err = tbm_surface_queue_release(swapchain_tdm->tbm_queue,
312                                                                                         swapchain_tdm->buffers[i]);
313                 VK_CHECK(tsq_err == TBM_SURFACE_QUEUE_ERROR_NONE,
314                                  return VK_ERROR_SURFACE_LOST_KHR,
315                                  "tbm_surface_queue_enqueue failed.\n");
316         }
317
318         *buffers = swapchain_tdm->buffers;
319
320         tdm_err = tdm_output_get_dpms(swapchain_tdm->tdm_output, &swapchain_tdm->tdm_dpms);
321         tdm_err = tdm_output_set_dpms(swapchain_tdm->tdm_output, TDM_OUTPUT_DPMS_ON);
322         VK_CHECK(tdm_err == TDM_ERROR_NONE, return VK_ERROR_SURFACE_LOST_KHR,
323                          "tdm_output_set_dpms failed.\n");
324
325         tdm_err = tdm_output_set_mode(swapchain_tdm->tdm_output,
326                                                                   swapchain_tdm->tdm_mode);
327         VK_CHECK(tdm_err == TDM_ERROR_NONE, return VK_ERROR_SURFACE_LOST_KHR,
328                          "tdm_output_set_mode failed.\n");
329
330         tbm_surface_get_info((*buffers)[0], &surf_info);
331
332         /* from enlightenment */
333         tdm_info.src_config.size.h = surf_info.planes[0].stride;
334         tdm_info.src_config.size.v = surf_info.height;
335
336         tdm_info.src_config.pos.x = 0;
337         tdm_info.src_config.pos.y = 0;
338         tdm_info.src_config.pos.w = surf_info.width;
339         tdm_info.src_config.pos.h = surf_info.height;
340
341         tdm_info.src_config.format = surf_info.format;
342
343         tdm_info.dst_pos.x = 0;
344         tdm_info.dst_pos.y = 0;
345         tdm_info.dst_pos.w = surf_info.width;
346         tdm_info.dst_pos.h = surf_info.height;
347
348         tdm_info.transform = TDM_TRANSFORM_NORMAL;
349
350         tdm_err = tdm_layer_set_info(swapchain_tdm->tdm_layer, &tdm_info);
351         VK_CHECK(tdm_err == TDM_ERROR_NONE, return VK_ERROR_SURFACE_LOST_KHR,
352                          "tdm_layer_set_info failed.\n");
353
354 /*      tdm_err = tdm_layer_set_buffer_queue(swapchain_tdm->tdm_layer,
355                                                                                  swapchain_tdm->tbm_queue);
356         VK_CHECK(tdm_err == TDM_ERROR_NONE, return VK_ERROR_SURFACE_LOST_KHR,
357                          "tdm_layer_set_buffer_queue failed.\n");*/
358
359         return VK_SUCCESS;
360 }
361
362 VkResult
363 swapchain_tdm_init(VkDevice                                                      device,
364                                    const VkSwapchainCreateInfoKHR       *info,
365                                    vk_swapchain_t                                       *chain,
366                                    tbm_format                                            format)
367 {
368         VkIcdSurfaceDisplay     *surface = (VkIcdSurfaceDisplay *)(uintptr_t)info->surface;
369         vk_display_mode_t       *disp_mode = (vk_display_mode_t *)(uintptr_t)surface->displayMode;
370         vk_display_t            *disp = disp_mode->display;
371         vk_swapchain_tdm_t      *swapchain_tdm;
372
373         swapchain_tdm = vk_alloc(chain->allocator, sizeof(vk_swapchain_tdm_t),
374                                                          VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
375         VK_CHECK(swapchain_tdm, return VK_ERROR_OUT_OF_HOST_MEMORY, "vk_alloc() failed.\n");
376
377         memset(swapchain_tdm, 0x00, sizeof(*swapchain_tdm));
378         chain->backend_data = swapchain_tdm;
379
380         swapchain_tdm->tdm_display = disp->pdev->tdm_display;
381         swapchain_tdm->tdm_output = disp->tdm_output;
382         swapchain_tdm->tdm_layer = disp->pdev->planes[surface->planeIndex].tdm_layer;
383
384         swapchain_tdm->tbm_queue =
385         /*      tbm_surface_queue_sequence_create(info->minImageCount,
386                                                                                   info->imageExtent.width,
387                                                                                   info->imageExtent.height,
388                                                                                   format, TBM_BO_SCANOUT);*/
389                 tbm_surface_queue_create(info->minImageCount,
390                                                                  info->imageExtent.width,
391                                                                  info->imageExtent.height,
392                                                                  format, TBM_BO_SCANOUT);
393
394         VK_CHECK(swapchain_tdm->tbm_queue, return VK_ERROR_SURFACE_LOST_KHR,
395                          "tbm_surface_queue_create failed.\n");
396
397         if (pthread_mutex_init(&swapchain_tdm->front_mutex, NULL))
398                 VK_ERROR("pthread_mutex_init front buffer failed\n");
399         if (pthread_mutex_init(&swapchain_tdm->free_queue_mutex, NULL))
400                 VK_ERROR("pthread_mutex_init free queue failed\n");
401         if (pthread_cond_init(&swapchain_tdm->free_queue_cond, NULL))
402                 VK_ERROR("pthread_cond_init free queue failed\n");
403
404         swapchain_tdm->present_mode = info->presentMode;
405         swapchain_tdm->tdm_mode = disp_mode->tdm_mode;
406         swapchain_tdm->tdm_dpms = TDM_OUTPUT_DPMS_OFF;
407
408         chain->get_buffers = swapchain_tdm_get_buffers;
409         chain->deinit = swapchain_tdm_deinit;
410         chain->acquire_image = swapchain_tdm_acquire_next_image;
411         chain->present_image = swapchain_tdm_queue_present_image;
412
413         return VK_SUCCESS;
414 }
415