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 }