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