Example #1
0
void
_cogl_texture_2d_gl_copy_from_framebuffer (CoglTexture2D *tex_2d,
                                           int src_x,
                                           int src_y,
                                           int width,
                                           int height,
                                           CoglFramebuffer *src_fb,
                                           int dst_x,
                                           int dst_y,
                                           int level)
{
  CoglTexture *tex = COGL_TEXTURE (tex_2d);
  CoglContext *ctx = tex->context;

  /* Make sure the current framebuffers are bound, though we don't need to
   * flush the clip state here since we aren't going to draw to the
   * framebuffer. */
  _cogl_framebuffer_flush_state (ctx->current_draw_buffer,
                                 src_fb,
                                 COGL_FRAMEBUFFER_STATE_ALL &
                                 ~COGL_FRAMEBUFFER_STATE_CLIP);

  _cogl_bind_gl_texture_transient (GL_TEXTURE_2D,
                                   tex_2d->gl_texture,
                                   tex_2d->is_foreign);

  ctx->glCopyTexSubImage2D (GL_TEXTURE_2D,
                            0, /* level */
                            dst_x, dst_y,
                            src_x, src_y,
                            width, height);
}
Example #2
0
void
cogl_begin_gl (void)
{
  CoglPipeline *pipeline;

  _COGL_GET_CONTEXT (ctx, NO_RETVAL);

  if (ctx->in_begin_gl_block)
    {
      static CoglBool shown = FALSE;
      if (!shown)
        g_warning ("You should not nest cogl_begin_gl/cogl_end_gl blocks");
      shown = TRUE;
      return;
    }
  ctx->in_begin_gl_block = TRUE;

  /* Flush all batched primitives */
  cogl_flush ();

  /* Flush framebuffer state, including clip state, modelview and
   * projection matrix state
   *
   * NB: _cogl_framebuffer_flush_state may disrupt various state (such
   * as the pipeline state) when flushing the clip stack, so should
   * always be done first when preparing to draw. */
  _cogl_framebuffer_flush_state (cogl_get_draw_framebuffer (),
                                 _cogl_get_read_framebuffer (),
                                 COGL_FRAMEBUFFER_STATE_ALL);

  /* Setup the state for the current pipeline */

  /* We considered flushing a specific, minimal pipeline here to try and
   * simplify the GL state, but decided to avoid special cases and second
   * guessing what would be actually helpful.
   *
   * A user should instead call cogl_set_source_color4ub() before
   * cogl_begin_gl() to simplify the state flushed.
   *
   * XXX: note defining n_tex_coord_attribs using
   * cogl_pipeline_get_n_layers is a hack, but the problem is that
   * n_tex_coord_attribs is usually defined when drawing a primitive
   * which isn't happening here.
   *
   * Maybe it would be more useful if this code did flush the
   * opaque_color_pipeline and then call into cogl-pipeline-opengl.c to then
   * restore all state for the material's backend back to default OpenGL
   * values.
   */
  pipeline = cogl_get_source ();
  _cogl_pipeline_flush_gl_state (ctx,
                                 pipeline,
                                 cogl_get_draw_framebuffer (),
                                 FALSE,
                                 FALSE);

  /* Disable any cached vertex arrays */
  _cogl_gl_disable_all_attributes (ctx);
}
Example #3
0
static void
_cogl_winsys_onscreen_swap_buffers (CoglOnscreen *onscreen)
{
  CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
  CoglRenderer *renderer = context->display->renderer;
  CoglRendererEGL *egl_renderer = renderer->winsys;
  CoglOnscreenEGL *egl_onscreen = onscreen->winsys;

  /* The specification for EGL (at least in 1.4) says that the surface
     needs to be bound to the current context for the swap to work
     although it may change in future. Mesa explicitly checks for this
     and just returns an error if this is not the case so we can't
     just pretend this isn't in the spec. */
  _cogl_framebuffer_flush_state (COGL_FRAMEBUFFER (onscreen),
                                 COGL_FRAMEBUFFER (onscreen),
                                 COGL_FRAMEBUFFER_STATE_BIND);

  eglSwapBuffers (egl_renderer->edpy, egl_onscreen->egl_surface);
}
Example #4
0
static void
_cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen,
                                                const int *rectangles,
                                                int n_rectangles)
{
  CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
  CoglRenderer *renderer = context->display->renderer;
  CoglRendererEGL *egl_renderer = renderer->winsys;
  CoglOnscreenEGL *egl_onscreen = onscreen->winsys;

  /* The specification for EGL (at least in 1.4) says that the surface
     needs to be bound to the current context for the swap to work
     although it may change in future. Mesa explicitly checks for this
     and just returns an error if this is not the case so we can't
     just pretend this isn't in the spec. */
  _cogl_framebuffer_flush_state (COGL_FRAMEBUFFER (onscreen),
                                 COGL_FRAMEBUFFER (onscreen),
                                 COGL_FRAMEBUFFER_STATE_BIND);

  if (n_rectangles && egl_renderer->pf_eglSwapBuffersWithDamage)
    {
      CoglFramebuffer *fb = COGL_FRAMEBUFFER (onscreen);
      size_t size = n_rectangles * sizeof (int) * 4;
      int *flipped = alloca (size);
      int i;

      memcpy (flipped, rectangles, size);
      for (i = 0; i < n_rectangles; i++)
        {
          const int *rect = rectangles + 4 * i;
          int *flip_rect = flipped + 4 * i;
          flip_rect[1] = fb->height - rect[1] - rect[3];
        }

      if (egl_renderer->pf_eglSwapBuffersWithDamage (egl_renderer->edpy,
                                                     egl_onscreen->egl_surface,
                                                     flipped,
                                                     n_rectangles) == EGL_FALSE)
        g_warning ("Error reported by eglSwapBuffersWithDamage");
    }
  else
    eglSwapBuffers (egl_renderer->edpy, egl_onscreen->egl_surface);
}
Example #5
0
static void
_cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen,
                                   const int *user_rectangles,
                                   int n_rectangles)
{
  CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
  CoglRenderer *renderer = context->display->renderer;
  CoglRendererEGL *egl_renderer = renderer->winsys;
  CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
  CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
  int framebuffer_height  = cogl_framebuffer_get_height (framebuffer);
  int *rectangles = g_alloca (sizeof (int) * n_rectangles * 4);
  int i;

  /* eglSwapBuffersRegion expects rectangles relative to the
   * bottom left corner but we are given rectangles relative to
   * the top left so we need to flip them... */
  memcpy (rectangles, user_rectangles, sizeof (int) * n_rectangles * 4);
  for (i = 0; i < n_rectangles; i++)
    {
      int *rect = &rectangles[4 * i];
      rect[1] = framebuffer_height - rect[1] - rect[3];
    }

  /* At least for eglSwapBuffers the EGL spec says that the surface to
     swap must be bound to the current context. It looks like Mesa
     also validates that this is the case for eglSwapBuffersRegion so
     we must bind here too */
  _cogl_framebuffer_flush_state (COGL_FRAMEBUFFER (onscreen),
                                 COGL_FRAMEBUFFER (onscreen),
                                 COGL_FRAMEBUFFER_STATE_BIND);

  if (egl_renderer->pf_eglSwapBuffersRegion (egl_renderer->edpy,
                                             egl_onscreen->egl_surface,
                                             n_rectangles,
                                             rectangles) == EGL_FALSE)
    g_warning ("Error reported by eglSwapBuffersRegion");
}
Example #6
0
void
add_stencil_clip_rectangle (float x_1,
                            float y_1,
                            float x_2,
                            float y_2,
                            gboolean first)
{
  CoglHandle current_source;
  CoglHandle framebuffer = _cogl_get_framebuffer ();

  _COGL_GET_CONTEXT (ctx, NO_RETVAL);

  /* We don't log changes to the stencil buffer so need to flush any
   * batched geometry before we start... */
  _cogl_journal_flush ();

  _cogl_framebuffer_flush_state (framebuffer, 0);

  /* temporarily swap in our special stenciling material */
  current_source = cogl_handle_ref (ctx->source_material);
  cogl_set_source (ctx->stencil_material);

  if (first)
    {
      GE( glEnable (GL_STENCIL_TEST) );

      /* Initially disallow everything */
      GE( glClearStencil (0) );
      GE( glClear (GL_STENCIL_BUFFER_BIT) );

      /* Punch out a hole to allow the rectangle */
      GE( glStencilFunc (GL_NEVER, 0x1, 0x1) );
      GE( glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE) );

      cogl_rectangle (x_1, y_1, x_2, y_2);
    }
  else
    {
      CoglMatrixStack *modelview_stack =
        _cogl_framebuffer_get_modelview_stack (framebuffer);
      CoglMatrixStack *projection_stack =
        _cogl_framebuffer_get_projection_stack (framebuffer);

      /* Add one to every pixel of the stencil buffer in the
	 rectangle */
      GE( glStencilFunc (GL_NEVER, 0x1, 0x3) );
      GE( glStencilOp (GL_INCR, GL_INCR, GL_INCR) );
      cogl_rectangle (x_1, y_1, x_2, y_2);

      /* make sure our rectangle hits the stencil buffer before we
       * change the stencil operation */
      _cogl_journal_flush ();

      /* Subtract one from all pixels in the stencil buffer so that
	 only pixels where both the original stencil buffer and the
	 rectangle are set will be valid */
      GE( glStencilOp (GL_DECR, GL_DECR, GL_DECR) );

      _cogl_matrix_stack_push (projection_stack);
      _cogl_matrix_stack_load_identity (projection_stack);

      _cogl_matrix_stack_push (modelview_stack);
      _cogl_matrix_stack_load_identity (modelview_stack);

      cogl_rectangle (-1.0, -1.0, 1.0, 1.0);

      _cogl_matrix_stack_pop (modelview_stack);
      _cogl_matrix_stack_pop (projection_stack);
    }

  /* make sure our rectangles hit the stencil buffer before we restore
   * the stencil function / operation */
  _cogl_journal_flush ();

  /* Restore the stencil mode */
  GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) );
  GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) );

  /* restore the original source material */
  cogl_set_source (current_source);
  cogl_handle_unref (current_source);
}
Example #7
0
File: cogl.c Project: collects/cogl
void
_cogl_read_pixels_with_rowstride (int x,
                                  int y,
                                  int width,
                                  int height,
                                  CoglReadPixelsFlags source,
                                  CoglPixelFormat format,
                                  guint8 *pixels,
                                  int rowstride)
{
    CoglFramebuffer *framebuffer = _cogl_get_read_framebuffer ();
    int              framebuffer_height;
    int              bpp;
    CoglBitmap      *bmp;
    GLenum           gl_intformat;
    GLenum           gl_format;
    GLenum           gl_type;
    CoglPixelFormat  bmp_format;
    gboolean         pack_invert_set;

    _COGL_GET_CONTEXT (ctx, NO_RETVAL);

    _COGL_RETURN_IF_FAIL (source == COGL_READ_PIXELS_COLOR_BUFFER);

    if (width == 1 && height == 1 && !framebuffer->clear_clip_dirty)
    {
        /* If everything drawn so far for this frame is still in the
         * Journal then if all of the rectangles only have a flat
         * opaque color we have a fast-path for reading a single pixel
         * that avoids the relatively high cost of flushing primitives
         * to be drawn on the GPU (considering how simple the geometry
         * is in this case) and then blocking on the long GPU pipelines
         * for the result.
         */
        if (_cogl_framebuffer_try_fast_read_pixel (framebuffer,
                x, y, source, format,
                pixels))
            return;
    }

    /* make sure any batched primitives get emitted to the GL driver
     * before issuing our read pixels...
     *
     * XXX: Note we currently use cogl_flush to ensure *all* journals
     * are flushed here and not _cogl_journal_flush because we don't
     * track the dependencies between framebuffers so we don't know if
     * the current framebuffer depends on the contents of other
     * framebuffers which could also have associated journal entries.
     */
    cogl_flush ();

    _cogl_framebuffer_flush_state (cogl_get_draw_framebuffer (),
                                   framebuffer,
                                   COGL_FRAMEBUFFER_STATE_BIND);

    framebuffer_height = cogl_framebuffer_get_height (framebuffer);

    /* The y co-ordinate should be given in OpenGL's coordinate system
     * so 0 is the bottom row
     *
     * NB: all offscreen rendering is done upside down so no conversion
     * is necissary in this case.
     */
    if (!cogl_is_offscreen (framebuffer))
        y = framebuffer_height - y - height;

    /* Initialise the CoglBitmap */
    bpp = _cogl_get_format_bpp (format);
    bmp_format = format;

    if ((format & COGL_A_BIT))
    {
        /* We match the premultiplied state of the target buffer to the
         * premultiplied state of the framebuffer so that it will get
         * converted to the right format below */

        if ((framebuffer->format & COGL_PREMULT_BIT))
            bmp_format |= COGL_PREMULT_BIT;
        else
            bmp_format &= ~COGL_PREMULT_BIT;
    }

    bmp = _cogl_bitmap_new_from_data (pixels,
                                      bmp_format, width, height, rowstride,
                                      NULL, NULL);

    ctx->texture_driver->pixel_format_to_gl (format,
            &gl_intformat,
            &gl_format,
            &gl_type);

    /* NB: All offscreen rendering is done upside down so there is no need
     * to flip in this case... */
    if ((ctx->private_feature_flags & COGL_PRIVATE_FEATURE_MESA_PACK_INVERT) &&
            !cogl_is_offscreen (framebuffer))
    {
        GE (ctx, glPixelStorei (GL_PACK_INVERT_MESA, TRUE));
        pack_invert_set = TRUE;
    }
    else
        pack_invert_set = FALSE;

    /* Under GLES only GL_RGBA with GL_UNSIGNED_BYTE as well as an
       implementation specific format under
       GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES and
       GL_IMPLEMENTATION_COLOR_READ_TYPE_OES is supported. We could try
       to be more clever and check if the requested type matches that
       but we would need some reliable functions to convert from GL
       types to Cogl types. For now, lets just always read in
       GL_RGBA/GL_UNSIGNED_BYTE and convert if necessary. We also need
       to use this intermediate buffer if the rowstride has padding
       because GLES does not support setting GL_ROW_LENGTH */
    if (ctx->driver != COGL_DRIVER_GL &&
            (gl_format != GL_RGBA || gl_type != GL_UNSIGNED_BYTE ||
             rowstride != 4 * width))
    {
        CoglBitmap *tmp_bmp, *dst_bmp;
        guint8 *tmp_data = g_malloc (width * height * 4);

        tmp_bmp = _cogl_bitmap_new_from_data (tmp_data,
                                              COGL_PIXEL_FORMAT_RGBA_8888 |
                                              (bmp_format & COGL_PREMULT_BIT),
                                              width, height, 4 * width,
                                              (CoglBitmapDestroyNotify) g_free,
                                              NULL);

        ctx->texture_driver->prep_gl_for_pixels_download (4 * width, 4);

        GE( ctx, glReadPixels (x, y, width, height,
                               GL_RGBA, GL_UNSIGNED_BYTE,
                               tmp_data) );

        /* CoglBitmap doesn't currently have a way to convert without
           allocating its own buffer so we have to copy the data
           again */
        if ((dst_bmp = _cogl_bitmap_convert_format_and_premult (tmp_bmp,
                       format)))
        {
            _cogl_bitmap_copy_subregion (dst_bmp,
                                         bmp,
                                         0, 0,
                                         0, 0,
                                         width, height);
            cogl_object_unref (dst_bmp);
        }
        else
        {
            /* FIXME: there's no way to report an error here so we'll
               just have to leave the data initialised */
        }

        cogl_object_unref (tmp_bmp);
    }
    else
    {
        ctx->texture_driver->prep_gl_for_pixels_download (rowstride, bpp);

        GE( ctx, glReadPixels (x, y, width, height, gl_format, gl_type, pixels) );

        /* Convert to the premult format specified by the caller
           in-place. This will do nothing if the premult status is already
           correct. */
        _cogl_bitmap_convert_premult_status (bmp, format);
    }

    /* Currently this function owns the pack_invert state and we don't want this
     * to interfere with other Cogl components so all other code can assume that
     * we leave the pack_invert state off. */
    if (pack_invert_set)
        GE (ctx, glPixelStorei (GL_PACK_INVERT_MESA, FALSE));

    /* NB: All offscreen rendering is done upside down so there is no need
     * to flip in this case... */
    if (!cogl_is_offscreen (framebuffer) && !pack_invert_set)
    {
        guint8 *temprow = g_alloca (rowstride * sizeof (guint8));

        /* vertically flip the buffer in-place */
        for (y = 0; y < height / 2; y++)
        {
            if (y != height - y - 1) /* skip center row */
            {
                memcpy (temprow,
                        pixels + y * rowstride, rowstride);
                memcpy (pixels + y * rowstride,
                        pixels + (height - y - 1) * rowstride, rowstride);
                memcpy (pixels + (height - y - 1) * rowstride,
                        temprow,
                        rowstride);
            }
        }
    }

    cogl_object_unref (bmp);
}