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