mirror of
https://github.com/brl/mutter.git
synced 2024-12-23 19:42:05 +00:00
7ddbcd1fd2
d42f1873fc
introduced a semi circular
reference between the CoglFramebuffer, and CoglJournal, where
CoglJournal would keep a reference on the CoglFramebuffer when there
were any entries in the journal log.
To avoid risking leaking these objects indefinitely, when freeing
objects without doing anything that triggered a flush, CoglFramebuffer
had a "filter" on cogl_object_unref() calls, which knew
about under what conditions CoglJournal had a reference to it. When it
could detect that there were only the journal itself holding such a
reference, it'd flush the journal, effectively releasing the reference
the journal held, thus freeing itself, as well as the journal.
When CoglFramebuffer was ported to be implemented using GObject instead
of CoglObject, this "filter" was missed, causing not only awkward but
infrequent leaks, but also situations where we'd flush journals when
only the journal itself held the last reference to the framebuffer,
meaning the journal would free the framebuffer, thus itself, in the
middle of flushing, causing memory corruption and crashes.
A way to detect this, by asserting on CoglObject reference count during
flush, is by adding the `g_assert()` as described below, which will
assert instead cause memory corruption.
void
_cogl_journal_flush (CoglJournal *journal
{
...
_cogl_journal_discard (journal);
+ g_assert (journal->_parent.ref_count > 0);
...
}
Fix this by making CoglFramebuffer the owner of the journal, which it
already was, and remove any circle referencing that was there before, as
it is not needed given that the CoglFramebuffer pointer is guaranteed to
be valid for the lifetime of CoglJournal as the framebuffer is the owner
of the journal.
However, to not miss flushing before tearing down, which is important as
this flushes painting calls to the driver that is important for e.g.
using the result of those journal entries, flush the journal the first
time cogl_framebuffer_dispose() is called, before doing anything else.
This also adds a test case. Without having broken the circular
reference, the test would fail on g_assert_null (offscreen), as it would
have been "leaked" at this point, but the actual memory corruption would
be a result of the `cogl_texture_get_data()` call, which flushes the
framebuffer, and causes the 'mid-flush' destruction of the journal
described above. Note that the texture keeps track of dependent
framebuffers, but it does not hold any references to them.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1474
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1735>
321 lines
11 KiB
C
321 lines
11 KiB
C
/*
|
|
* Cogl
|
|
*
|
|
* A Low Level GPU Graphics and Utilities API
|
|
*
|
|
* Copyright (C) 2007,2008,2009 Intel Corporation.
|
|
* Copyright (C) 2019 DisplayLink (UK) Ltd.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person
|
|
* obtaining a copy of this software and associated documentation
|
|
* files (the "Software"), to deal in the Software without
|
|
* restriction, including without limitation the rights to use, copy,
|
|
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
* of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*
|
|
*
|
|
*/
|
|
|
|
#ifndef __COGL_FRAMEBUFFER_PRIVATE_H
|
|
#define __COGL_FRAMEBUFFER_PRIVATE_H
|
|
|
|
#include "cogl-framebuffer-driver.h"
|
|
#include "cogl-object-private.h"
|
|
#include "cogl-matrix-stack-private.h"
|
|
#include "cogl-journal-private.h"
|
|
#include "winsys/cogl-winsys-private.h"
|
|
#include "cogl-attribute-private.h"
|
|
#include "cogl-clip-stack.h"
|
|
|
|
typedef enum
|
|
{
|
|
COGL_FRAMEBUFFER_DRIVER_TYPE_FBO,
|
|
COGL_FRAMEBUFFER_DRIVER_TYPE_BACK,
|
|
} CoglFramebufferDriverType;
|
|
|
|
struct _CoglFramebufferDriverConfig
|
|
{
|
|
CoglFramebufferDriverType type;
|
|
|
|
gboolean disable_depth_and_stencil;
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
CoglSwapChain *swap_chain;
|
|
gboolean need_stencil;
|
|
int samples_per_pixel;
|
|
gboolean stereo_enabled;
|
|
} CoglFramebufferConfig;
|
|
|
|
/* XXX: The order of these indices determines the order they are
|
|
* flushed.
|
|
*
|
|
* Flushing clip state may trash the modelview and projection matrices
|
|
* so we must do it before flushing the matrices.
|
|
*/
|
|
typedef enum _CoglFramebufferStateIndex
|
|
{
|
|
COGL_FRAMEBUFFER_STATE_INDEX_BIND = 0,
|
|
COGL_FRAMEBUFFER_STATE_INDEX_VIEWPORT = 1,
|
|
COGL_FRAMEBUFFER_STATE_INDEX_CLIP = 2,
|
|
COGL_FRAMEBUFFER_STATE_INDEX_DITHER = 3,
|
|
COGL_FRAMEBUFFER_STATE_INDEX_MODELVIEW = 4,
|
|
COGL_FRAMEBUFFER_STATE_INDEX_PROJECTION = 5,
|
|
COGL_FRAMEBUFFER_STATE_INDEX_FRONT_FACE_WINDING = 6,
|
|
COGL_FRAMEBUFFER_STATE_INDEX_DEPTH_WRITE = 7,
|
|
COGL_FRAMEBUFFER_STATE_INDEX_STEREO_MODE = 8,
|
|
COGL_FRAMEBUFFER_STATE_INDEX_MAX = 9
|
|
} CoglFramebufferStateIndex;
|
|
|
|
typedef enum _CoglFramebufferState
|
|
{
|
|
COGL_FRAMEBUFFER_STATE_BIND = 1<<0,
|
|
COGL_FRAMEBUFFER_STATE_VIEWPORT = 1<<1,
|
|
COGL_FRAMEBUFFER_STATE_CLIP = 1<<2,
|
|
COGL_FRAMEBUFFER_STATE_DITHER = 1<<3,
|
|
COGL_FRAMEBUFFER_STATE_MODELVIEW = 1<<4,
|
|
COGL_FRAMEBUFFER_STATE_PROJECTION = 1<<5,
|
|
COGL_FRAMEBUFFER_STATE_FRONT_FACE_WINDING = 1<<6,
|
|
COGL_FRAMEBUFFER_STATE_DEPTH_WRITE = 1<<7,
|
|
COGL_FRAMEBUFFER_STATE_STEREO_MODE = 1<<8
|
|
} CoglFramebufferState;
|
|
|
|
#define COGL_FRAMEBUFFER_STATE_ALL ((1<<COGL_FRAMEBUFFER_STATE_INDEX_MAX) - 1)
|
|
|
|
/* Private flags that can internally be added to CoglReadPixelsFlags */
|
|
typedef enum
|
|
{
|
|
/* If this is set then the data will not be flipped to compensate
|
|
for GL's upside-down coordinate system but instead will be left
|
|
in whatever order GL gives us (which will depend on whether the
|
|
framebuffer is offscreen or not) */
|
|
COGL_READ_PIXELS_NO_FLIP = 1L << 30
|
|
} CoglPrivateReadPixelsFlags;
|
|
|
|
typedef struct _CoglFramebufferBits
|
|
{
|
|
int red;
|
|
int blue;
|
|
int green;
|
|
int alpha;
|
|
int depth;
|
|
int stencil;
|
|
} CoglFramebufferBits;
|
|
|
|
gboolean
|
|
cogl_framebuffer_is_allocated (CoglFramebuffer *framebuffer);
|
|
|
|
void
|
|
cogl_framebuffer_init_config (CoglFramebuffer *framebuffer,
|
|
const CoglFramebufferConfig *config);
|
|
|
|
const CoglFramebufferConfig *
|
|
cogl_framebuffer_get_config (CoglFramebuffer *framebuffer);
|
|
|
|
void
|
|
cogl_framebuffer_update_samples_per_pixel (CoglFramebuffer *framebuffer,
|
|
int samples_per_pixel);
|
|
|
|
void
|
|
cogl_framebuffer_update_size (CoglFramebuffer *framebuffer,
|
|
int width,
|
|
int height);
|
|
|
|
/* XXX: For a public api we might instead want a way to explicitly
|
|
* set the _premult status of a framebuffer or what components we
|
|
* care about instead of exposing the CoglPixelFormat
|
|
* internal_format.
|
|
*
|
|
* The current use case for this api is where we create an offscreen
|
|
* framebuffer for a shared atlas texture that has a format of
|
|
* RGBA_8888 disregarding the premultiplied alpha status for
|
|
* individual atlased textures or whether the alpha component is being
|
|
* discarded. We want to overried the internal_format that will be
|
|
* derived from the texture.
|
|
*/
|
|
void
|
|
_cogl_framebuffer_set_internal_format (CoglFramebuffer *framebuffer,
|
|
CoglPixelFormat internal_format);
|
|
|
|
CoglPixelFormat
|
|
cogl_framebuffer_get_internal_format (CoglFramebuffer *framebuffer);
|
|
|
|
void _cogl_framebuffer_free (CoglFramebuffer *framebuffer);
|
|
|
|
const CoglWinsysVtable *
|
|
_cogl_framebuffer_get_winsys (CoglFramebuffer *framebuffer);
|
|
|
|
void
|
|
_cogl_framebuffer_clear_without_flush4f (CoglFramebuffer *framebuffer,
|
|
unsigned long buffers,
|
|
float red,
|
|
float green,
|
|
float blue,
|
|
float alpha);
|
|
|
|
void
|
|
_cogl_framebuffer_mark_clear_clip_dirty (CoglFramebuffer *framebuffer);
|
|
|
|
void
|
|
cogl_framebuffer_set_depth_buffer_clear_needed (CoglFramebuffer *framebuffer);
|
|
|
|
/*
|
|
* _cogl_framebuffer_get_clip_stack:
|
|
* @framebuffer: A #CoglFramebuffer
|
|
*
|
|
* Gets a pointer to the current clip stack. A reference is not taken on the
|
|
* stack so if you want to keep it you should call
|
|
* _cogl_clip_stack_ref().
|
|
*
|
|
* Return value: a pointer to the @framebuffer clip stack.
|
|
*/
|
|
CoglClipStack *
|
|
_cogl_framebuffer_get_clip_stack (CoglFramebuffer *framebuffer);
|
|
|
|
COGL_EXPORT CoglMatrixStack *
|
|
_cogl_framebuffer_get_modelview_stack (CoglFramebuffer *framebuffer);
|
|
|
|
COGL_EXPORT CoglMatrixStack *
|
|
_cogl_framebuffer_get_projection_stack (CoglFramebuffer *framebuffer);
|
|
|
|
void
|
|
_cogl_framebuffer_add_dependency (CoglFramebuffer *framebuffer,
|
|
CoglFramebuffer *dependency);
|
|
|
|
void
|
|
_cogl_framebuffer_flush_journal (CoglFramebuffer *framebuffer);
|
|
|
|
void
|
|
_cogl_framebuffer_flush_dependency_journals (CoglFramebuffer *framebuffer);
|
|
|
|
void
|
|
cogl_context_flush_framebuffer_state (CoglContext *context,
|
|
CoglFramebuffer *draw_buffer,
|
|
CoglFramebuffer *read_buffer,
|
|
CoglFramebufferState state);
|
|
|
|
CoglFramebuffer *
|
|
_cogl_get_read_framebuffer (void);
|
|
|
|
GSList *
|
|
_cogl_create_framebuffer_stack (void);
|
|
|
|
void
|
|
_cogl_free_framebuffer_stack (GSList *stack);
|
|
|
|
void
|
|
_cogl_framebuffer_save_clip_stack (CoglFramebuffer *framebuffer);
|
|
|
|
void
|
|
_cogl_framebuffer_restore_clip_stack (CoglFramebuffer *framebuffer);
|
|
|
|
/* This can be called directly by the CoglJournal to draw attributes
|
|
* skipping the implicit journal flush, the framebuffer flush and
|
|
* pipeline validation. */
|
|
void
|
|
_cogl_framebuffer_draw_attributes (CoglFramebuffer *framebuffer,
|
|
CoglPipeline *pipeline,
|
|
CoglVerticesMode mode,
|
|
int first_vertex,
|
|
int n_vertices,
|
|
CoglAttribute **attributes,
|
|
int n_attributes,
|
|
CoglDrawFlags flags);
|
|
|
|
void
|
|
_cogl_framebuffer_draw_indexed_attributes (CoglFramebuffer *framebuffer,
|
|
CoglPipeline *pipeline,
|
|
CoglVerticesMode mode,
|
|
int first_vertex,
|
|
int n_vertices,
|
|
CoglIndices *indices,
|
|
CoglAttribute **attributes,
|
|
int n_attributes,
|
|
CoglDrawFlags flags);
|
|
|
|
void
|
|
cogl_framebuffer_set_viewport4fv (CoglFramebuffer *framebuffer,
|
|
float *viewport);
|
|
|
|
void
|
|
cogl_framebuffer_get_viewport4f (CoglFramebuffer *framebuffer,
|
|
float *viewport_x,
|
|
float *viewport_y,
|
|
float *viewport_width,
|
|
float *viewport_height);
|
|
|
|
unsigned long
|
|
_cogl_framebuffer_compare (CoglFramebuffer *a,
|
|
CoglFramebuffer *b,
|
|
unsigned long state);
|
|
|
|
static inline CoglMatrixEntry *
|
|
_cogl_framebuffer_get_modelview_entry (CoglFramebuffer *framebuffer)
|
|
{
|
|
CoglMatrixStack *modelview_stack =
|
|
_cogl_framebuffer_get_modelview_stack (framebuffer);
|
|
return modelview_stack->last_entry;
|
|
}
|
|
|
|
static inline CoglMatrixEntry *
|
|
_cogl_framebuffer_get_projection_entry (CoglFramebuffer *framebuffer)
|
|
{
|
|
CoglMatrixStack *projection_stack =
|
|
_cogl_framebuffer_get_projection_stack (framebuffer);
|
|
return projection_stack->last_entry;
|
|
}
|
|
|
|
gboolean
|
|
_cogl_framebuffer_read_pixels_into_bitmap (CoglFramebuffer *framebuffer,
|
|
int x,
|
|
int y,
|
|
CoglReadPixelsFlags source,
|
|
CoglBitmap *bitmap,
|
|
GError **error);
|
|
|
|
/*
|
|
* _cogl_framebuffer_get_stencil_bits:
|
|
* @framebuffer: a pointer to a #CoglFramebuffer
|
|
*
|
|
* Retrieves the number of stencil bits of @framebuffer
|
|
*
|
|
* Return value: the number of bits
|
|
*
|
|
* Since: 2.0
|
|
* Stability: unstable
|
|
*/
|
|
COGL_EXPORT int
|
|
_cogl_framebuffer_get_stencil_bits (CoglFramebuffer *framebuffer);
|
|
|
|
CoglJournal *
|
|
cogl_framebuffer_get_journal (CoglFramebuffer *framebuffer);
|
|
|
|
CoglFramebufferDriver *
|
|
cogl_framebuffer_get_driver (CoglFramebuffer *framebuffer);
|
|
|
|
/**
|
|
* cogl_framebuffer_is_y_flipped:
|
|
* @framebuffer: a #CoglFramebuffer
|
|
*
|
|
* Returns %TRUE if the Y coordinate 0 means the bottom of the framebuffer, and
|
|
* %FALSE if the Y coordinate means the top.
|
|
*/
|
|
gboolean
|
|
cogl_framebuffer_is_y_flipped (CoglFramebuffer *framebuffer);
|
|
|
|
#endif /* __COGL_FRAMEBUFFER_PRIVATE_H */
|