main buffered_surface.c
  1/* wld: buffered_surface.c
  2 *
  3 * Copyright (c) 2013, 2014 Michael Forney
  4 *
  5 * Permission is hereby granted, free of charge, to any person obtaining a copy
  6 * of this software and associated documentation files (the "Software"), to deal
  7 * in the Software without restriction, including without limitation the rights
  8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9 * copies of the Software, and to permit persons to whom the Software is
 10 * furnished to do so, subject to the following conditions:
 11 *
 12 * The above copyright notice and this permission notice shall be included in
 13 * all copies or substantial portions of the Software.
 14 *
 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 21 * SOFTWARE.
 22 */
 23
 24#include "wld-private.h"
 25
 26#include "interface/surface.h"
 27IMPL(buffered_surface, wld_surface)
 28
 29struct buffer_entry {
 30	struct buffer *buffer;
 31	bool busy;
 32};
 33
 34struct buffered_surface {
 35	struct wld_surface base;
 36
 37	struct wld_context *context;
 38	struct buffer_entry *entries, *back;
 39	unsigned entries_size, entries_capacity;
 40
 41	struct buffer_socket *buffer_socket;
 42
 43	uint32_t width, height;
 44	enum wld_format format;
 45	uint32_t flags;
 46};
 47
 48struct wld_surface *
 49buffered_surface_create(struct wld_context *context, uint32_t width, uint32_t height,
 50                        uint32_t format, uint32_t flags, struct buffer_socket *buffer_socket)
 51{
 52	struct buffered_surface *surface;
 53
 54	if (!(surface = malloc(sizeof *surface)))
 55		return NULL;
 56
 57	surface_initialize(&surface->base, &wld_surface_impl);
 58	surface->context = context;
 59	surface->entries = NULL;
 60	surface->back = NULL;
 61	surface->entries_size = 0;
 62	surface->entries_capacity = 0;
 63	surface->buffer_socket = buffer_socket;
 64	surface->width = width;
 65	surface->height = height;
 66	surface->format = format;
 67	surface->flags = flags;
 68
 69	return &surface->base;
 70}
 71
 72pixman_region32_t *
 73surface_damage(struct wld_surface *base, pixman_region32_t *new_damage)
 74{
 75	struct buffered_surface *surface = buffered_surface(base);
 76	struct buffer *back_buffer;
 77	unsigned index;
 78
 79	if (pixman_region32_not_empty(new_damage)) {
 80		for (index = 0; index < surface->entries_size; ++index) {
 81			pixman_region32_union(&surface->entries[index].buffer->base.damage,
 82			                      &surface->entries[index].buffer->base.damage,
 83			                      new_damage);
 84		}
 85	}
 86
 87	if (!(back_buffer = surface_back(base)))
 88		return NULL;
 89
 90	return &back_buffer->base.damage;
 91}
 92
 93struct buffer *
 94surface_back(struct wld_surface *base)
 95{
 96	struct buffered_surface *surface = buffered_surface(base);
 97	unsigned index;
 98
 99	if (surface->back)
100		return surface->back->buffer;
101
102	/* The buffer socket may need to process any incoming buffer releases. */
103	if (surface->buffer_socket)
104		surface->buffer_socket->impl->process(surface->buffer_socket);
105
106	for (index = 0; index < surface->entries_size; ++index) {
107		if (!surface->entries[index].busy) {
108			surface->back = &surface->entries[index];
109			return surface->back->buffer;
110		}
111	}
112
113	/* If there are no free buffers, we need to allocate another one. */
114	struct buffer *buffer;
115
116	buffer = surface->context->impl->create_buffer(surface->context, surface->width, surface->height,
117	                                               surface->format, surface->flags);
118
119	if (!buffer)
120		goto error0;
121
122	if (surface->entries_size == surface->entries_capacity) {
123		struct buffer_entry *new_entries;
124		size_t new_capacity = surface->entries_capacity * 2 + 1;
125
126		new_entries = realloc(surface->entries,
127		                      new_capacity * sizeof surface->entries[0]);
128
129		if (!new_entries)
130			goto error1;
131
132		surface->entries = new_entries;
133		surface->entries_capacity = new_capacity;
134	}
135
136	surface->back = &surface->entries[surface->entries_size++];
137	*surface->back = (struct buffer_entry){
138		.buffer = buffer,
139		.busy = false
140	};
141
142	return buffer;
143
144error1:
145	wld_buffer_unreference(&buffer->base);
146error0:
147	return NULL;
148}
149
150struct buffer *
151surface_take(struct wld_surface *base)
152{
153	struct buffered_surface *surface = buffered_surface(base);
154	struct buffer *buffer;
155
156	if (!(buffer = surface_back(base)))
157		return NULL;
158
159	surface->back->busy = true;
160	surface->back = NULL;
161	pixman_region32_clear(&buffer->base.damage);
162
163	return buffer;
164}
165
166bool
167surface_release(struct wld_surface *base, struct buffer *buffer)
168{
169	struct buffered_surface *surface = buffered_surface(base);
170	unsigned index;
171
172	for (index = 0; index < surface->entries_size; ++index) {
173		if (surface->entries[index].buffer == buffer) {
174			surface->entries[index].busy = false;
175			return true;
176		}
177	}
178
179	return false;
180}
181
182bool
183surface_swap(struct wld_surface *base)
184{
185	struct buffered_surface *surface = buffered_surface(base);
186	struct buffer *buffer;
187
188	if (!surface->buffer_socket)
189		return false;
190
191	if (!(buffer = surface_back(base)))
192		return false;
193
194	if (!surface->buffer_socket->impl->attach(surface->buffer_socket, buffer))
195		return false;
196
197	surface->back->busy = true;
198	surface->back = NULL;
199	pixman_region32_clear(&buffer->base.damage);
200
201	return true;
202}
203
204void
205surface_destroy(struct wld_surface *base)
206{
207	struct buffered_surface *surface = buffered_surface(base);
208	unsigned index;
209
210	if (surface->buffer_socket)
211		surface->buffer_socket->impl->destroy(surface->buffer_socket);
212
213	for (index = 0; index < surface->entries_size; ++index)
214		wld_buffer_unreference(&surface->entries[index].buffer->base);
215
216	free(surface->entries);
217	free(surface);
218}