vg_manager.c revision 438359597cd4254558f4d2fd5b54eb32c03e1b4c
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_api.h"
40#include "vg_manager.h"
41#include "vg_context.h"
42#include "image.h"
43#include "mask.h"
44#include "api.h"
45
46static struct pipe_resource *
47create_texture(struct pipe_context *pipe, enum pipe_format format,
48                    VGint width, VGint height)
49{
50   struct pipe_resource templ;
51
52   memset(&templ, 0, sizeof(templ));
53
54   if (format != PIPE_FORMAT_NONE) {
55      templ.format = format;
56   }
57   else {
58      templ.format = PIPE_FORMAT_B8G8R8A8_UNORM;
59   }
60
61   templ.target = PIPE_TEXTURE_2D;
62   templ.width0 = width;
63   templ.height0 = height;
64   templ.depth0 = 1;
65   templ.last_level = 0;
66
67   if (util_format_get_component_bits(format, UTIL_FORMAT_COLORSPACE_ZS, 1)) {
68      templ.bind = PIPE_BIND_DEPTH_STENCIL;
69   } else {
70      templ.bind = (PIPE_BIND_DISPLAY_TARGET |
71                    PIPE_BIND_RENDER_TARGET |
72                    PIPE_BIND_SAMPLER_VIEW);
73   }
74
75   return pipe->screen->resource_create(pipe->screen, &templ);
76}
77
78static struct pipe_sampler_view *
79create_tex_and_view(struct pipe_context *pipe, enum pipe_format format,
80                    VGint width, VGint height)
81{
82   struct pipe_resource *texture;
83   struct pipe_sampler_view view_templ;
84   struct pipe_sampler_view *view;
85
86   texture = create_texture(pipe, format, width, height);
87
88   if (!texture)
89      return NULL;
90
91   u_sampler_view_default_template(&view_templ, texture, texture->format);
92   view = pipe->create_sampler_view(pipe, texture, &view_templ);
93   /* want the texture to go away if the view is freed */
94   pipe_resource_reference(&texture, NULL);
95
96   return view;
97}
98
99static void
100vg_context_update_alpha_mask_view(struct vg_context *ctx,
101                                  uint width, uint height)
102{
103   struct st_framebuffer *stfb = ctx->draw_buffer;
104   struct pipe_sampler_view *old_sampler_view = stfb->alpha_mask_view;
105   struct pipe_context *pipe = ctx->pipe;
106
107   if (old_sampler_view &&
108       old_sampler_view->texture->width0 == width &&
109       old_sampler_view->texture->height0 == height)
110      return;
111
112   /*
113     we use PIPE_FORMAT_B8G8R8A8_UNORM because we want to render to
114     this texture and use it as a sampler, so while this wastes some
115     space it makes both of those a lot simpler
116   */
117   stfb->alpha_mask_view = create_tex_and_view(pipe,
118         PIPE_FORMAT_B8G8R8A8_UNORM, width, height);
119
120   if (!stfb->alpha_mask_view) {
121      if (old_sampler_view)
122         pipe_sampler_view_reference(&old_sampler_view, NULL);
123      return;
124   }
125
126   /* XXX could this call be avoided? */
127   vg_validate_state(ctx);
128
129   /* alpha mask starts with 1.f alpha */
130   mask_fill(0, 0, width, height, 1.f);
131
132   /* if we had an old surface copy it over */
133   if (old_sampler_view) {
134      struct pipe_subresource subsurf, subold_surf;
135      subsurf.face = 0;
136      subsurf.level = 0;
137      subold_surf.face = 0;
138      subold_surf.level = 0;
139      pipe->resource_copy_region(pipe,
140                                 stfb->alpha_mask_view->texture,
141                                 subsurf,
142                                 0, 0, 0,
143                                 old_sampler_view->texture,
144                                 subold_surf,
145                                 0, 0, 0,
146                                 MIN2(old_sampler_view->texture->width0,
147                                      stfb->alpha_mask_view->texture->width0),
148                                 MIN2(old_sampler_view->texture->height0,
149                                      stfb->alpha_mask_view->texture->height0));
150   }
151
152   /* Free the old texture
153    */
154   if (old_sampler_view)
155      pipe_sampler_view_reference(&old_sampler_view, NULL);
156}
157
158static void
159vg_context_update_blend_texture_view(struct vg_context *ctx,
160                                     uint width, uint height)
161{
162   struct pipe_context *pipe = ctx->pipe;
163   struct st_framebuffer *stfb = ctx->draw_buffer;
164   struct pipe_sampler_view *old = stfb->blend_texture_view;
165
166   if (old &&
167       old->texture->width0 == width &&
168       old->texture->height0 == height)
169      return;
170
171   stfb->blend_texture_view = create_tex_and_view(pipe,
172         PIPE_FORMAT_B8G8R8A8_UNORM, width, height);
173
174   pipe_sampler_view_reference(&old, NULL);
175}
176
177static boolean
178vg_context_update_depth_stencil_rb(struct vg_context * ctx,
179                                   uint width, uint height)
180{
181   struct st_renderbuffer *dsrb = ctx->draw_buffer->dsrb;
182   struct pipe_context *pipe = ctx->pipe;
183   unsigned surface_usage;
184
185   if ((dsrb->width == width && dsrb->height == height) && dsrb->texture)
186      return FALSE;
187
188   /* unreference existing ones */
189   pipe_surface_reference(&dsrb->surface, NULL);
190   pipe_resource_reference(&dsrb->texture, NULL);
191   dsrb->width = dsrb->height = 0;
192
193   /* Probably need dedicated flags for surface usage too:
194    */
195   surface_usage = PIPE_BIND_DEPTH_STENCIL; /* XXX: was: RENDER_TARGET */
196
197   dsrb->texture = create_texture(pipe, dsrb->format, width, height);
198   if (!dsrb->texture)
199      return TRUE;
200
201   dsrb->surface = pipe->screen->get_tex_surface(pipe->screen,
202                                                 dsrb->texture,
203                                                 0, 0, 0,
204                                                 surface_usage);
205   if (!dsrb->surface) {
206      pipe_resource_reference(&dsrb->texture, NULL);
207      return TRUE;
208   }
209
210   dsrb->width = width;
211   dsrb->height = height;
212
213   assert(dsrb->surface->width == width);
214   assert(dsrb->surface->height == height);
215
216   return TRUE;
217}
218
219static boolean
220vg_context_update_color_rb(struct vg_context *ctx, struct pipe_resource *pt)
221{
222   struct st_renderbuffer *strb = ctx->draw_buffer->strb;
223   struct pipe_screen *screen = ctx->pipe->screen;
224
225   if (strb->texture == pt) {
226      pipe_resource_reference(&pt, NULL);
227      return FALSE;
228   }
229
230   /* unreference existing ones */
231   pipe_surface_reference(&strb->surface, NULL);
232   pipe_resource_reference(&strb->texture, NULL);
233   strb->width = strb->height = 0;
234
235   strb->texture = pt;
236   strb->surface = screen->get_tex_surface(screen, strb->texture, 0, 0, 0,
237                                           PIPE_BIND_RENDER_TARGET);
238   if (!strb->surface) {
239      pipe_resource_reference(&strb->texture, NULL);
240      return TRUE;
241   }
242
243   strb->width = pt->width0;
244   strb->height = pt->height0;
245
246   return TRUE;
247}
248
249/**
250 * Flush the front buffer if the current context renders to the front buffer.
251 */
252void
253vg_manager_flush_frontbuffer(struct vg_context *ctx)
254{
255   struct st_framebuffer *stfb = ctx->draw_buffer;
256
257   if (!stfb)
258      return;
259
260   switch (stfb->strb_att) {
261   case ST_ATTACHMENT_FRONT_LEFT:
262   case ST_ATTACHMENT_FRONT_RIGHT:
263      stfb->iface->flush_front(stfb->iface, stfb->strb_att);
264      break;
265   default:
266      break;
267   }
268}
269
270/**
271 * Re-validate the framebuffer.
272 */
273void
274vg_manager_validate_framebuffer(struct vg_context *ctx)
275{
276   struct st_framebuffer *stfb = ctx->draw_buffer;
277   struct pipe_resource *pt;
278
279   /* no binding surface */
280   if (!stfb)
281      return;
282
283   if (!p_atomic_read(&ctx->draw_buffer_invalid))
284      return;
285
286   /* validate the fb */
287   if (!stfb->iface->validate(stfb->iface, &stfb->strb_att, 1, &pt) || !pt)
288      return;
289
290   p_atomic_set(&ctx->draw_buffer_invalid, FALSE);
291
292   if (vg_context_update_color_rb(ctx, pt) ||
293       stfb->width != pt->width0 ||
294       stfb->height != pt->height0)
295      ctx->state.dirty |= FRAMEBUFFER_DIRTY;
296
297   if (vg_context_update_depth_stencil_rb(ctx, pt->width0, pt->height0))
298      ctx->state.dirty |= DEPTH_STENCIL_DIRTY;
299
300   stfb->width = pt->width0;
301   stfb->height = pt->height0;
302
303   /* TODO create as needed */
304   vg_context_update_alpha_mask_view(ctx, stfb->width, stfb->height);
305   vg_context_update_blend_texture_view(ctx, stfb->width, stfb->height);
306}
307
308static void
309vg_context_notify_invalid_framebuffer(struct st_context_iface *stctxi,
310                                      struct st_framebuffer_iface *stfbi)
311{
312   struct vg_context *ctx = (struct vg_context *) stctxi;
313   p_atomic_set(&ctx->draw_buffer_invalid, TRUE);
314}
315
316static void
317vg_context_flush(struct st_context_iface *stctxi, unsigned flags,
318                 struct pipe_fence_handle **fence)
319{
320   struct vg_context *ctx = (struct vg_context *) stctxi;
321   ctx->pipe->flush(ctx->pipe, flags, fence);
322   if (flags & PIPE_FLUSH_RENDER_CACHE)
323      vg_manager_flush_frontbuffer(ctx);
324}
325
326static void
327vg_context_destroy(struct st_context_iface *stctxi)
328{
329   struct vg_context *ctx = (struct vg_context *) stctxi;
330   vg_destroy_context(ctx);
331}
332
333static struct st_context_iface *
334vg_api_create_context(struct st_api *stapi, struct st_manager *smapi,
335                      const struct st_context_attribs *attribs,
336                      struct st_context_iface *shared_stctxi)
337{
338   struct vg_context *shared_ctx = (struct vg_context *) shared_stctxi;
339   struct vg_context *ctx;
340   struct pipe_context *pipe;
341
342   if (!(stapi->profile_mask & (1 << attribs->profile)))
343      return NULL;
344
345   /* only 1.0 is supported */
346   if (attribs->major > 1 || (attribs->major == 1 && attribs->minor > 0))
347      return NULL;
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         destroy_renderbuffer(stfb->strb);
451         destroy_renderbuffer(stfb->dsrb);
452         FREE(stfb);
453
454         ctx->draw_buffer = NULL;
455      }
456   }
457
458   if (!stdrawi)
459      return TRUE;
460
461   if (strb_att == ST_ATTACHMENT_INVALID)
462      return FALSE;
463
464   /* create a new fb */
465   if (!ctx->draw_buffer) {
466      stfb = CALLOC_STRUCT(st_framebuffer);
467      if (!stfb)
468         return FALSE;
469
470      stfb->strb = create_renderbuffer(stdrawi->visual->color_format);
471      if (!stfb->strb) {
472         FREE(stfb);
473         return FALSE;
474      }
475
476      stfb->dsrb = create_renderbuffer(ctx->ds_format);
477      if (!stfb->dsrb) {
478         FREE(stfb->strb);
479         FREE(stfb);
480         return FALSE;
481      }
482
483      stfb->width = 0;
484      stfb->height = 0;
485      stfb->strb_att = strb_att;
486
487      ctx->draw_buffer = stfb;
488   }
489
490   ctx->draw_buffer->iface = stdrawi;
491
492   return TRUE;
493}
494
495static boolean
496vg_api_make_current(struct st_api *stapi, struct st_context_iface *stctxi,
497                    struct st_framebuffer_iface *stdrawi,
498                    struct st_framebuffer_iface *streadi)
499{
500   struct vg_context *ctx = (struct vg_context *) stctxi;
501
502   if (stctxi)
503      vg_context_bind_framebuffers(stctxi, stdrawi, streadi);
504   vg_set_current_context(ctx);
505
506   return TRUE;
507}
508
509static struct st_context_iface *
510vg_api_get_current(struct st_api *stapi)
511{
512   struct vg_context *ctx = vg_current_context();
513
514   return (ctx) ? &ctx->iface : NULL;
515}
516
517static st_proc_t
518vg_api_get_proc_address(struct st_api *stapi, const char *procname)
519{
520   return api_get_proc_address(procname);
521}
522
523static void
524vg_api_destroy(struct st_api *stapi)
525{
526}
527
528static const struct st_api vg_api = {
529   "Vega " VEGA_VERSION_STRING,
530   ST_API_OPENVG,
531   ST_PROFILE_DEFAULT_MASK,
532   vg_api_destroy,
533   vg_api_get_proc_address,
534   vg_api_create_context,
535   vg_api_make_current,
536   vg_api_get_current,
537};
538
539const struct st_api *
540vg_api_get(void)
541{
542   return &vg_api;
543}
544