vg_manager.c revision c36c3d86b62b525291b1c6527de3ac5de93a2faf
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
44static struct pipe_resource *
45create_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
76static struct pipe_sampler_view *
77create_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
97static void
98setup_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_subresource subsurf, subold_surf;
126      subsurf.face = 0;
127      subsurf.level = 0;
128      subold_surf.face = 0;
129      subold_surf.level = 0;
130      pipe->resource_copy_region(pipe,
131                                 stfb->alpha_mask_view->texture,
132                                 subsurf,
133                                 0, 0, 0,
134                                 old_sampler_view->texture,
135                                 subold_surf,
136                                 0, 0, 0,
137                                 MIN2(old_sampler_view->texture->width0,
138                                      stfb->alpha_mask_view->texture->width0),
139                                 MIN2(old_sampler_view->texture->height0,
140                                      stfb->alpha_mask_view->texture->height0));
141   }
142
143   /* Free the old texture
144    */
145   if (old_sampler_view)
146      pipe_sampler_view_reference(&old_sampler_view, NULL);
147}
148
149static boolean
150vg_context_update_depth_stencil_rb(struct vg_context * ctx,
151                                   uint width, uint height)
152{
153   struct st_renderbuffer *dsrb = ctx->draw_buffer->dsrb;
154   struct pipe_context *pipe = ctx->pipe;
155   unsigned surface_usage;
156
157   if ((dsrb->width == width && dsrb->height == height) && dsrb->texture)
158      return FALSE;
159
160   /* unreference existing ones */
161   pipe_surface_reference(&dsrb->surface, NULL);
162   pipe_resource_reference(&dsrb->texture, NULL);
163   dsrb->width = dsrb->height = 0;
164
165   /* Probably need dedicated flags for surface usage too:
166    */
167   surface_usage = PIPE_BIND_DEPTH_STENCIL; /* XXX: was: RENDER_TARGET */
168
169   dsrb->texture = create_texture(pipe, dsrb->format, width, height);
170   if (!dsrb->texture)
171      return TRUE;
172
173   dsrb->surface = pipe->screen->get_tex_surface(pipe->screen,
174                                                 dsrb->texture,
175                                                 0, 0, 0,
176                                                 surface_usage);
177   if (!dsrb->surface) {
178      pipe_resource_reference(&dsrb->texture, NULL);
179      return TRUE;
180   }
181
182   dsrb->width = width;
183   dsrb->height = height;
184
185   assert(dsrb->surface->width == width);
186   assert(dsrb->surface->height == height);
187
188   return TRUE;
189}
190
191static boolean
192vg_context_update_color_rb(struct vg_context *ctx, struct pipe_resource *pt)
193{
194   struct st_renderbuffer *strb = ctx->draw_buffer->strb;
195   struct pipe_screen *screen = ctx->pipe->screen;
196
197   if (strb->texture == pt) {
198      pipe_resource_reference(&pt, NULL);
199      return FALSE;
200   }
201
202   /* unreference existing ones */
203   pipe_surface_reference(&strb->surface, NULL);
204   pipe_resource_reference(&strb->texture, NULL);
205   strb->width = strb->height = 0;
206
207   strb->texture = pt;
208   strb->surface = screen->get_tex_surface(screen, strb->texture, 0, 0, 0,
209                                           PIPE_BIND_RENDER_TARGET);
210   if (!strb->surface) {
211      pipe_resource_reference(&strb->texture, NULL);
212      return TRUE;
213   }
214
215   strb->width = pt->width0;
216   strb->height = pt->height0;
217
218   return TRUE;
219}
220
221static void
222vg_context_update_draw_buffer(struct vg_context *ctx, struct pipe_resource *pt)
223{
224   struct st_framebuffer *stfb = ctx->draw_buffer;
225   boolean new_cbuf, new_zsbuf, new_size;
226
227   new_cbuf = vg_context_update_color_rb(ctx, pt);
228   new_zsbuf =
229      vg_context_update_depth_stencil_rb(ctx, pt->width0, pt->height0);
230
231   new_size = (stfb->width != pt->width0 || stfb->height != pt->height0);
232   stfb->width = pt->width0;
233   stfb->height = pt->height0;
234
235   if (new_cbuf || new_zsbuf || new_size) {
236      struct pipe_framebuffer_state *state = &ctx->state.g3d.fb;
237
238      memset(state, 0, sizeof(struct pipe_framebuffer_state));
239      state->width  = stfb->width;
240      state->height = stfb->height;
241      state->nr_cbufs = 1;
242      state->cbufs[0] = stfb->strb->surface;
243      state->zsbuf = stfb->dsrb->surface;
244
245      cso_set_framebuffer(ctx->cso_context, state);
246   }
247
248   if (new_zsbuf || new_size) {
249      ctx->state.dirty |= VIEWPORT_DIRTY;
250      ctx->state.dirty |= DEPTH_STENCIL_DIRTY;/*to reset the scissors*/
251
252      ctx->pipe->clear(ctx->pipe, PIPE_CLEAR_DEPTHSTENCIL, NULL, 0.0, 0);
253
254      /* we need all the other state already set */
255
256      setup_new_alpha_mask(ctx, stfb);
257
258      pipe_sampler_view_reference( &stfb->blend_texture_view, NULL);
259      stfb->blend_texture_view = create_tex_and_view(ctx->pipe,
260            PIPE_FORMAT_B8G8R8A8_UNORM, stfb->width, stfb->height);
261   }
262}
263
264/**
265 * Flush the front buffer if the current context renders to the front buffer.
266 */
267void
268vg_manager_flush_frontbuffer(struct vg_context *ctx)
269{
270   struct st_framebuffer *stfb = ctx->draw_buffer;
271
272   if (!stfb)
273      return;
274
275   switch (stfb->strb_att) {
276   case ST_ATTACHMENT_FRONT_LEFT:
277   case ST_ATTACHMENT_FRONT_RIGHT:
278      stfb->iface->flush_front(stfb->iface, stfb->strb_att);
279      break;
280   default:
281      break;
282   }
283}
284
285/**
286 * Re-validate the framebuffer.
287 */
288void
289vg_manager_validate_framebuffer(struct vg_context *ctx)
290{
291   struct st_framebuffer *stfb = ctx->draw_buffer;
292   struct pipe_resource *pt;
293
294   /* no binding surface */
295   if (!stfb)
296      return;
297
298   if (!p_atomic_read(&ctx->draw_buffer_invalid))
299      return;
300
301   /* validate the fb */
302   if (!stfb->iface->validate(stfb->iface, &stfb->strb_att, 1, &pt) || !pt)
303      return;
304
305   /*
306    * unset draw_buffer_invalid first because vg_context_update_draw_buffer
307    * will cause the framebuffer to be validated again because of a call to
308    * vg_validate_state
309    */
310   p_atomic_set(&ctx->draw_buffer_invalid, FALSE);
311   vg_context_update_draw_buffer(ctx, pt);
312}
313
314
315static void
316vg_context_notify_invalid_framebuffer(struct st_context_iface *stctxi,
317                                      struct st_framebuffer_iface *stfbi)
318{
319   struct vg_context *ctx = (struct vg_context *) stctxi;
320   p_atomic_set(&ctx->draw_buffer_invalid, TRUE);
321}
322
323static void
324vg_context_flush(struct st_context_iface *stctxi, unsigned flags,
325                 struct pipe_fence_handle **fence)
326{
327   struct vg_context *ctx = (struct vg_context *) stctxi;
328   ctx->pipe->flush(ctx->pipe, flags, fence);
329   if (flags & PIPE_FLUSH_RENDER_CACHE)
330      vg_manager_flush_frontbuffer(ctx);
331}
332
333static void
334vg_context_destroy(struct st_context_iface *stctxi)
335{
336   struct vg_context *ctx = (struct vg_context *) stctxi;
337   vg_destroy_context(ctx);
338}
339
340static struct st_context_iface *
341vg_api_create_context(struct st_api *stapi, struct st_manager *smapi,
342                      const struct st_visual *visual,
343                      struct st_context_iface *shared_stctxi)
344{
345   struct vg_context *shared_ctx = (struct vg_context *) shared_stctxi;
346   struct vg_context *ctx;
347   struct pipe_context *pipe;
348
349   pipe = smapi->screen->context_create(smapi->screen, NULL);
350   if (!pipe)
351      return NULL;
352   ctx = vg_create_context(pipe, NULL, shared_ctx);
353   if (!ctx) {
354      pipe->destroy(pipe);
355      return NULL;
356   }
357
358   ctx->iface.destroy = vg_context_destroy;
359
360   ctx->iface.notify_invalid_framebuffer =
361      vg_context_notify_invalid_framebuffer;
362   ctx->iface.flush = vg_context_flush;
363
364   ctx->iface.teximage = NULL;
365   ctx->iface.copy = NULL;
366
367   ctx->iface.st_context_private = (void *) smapi;
368
369   return &ctx->iface;
370}
371
372static struct st_renderbuffer *
373create_renderbuffer(enum pipe_format format)
374{
375   struct st_renderbuffer *strb;
376
377   strb = CALLOC_STRUCT(st_renderbuffer);
378   if (strb)
379      strb->format = format;
380
381   return strb;
382}
383
384static void
385destroy_renderbuffer(struct st_renderbuffer *strb)
386{
387   pipe_surface_reference(&strb->surface, NULL);
388   pipe_resource_reference(&strb->texture, NULL);
389   free(strb);
390}
391
392/**
393 * Decide the buffer to render to.
394 */
395static enum st_attachment_type
396choose_attachment(struct st_framebuffer_iface *stfbi)
397{
398   enum st_attachment_type statt;
399
400   statt = stfbi->visual->render_buffer;
401   if (statt != ST_ATTACHMENT_INVALID) {
402      /* use the buffer given by the visual, unless it is unavailable */
403      if (!st_visual_have_buffers(stfbi->visual, 1 << statt)) {
404         switch (statt) {
405         case ST_ATTACHMENT_BACK_LEFT:
406            statt = ST_ATTACHMENT_FRONT_LEFT;
407            break;
408         case ST_ATTACHMENT_BACK_RIGHT:
409            statt = ST_ATTACHMENT_FRONT_RIGHT;
410            break;
411         default:
412            break;
413         }
414
415         if (!st_visual_have_buffers(stfbi->visual, 1 << statt))
416            statt = ST_ATTACHMENT_INVALID;
417      }
418   }
419
420   return statt;
421}
422
423/**
424 * Bind the context to the given framebuffers.
425 */
426static boolean
427vg_context_bind_framebuffers(struct st_context_iface *stctxi,
428                             struct st_framebuffer_iface *stdrawi,
429                             struct st_framebuffer_iface *streadi)
430{
431   struct vg_context *ctx = (struct vg_context *) stctxi;
432   struct st_framebuffer *stfb;
433   enum st_attachment_type strb_att;
434
435   /* the draw and read framebuffers must be the same */
436   if (stdrawi != streadi)
437      return FALSE;
438
439   p_atomic_set(&ctx->draw_buffer_invalid, TRUE);
440
441   strb_att = (stdrawi) ? choose_attachment(stdrawi) : ST_ATTACHMENT_INVALID;
442
443   if (ctx->draw_buffer) {
444      stfb = ctx->draw_buffer;
445
446      /* free the existing fb */
447      if (!stdrawi ||
448          stfb->strb_att != strb_att ||
449          stfb->strb->format != stdrawi->visual->color_format ||
450          stfb->dsrb->format != stdrawi->visual->depth_stencil_format) {
451         destroy_renderbuffer(stfb->strb);
452         destroy_renderbuffer(stfb->dsrb);
453         free(stfb);
454
455         ctx->draw_buffer = NULL;
456      }
457   }
458
459   if (!stdrawi)
460      return TRUE;
461
462   if (strb_att == ST_ATTACHMENT_INVALID)
463      return FALSE;
464
465   /* create a new fb */
466   if (!ctx->draw_buffer) {
467      stfb = CALLOC_STRUCT(st_framebuffer);
468      if (!stfb)
469         return FALSE;
470
471      stfb->strb = create_renderbuffer(stdrawi->visual->color_format);
472      if (!stfb->strb) {
473         free(stfb);
474         return FALSE;
475      }
476
477      stfb->dsrb = create_renderbuffer(stdrawi->visual->depth_stencil_format);
478      if (!stfb->dsrb) {
479         free(stfb->strb);
480         free(stfb);
481         return FALSE;
482      }
483
484      stfb->width = 0;
485      stfb->height = 0;
486      stfb->strb_att = strb_att;
487
488      ctx->draw_buffer = stfb;
489   }
490
491   ctx->draw_buffer->iface = stdrawi;
492
493   return TRUE;
494}
495
496static boolean
497vg_api_make_current(struct st_api *stapi, struct st_context_iface *stctxi,
498                    struct st_framebuffer_iface *stdrawi,
499                    struct st_framebuffer_iface *streadi)
500{
501   struct vg_context *ctx = (struct vg_context *) stctxi;
502
503   if (stctxi)
504      vg_context_bind_framebuffers(stctxi, stdrawi, streadi);
505   vg_set_current_context(ctx);
506
507   return TRUE;
508}
509
510static struct st_context_iface *
511vg_api_get_current(struct st_api *stapi)
512{
513   struct vg_context *ctx = vg_current_context();
514
515   return (ctx) ? &ctx->iface : NULL;
516}
517
518static boolean
519vg_api_is_visual_supported(struct st_api *stapi,
520                           const struct st_visual *visual)
521{
522   /* the impl requires a depth/stencil buffer */
523   return util_format_is_depth_and_stencil(visual->depth_stencil_format);
524}
525
526static st_proc_t
527vg_api_get_proc_address(struct st_api *stapi, const char *procname)
528{
529   /* TODO */
530   return (st_proc_t) NULL;
531}
532
533static void
534vg_api_destroy(struct st_api *stapi)
535{
536   free(stapi);
537}
538
539struct st_api st_vg_api = {
540   vg_api_destroy,
541   vg_api_get_proc_address,
542   vg_api_is_visual_supported,
543   vg_api_create_context,
544   vg_api_make_current,
545   vg_api_get_current,
546};
547
548struct st_api *
549st_api_create_OpenVG(void)
550{
551   return &st_vg_api;
552}
553