1 /**
2 * \file render_queue.c
3 * \brief Manage a queue of rendered backbuffers to display
4 *
5 * \author David Hogan <david.q.hogan@gmail.com>
6 */
7
8 /* This file is part of VICE, the Versatile Commodore Emulator.
9 * See README for copyright notice.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24 * 02111-1307 USA.
25 *
26 */
27
28 #include "vice.h"
29 #include "render_queue.h"
30
31 #include <assert.h>
32 #include <pthread.h>
33 #include <string.h>
34
35 #include "lib.h"
36 #include "vsyncapi.h"
37
38 #define LOCK() pthread_mutex_lock(&rq->lock)
39 #define UNLOCK() pthread_mutex_unlock(&rq->lock)
40
41 typedef struct vice_render_queue_s {
42 pthread_mutex_t lock;
43
44 /** Holds all currently unused backbuffers */
45 backbuffer_t *backbuffer_stack[RENDER_QUEUE_MAX_BACKBUFFERS];
46
47 /** How many unused backbuffers in the stack */
48 unsigned int backbuffer_stack_size;
49
50 /** Holds the queue of backbuffers ready to render */
51 backbuffer_t *render_queue[RENDER_QUEUE_MAX_BACKBUFFERS];
52
53 /** How many backbuffers in render queue */
54 unsigned int render_queue_length;
55
56 /** Index of next backbuffer to render in render_queue */
57 unsigned int render_queue_next;
58
59 /** Allows discarding of late buffer returns */
60 unsigned int backbuffer_generation;
61 } render_queue_t;
62
free_backbuffer(backbuffer_t * backbuffer)63 static void free_backbuffer(backbuffer_t *backbuffer) {
64 lib_free(backbuffer->pixel_data);
65 lib_free(backbuffer);
66 }
67
68 /****/
69
70 /** \brief Allocate, initialise and return a new render queue. */
render_queue_create(void)71 void *render_queue_create(void)
72 {
73 render_queue_t *rq;
74 backbuffer_t *bb;
75
76 rq = lib_calloc(1, sizeof(render_queue_t));
77 pthread_mutex_init(&rq->lock, NULL);
78
79 /* Seed the pool with the maximum number of backbuffers */
80 for (int i = 0; i < RENDER_QUEUE_MAX_BACKBUFFERS; i++) {
81
82 bb = lib_malloc(sizeof(backbuffer_t));
83 bb->pixel_data = lib_malloc(0);
84 bb->pixel_data_size_bytes = 0;
85 bb->width = 0;
86 bb->height = 0;
87 bb->pixel_aspect_ratio = 0.0f;
88
89 rq->backbuffer_stack[rq->backbuffer_stack_size++] = bb;
90 }
91
92 return rq;
93 }
94
95 /** \brief Destroy a render queue. */
render_queue_destroy(void * render_queue)96 void render_queue_destroy(void *render_queue)
97 {
98 render_queue_t *rq = (render_queue_t *)render_queue;
99 int i;
100
101 /* The unused backbuffers */
102 for (i = 0; i < rq->backbuffer_stack_size; i++) {
103 free_backbuffer(rq->backbuffer_stack[i]);
104 }
105
106 /* The backbuffers queued for rendering */
107 for (i = 0; i < rq->render_queue_length; i++) {
108 free_backbuffer(rq->render_queue[rq->render_queue_next++]);
109 rq->render_queue_next = rq->render_queue_next % RENDER_QUEUE_MAX_BACKBUFFERS;
110 }
111
112 pthread_mutex_destroy(&rq->lock);
113 lib_free(render_queue);
114 }
115
116 /****/
117
118 /** Obtain unused backbuffer for offscreen rendering, or NULL if none available */
render_queue_get_from_pool(void * render_queue,int pixel_data_size_bytes)119 backbuffer_t *render_queue_get_from_pool(void *render_queue, int pixel_data_size_bytes)
120 {
121 render_queue_t *rq = (render_queue_t *)render_queue;
122 backbuffer_t *bb;
123
124 LOCK();
125
126 if (!rq->backbuffer_stack_size) {
127 /* no buffers available, skip this frame */
128 UNLOCK();
129 return NULL;
130 }
131
132 bb = rq->backbuffer_stack[rq->backbuffer_stack_size - 1];
133 rq->backbuffer_stack_size--;
134
135 UNLOCK();
136
137 /* Make sure there's at least the requested size in bytes */
138 if (bb->pixel_data_size_bytes < pixel_data_size_bytes) {
139 lib_free(bb->pixel_data);
140 bb->pixel_data = lib_malloc(pixel_data_size_bytes);
141 bb->pixel_data_size_bytes = pixel_data_size_bytes;
142 }
143
144 bb->width = 0;
145 bb->height = 0;
146 bb->pixel_aspect_ratio = 0.0f;
147
148 return bb;
149 }
150
151 /** Add backbuffer to the queue of backbuffers to be displayed */
render_queue_enqueue_for_display(void * render_queue,backbuffer_t * backbuffer)152 void render_queue_enqueue_for_display(void *render_queue, backbuffer_t *backbuffer)
153 {
154 render_queue_t *rq = (render_queue_t *)render_queue;
155
156 LOCK();
157
158 assert(rq->render_queue_length < RENDER_QUEUE_MAX_BACKBUFFERS);
159
160 rq->render_queue[(rq->render_queue_next + rq->render_queue_length) % RENDER_QUEUE_MAX_BACKBUFFERS] = backbuffer;
161 rq->render_queue_length++;
162
163 UNLOCK();
164 }
165
render_queue_length(void * render_queue)166 unsigned int render_queue_length(void *render_queue)
167 {
168 render_queue_t *rq = (render_queue_t *)render_queue;
169
170 unsigned int render_queue_length;
171
172 LOCK();
173
174 render_queue_length = rq->render_queue_length;
175
176 UNLOCK();
177
178 return render_queue_length;
179 }
180
181 /** Obtain rendered backbuffer for display, or NULL if none available */
render_queue_dequeue_for_display(void * render_queue)182 backbuffer_t *render_queue_dequeue_for_display(void *render_queue)
183 {
184 render_queue_t *rq = (render_queue_t *)render_queue;
185
186 LOCK();
187
188 /* Are there any available? */
189 if (!rq->render_queue_length) {
190 UNLOCK();
191 return NULL;
192 }
193
194 void *backbuffer = rq->render_queue[rq->render_queue_next];
195 rq->render_queue_next = (rq->render_queue_next + 1) % RENDER_QUEUE_MAX_BACKBUFFERS;
196 rq->render_queue_length--;
197
198 UNLOCK();
199
200 return backbuffer;
201 }
202
render_queue_return_to_pool(void * render_queue,backbuffer_t * backbuffer)203 void render_queue_return_to_pool(void *render_queue, backbuffer_t *backbuffer)
204 {
205 render_queue_t *rq = (render_queue_t *)render_queue;
206
207 LOCK();
208
209 assert(rq->backbuffer_stack_size < RENDER_QUEUE_MAX_BACKBUFFERS);
210
211 rq->backbuffer_stack[rq->backbuffer_stack_size] = backbuffer;
212 rq->backbuffer_stack_size++;
213
214 UNLOCK();
215 }