Merge branch '7.8'
[mesa.git] / src / gallium / state_trackers / vega / vg_manager.c
1 /*
2 * Mesa 3-D graphics library
3 * Version: 7.9
4 *
5 * Copyright 2009 VMware, Inc. All Rights Reserved.
6 * Copyright (C) 2010 LunarG Inc.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included
16 * in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
25 *
26 * Authors:
27 * Chia-I Wu <olv@lunarg.com>
28 */
29
30 #include "state_tracker/st_api.h"
31
32 #include "pipe/p_context.h"
33 #include "pipe/p_screen.h"
34 #include "util/u_memory.h"
35 #include "util/u_inlines.h"
36 #include "util/u_format.h"
37 #include "util/u_sampler.h"
38
39 #include "vg_manager.h"
40 #include "vg_context.h"
41 #include "image.h"
42 #include "mask.h"
43
44 static struct pipe_resource *
45 create_texture(struct pipe_context *pipe, enum pipe_format format,
46 VGint width, VGint height)
47 {
48 struct pipe_resource templ;
49
50 memset(&templ, 0, sizeof(templ));
51
52 if (format != PIPE_FORMAT_NONE) {
53 templ.format = format;
54 }
55 else {
56 templ.format = PIPE_FORMAT_B8G8R8A8_UNORM;
57 }
58
59 templ.target = PIPE_TEXTURE_2D;
60 templ.width0 = width;
61 templ.height0 = height;
62 templ.depth0 = 1;
63 templ.last_level = 0;
64
65 if (util_format_get_component_bits(format, UTIL_FORMAT_COLORSPACE_ZS, 1)) {
66 templ.bind = PIPE_BIND_DEPTH_STENCIL;
67 } else {
68 templ.bind = (PIPE_BIND_DISPLAY_TARGET |
69 PIPE_BIND_RENDER_TARGET |
70 PIPE_BIND_SAMPLER_VIEW);
71 }
72
73 return pipe->screen->resource_create(pipe->screen, &templ);
74 }
75
76 static struct pipe_sampler_view *
77 create_tex_and_view(struct pipe_context *pipe, enum pipe_format format,
78 VGint width, VGint height)
79 {
80 struct pipe_resource *texture;
81 struct pipe_sampler_view view_templ;
82 struct pipe_sampler_view *view;
83
84 texture = create_texture(pipe, format, width, height);
85
86 if (!texture)
87 return NULL;
88
89 u_sampler_view_default_template(&view_templ, texture, texture->format);
90 view = pipe->create_sampler_view(pipe, texture, &view_templ);
91 /* want the texture to go away if the view is freed */
92 pipe_resource_reference(&texture, NULL);
93
94 return view;
95 }
96
97 static void
98 setup_new_alpha_mask(struct vg_context *ctx, struct st_framebuffer *stfb)
99 {
100 struct pipe_context *pipe = ctx->pipe;
101 struct pipe_sampler_view *old_sampler_view = stfb->alpha_mask_view;
102
103 /*
104 we use PIPE_FORMAT_B8G8R8A8_UNORM because we want to render to
105 this texture and use it as a sampler, so while this wastes some
106 space it makes both of those a lot simpler
107 */
108 stfb->alpha_mask_view = create_tex_and_view(pipe,
109 PIPE_FORMAT_B8G8R8A8_UNORM, stfb->width, stfb->height);
110
111 if (!stfb->alpha_mask_view) {
112 if (old_sampler_view)
113 pipe_sampler_view_reference(&old_sampler_view, NULL);
114 return;
115 }
116
117 /* XXX could this call be avoided? */
118 vg_validate_state(ctx);
119
120 /* alpha mask starts with 1.f alpha */
121 mask_fill(0, 0, stfb->width, stfb->height, 1.f);
122
123 /* if we had an old surface copy it over */
124 if (old_sampler_view) {
125 struct pipe_surface *surface = pipe->screen->get_tex_surface(
126 pipe->screen,
127 stfb->alpha_mask_view->texture,
128 0, 0, 0,
129 PIPE_BIND_RENDER_TARGET |
130 PIPE_BIND_BLIT_DESTINATION);
131 struct pipe_surface *old_surface = pipe->screen->get_tex_surface(
132 pipe->screen,
133 old_sampler_view->texture,
134 0, 0, 0,
135 PIPE_BIND_BLIT_SOURCE);
136 pipe->surface_copy(pipe,
137 surface,
138 0, 0,
139 old_surface,
140 0, 0,
141 MIN2(old_surface->width, surface->width),
142 MIN2(old_surface->height, surface->height));
143 if (surface)
144 pipe_surface_reference(&surface, NULL);
145 if (old_surface)
146 pipe_surface_reference(&old_surface, NULL);
147 }
148
149 /* Free the old texture
150 */
151 if (old_sampler_view)
152 pipe_sampler_view_reference(&old_sampler_view, NULL);
153 }
154
155 static boolean
156 vg_context_update_depth_stencil_rb(struct vg_context * ctx,
157 uint width, uint height)
158 {
159 struct st_renderbuffer *dsrb = ctx->draw_buffer->dsrb;
160 struct pipe_context *pipe = ctx->pipe;
161 unsigned surface_usage;
162
163 if ((dsrb->width == width && dsrb->height == height) && dsrb->texture)
164 return FALSE;
165
166 /* unreference existing ones */
167 pipe_surface_reference(&dsrb->surface, NULL);
168 pipe_resource_reference(&dsrb->texture, NULL);
169 dsrb->width = dsrb->height = 0;
170
171 /* Probably need dedicated flags for surface usage too:
172 */
173 surface_usage = (PIPE_BIND_RENDER_TARGET |
174 PIPE_BIND_BLIT_SOURCE |
175 PIPE_BIND_BLIT_DESTINATION);
176
177 dsrb->texture = create_texture(pipe, dsrb->format, width, height);
178 if (!dsrb->texture)
179 return TRUE;
180
181 dsrb->surface = pipe->screen->get_tex_surface(pipe->screen,
182 dsrb->texture,
183 0, 0, 0,
184 surface_usage);
185 if (!dsrb->surface) {
186 pipe_resource_reference(&dsrb->texture, NULL);
187 return TRUE;
188 }
189
190 dsrb->width = width;
191 dsrb->height = height;
192
193 assert(dsrb->surface->width == width);
194 assert(dsrb->surface->height == height);
195
196 return TRUE;
197 }
198
199 static boolean
200 vg_context_update_color_rb(struct vg_context *ctx, struct pipe_resource *pt)
201 {
202 struct st_renderbuffer *strb = ctx->draw_buffer->strb;
203 struct pipe_screen *screen = ctx->pipe->screen;
204
205 if (strb->texture == pt) {
206 pipe_resource_reference(&pt, NULL);
207 return FALSE;
208 }
209
210 /* unreference existing ones */
211 pipe_surface_reference(&strb->surface, NULL);
212 pipe_resource_reference(&strb->texture, NULL);
213 strb->width = strb->height = 0;
214
215 strb->texture = pt;
216 strb->surface = screen->get_tex_surface(screen, strb->texture, 0, 0, 0,
217 PIPE_BIND_RENDER_TARGET |
218 PIPE_BIND_BLIT_SOURCE |
219 PIPE_BIND_BLIT_DESTINATION);
220 if (!strb->surface) {
221 pipe_resource_reference(&strb->texture, NULL);
222 return TRUE;
223 }
224
225 strb->width = pt->width0;
226 strb->height = pt->height0;
227
228 return TRUE;
229 }
230
231 static void
232 vg_context_update_draw_buffer(struct vg_context *ctx, struct pipe_resource *pt)
233 {
234 struct st_framebuffer *stfb = ctx->draw_buffer;
235 boolean new_cbuf, new_zsbuf, new_size;
236
237 new_cbuf = vg_context_update_color_rb(ctx, pt);
238 new_zsbuf =
239 vg_context_update_depth_stencil_rb(ctx, pt->width0, pt->height0);
240
241 new_size = (stfb->width != pt->width0 || stfb->height != pt->height0);
242 stfb->width = pt->width0;
243 stfb->height = pt->height0;
244
245 if (new_cbuf || new_zsbuf || new_size) {
246 struct pipe_framebuffer_state *state = &ctx->state.g3d.fb;
247
248 memset(state, 0, sizeof(struct pipe_framebuffer_state));
249 state->width = stfb->width;
250 state->height = stfb->height;
251 state->nr_cbufs = 1;
252 state->cbufs[0] = stfb->strb->surface;
253 state->zsbuf = stfb->dsrb->surface;
254
255 cso_set_framebuffer(ctx->cso_context, state);
256 }
257
258 if (new_zsbuf || new_size) {
259 ctx->state.dirty |= VIEWPORT_DIRTY;
260 ctx->state.dirty |= DEPTH_STENCIL_DIRTY;/*to reset the scissors*/
261
262 ctx->pipe->clear(ctx->pipe, PIPE_CLEAR_DEPTHSTENCIL, NULL, 0.0, 0);
263
264 /* we need all the other state already set */
265
266 setup_new_alpha_mask(ctx, stfb);
267
268 pipe_sampler_view_reference( &stfb->blend_texture_view, NULL);
269 stfb->blend_texture_view = create_tex_and_view(ctx->pipe,
270 PIPE_FORMAT_B8G8R8A8_UNORM, stfb->width, stfb->height);
271 }
272 }
273
274 /**
275 * Flush the front buffer if the current context renders to the front buffer.
276 */
277 void
278 vg_manager_flush_frontbuffer(struct vg_context *ctx)
279 {
280 struct st_framebuffer *stfb = ctx->draw_buffer;
281
282 if (!stfb)
283 return;
284
285 switch (stfb->strb_att) {
286 case ST_ATTACHMENT_FRONT_LEFT:
287 case ST_ATTACHMENT_FRONT_RIGHT:
288 stfb->iface->flush_front(stfb->iface, stfb->strb_att);
289 break;
290 default:
291 break;
292 }
293 }
294
295 /**
296 * Re-validate the framebuffer.
297 */
298 void
299 vg_manager_validate_framebuffer(struct vg_context *ctx)
300 {
301 struct st_framebuffer *stfb = ctx->draw_buffer;
302 struct pipe_resource *pt;
303
304 /* no binding surface */
305 if (!stfb)
306 return;
307
308 if (!p_atomic_read(&ctx->draw_buffer_invalid))
309 return;
310
311 /* validate the fb */
312 if (!stfb->iface->validate(stfb->iface, &stfb->strb_att, 1, &pt) || !pt)
313 return;
314
315 /*
316 * unset draw_buffer_invalid first because vg_context_update_draw_buffer
317 * will cause the framebuffer to be validated again because of a call to
318 * vg_validate_state
319 */
320 p_atomic_set(&ctx->draw_buffer_invalid, FALSE);
321 vg_context_update_draw_buffer(ctx, pt);
322 }
323
324
325 static void
326 vg_context_notify_invalid_framebuffer(struct st_context_iface *stctxi,
327 struct st_framebuffer_iface *stfbi)
328 {
329 struct vg_context *ctx = (struct vg_context *) stctxi;
330 p_atomic_set(&ctx->draw_buffer_invalid, TRUE);
331 }
332
333 static void
334 vg_context_flush(struct st_context_iface *stctxi, unsigned flags,
335 struct pipe_fence_handle **fence)
336 {
337 struct vg_context *ctx = (struct vg_context *) stctxi;
338 ctx->pipe->flush(ctx->pipe, flags, fence);
339 if (flags & PIPE_FLUSH_RENDER_CACHE)
340 vg_manager_flush_frontbuffer(ctx);
341 }
342
343 static void
344 vg_context_destroy(struct st_context_iface *stctxi)
345 {
346 struct vg_context *ctx = (struct vg_context *) stctxi;
347 vg_destroy_context(ctx);
348 }
349
350 static struct st_context_iface *
351 vg_api_create_context(struct st_api *stapi, struct st_manager *smapi,
352 const struct st_visual *visual,
353 struct st_context_iface *shared_stctxi)
354 {
355 struct vg_context *shared_ctx = (struct vg_context *) shared_stctxi;
356 struct vg_context *ctx;
357 struct pipe_context *pipe;
358
359 pipe = smapi->screen->context_create(smapi->screen, NULL);
360 if (!pipe)
361 return NULL;
362 ctx = vg_create_context(pipe, NULL, shared_ctx);
363 if (!ctx) {
364 pipe->destroy(pipe);
365 return NULL;
366 }
367
368 ctx->iface.destroy = vg_context_destroy;
369
370 ctx->iface.notify_invalid_framebuffer =
371 vg_context_notify_invalid_framebuffer;
372 ctx->iface.flush = vg_context_flush;
373
374 ctx->iface.teximage = NULL;
375 ctx->iface.copy = NULL;
376
377 ctx->iface.st_context_private = (void *) smapi;
378
379 return &ctx->iface;
380 }
381
382 static struct st_renderbuffer *
383 create_renderbuffer(enum pipe_format format)
384 {
385 struct st_renderbuffer *strb;
386
387 strb = CALLOC_STRUCT(st_renderbuffer);
388 if (strb)
389 strb->format = format;
390
391 return strb;
392 }
393
394 static void
395 destroy_renderbuffer(struct st_renderbuffer *strb)
396 {
397 pipe_surface_reference(&strb->surface, NULL);
398 pipe_resource_reference(&strb->texture, NULL);
399 free(strb);
400 }
401
402 /**
403 * Decide the buffer to render to.
404 */
405 static enum st_attachment_type
406 choose_attachment(struct st_framebuffer_iface *stfbi)
407 {
408 enum st_attachment_type statt;
409
410 statt = stfbi->visual->render_buffer;
411 if (statt != ST_ATTACHMENT_INVALID) {
412 /* use the buffer given by the visual, unless it is unavailable */
413 if (!st_visual_have_buffers(stfbi->visual, 1 << statt)) {
414 switch (statt) {
415 case ST_ATTACHMENT_BACK_LEFT:
416 statt = ST_ATTACHMENT_FRONT_LEFT;
417 break;
418 case ST_ATTACHMENT_BACK_RIGHT:
419 statt = ST_ATTACHMENT_FRONT_RIGHT;
420 break;
421 default:
422 break;
423 }
424
425 if (!st_visual_have_buffers(stfbi->visual, 1 << statt))
426 statt = ST_ATTACHMENT_INVALID;
427 }
428 }
429
430 return statt;
431 }
432
433 /**
434 * Bind the context to the given framebuffers.
435 */
436 static boolean
437 vg_context_bind_framebuffers(struct st_context_iface *stctxi,
438 struct st_framebuffer_iface *stdrawi,
439 struct st_framebuffer_iface *streadi)
440 {
441 struct vg_context *ctx = (struct vg_context *) stctxi;
442 struct st_framebuffer *stfb;
443 enum st_attachment_type strb_att;
444
445 /* the draw and read framebuffers must be the same */
446 if (stdrawi != streadi)
447 return FALSE;
448
449 p_atomic_set(&ctx->draw_buffer_invalid, TRUE);
450
451 strb_att = (stdrawi) ? choose_attachment(stdrawi) : ST_ATTACHMENT_INVALID;
452
453 if (ctx->draw_buffer) {
454 stfb = ctx->draw_buffer;
455
456 /* free the existing fb */
457 if (!stdrawi ||
458 stfb->strb_att != strb_att ||
459 stfb->strb->format != stdrawi->visual->color_format ||
460 stfb->dsrb->format != stdrawi->visual->depth_stencil_format) {
461 destroy_renderbuffer(stfb->strb);
462 destroy_renderbuffer(stfb->dsrb);
463 free(stfb);
464
465 ctx->draw_buffer = NULL;
466 }
467 }
468
469 if (!stdrawi)
470 return TRUE;
471
472 if (strb_att == ST_ATTACHMENT_INVALID)
473 return FALSE;
474
475 /* create a new fb */
476 if (!ctx->draw_buffer) {
477 stfb = CALLOC_STRUCT(st_framebuffer);
478 if (!stfb)
479 return FALSE;
480
481 stfb->strb = create_renderbuffer(stdrawi->visual->color_format);
482 if (!stfb->strb) {
483 free(stfb);
484 return FALSE;
485 }
486
487 stfb->dsrb = create_renderbuffer(stdrawi->visual->depth_stencil_format);
488 if (!stfb->dsrb) {
489 free(stfb->strb);
490 free(stfb);
491 return FALSE;
492 }
493
494 stfb->width = 0;
495 stfb->height = 0;
496 stfb->strb_att = strb_att;
497
498 ctx->draw_buffer = stfb;
499 }
500
501 ctx->draw_buffer->iface = stdrawi;
502
503 return TRUE;
504 }
505
506 static boolean
507 vg_api_make_current(struct st_api *stapi, struct st_context_iface *stctxi,
508 struct st_framebuffer_iface *stdrawi,
509 struct st_framebuffer_iface *streadi)
510 {
511 struct vg_context *ctx = (struct vg_context *) stctxi;
512
513 if (stctxi)
514 vg_context_bind_framebuffers(stctxi, stdrawi, streadi);
515 vg_set_current_context(ctx);
516
517 return TRUE;
518 }
519
520 static struct st_context_iface *
521 vg_api_get_current(struct st_api *stapi)
522 {
523 struct vg_context *ctx = vg_current_context();
524
525 return (ctx) ? &ctx->iface : NULL;
526 }
527
528 static boolean
529 vg_api_is_visual_supported(struct st_api *stapi,
530 const struct st_visual *visual)
531 {
532 /* the impl requires a depth/stencil buffer */
533 return util_format_is_depth_and_stencil(visual->depth_stencil_format);
534 }
535
536 static st_proc_t
537 vg_api_get_proc_address(struct st_api *stapi, const char *procname)
538 {
539 /* TODO */
540 return (st_proc_t) NULL;
541 }
542
543 static void
544 vg_api_destroy(struct st_api *stapi)
545 {
546 free(stapi);
547 }
548
549 static struct st_api *
550 vg_module_create_api(void)
551 {
552 struct st_api *stapi;
553
554 stapi = CALLOC_STRUCT(st_api);
555 if (stapi) {
556 stapi->destroy = vg_api_destroy;
557 stapi->get_proc_address = vg_api_get_proc_address;
558 stapi->is_visual_supported = vg_api_is_visual_supported;
559
560 stapi->create_context = vg_api_create_context;
561 stapi->make_current = vg_api_make_current;
562 stapi->get_current = vg_api_get_current;
563 }
564
565 return stapi;
566 }
567
568 PUBLIC const struct st_module st_module_OpenVG = {
569 .api = ST_API_OPENVG,
570 .create_api = vg_module_create_api,
571 };