8019714b5d
Seems we were leaking mipmap->mipmap_texture and mipmap->fb. Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2562>
241 lines
6.6 KiB
C
241 lines
6.6 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
/*
|
|
* MetaTextureMipmap
|
|
*
|
|
* Mipmap management object using OpenGL
|
|
*
|
|
* Copyright (C) 2009 Red Hat, Inc.
|
|
* Copyright (C) 2021 Canonical Ltd.
|
|
* Copyright (C) 2022 Neil Moore
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "compositor/meta-texture-mipmap.h"
|
|
|
|
#include <math.h>
|
|
#include <string.h>
|
|
|
|
struct _MetaTextureMipmap
|
|
{
|
|
CoglTexture *base_texture;
|
|
CoglTexture *mipmap_texture;
|
|
CoglPipeline *pipeline;
|
|
CoglFramebuffer *fb;
|
|
gboolean invalid;
|
|
};
|
|
|
|
/**
|
|
* meta_texture_mipmap_new:
|
|
*
|
|
* Creates a new mipmap handler. The base texture has to be set with
|
|
* meta_texture_mipmap_set_base_texture() before use.
|
|
*
|
|
* Return value: the new texture mipmap handler. Free with meta_texture_mipmap_free()
|
|
*/
|
|
MetaTextureMipmap *
|
|
meta_texture_mipmap_new (void)
|
|
{
|
|
MetaTextureMipmap *mipmap;
|
|
|
|
mipmap = g_new0 (MetaTextureMipmap, 1);
|
|
|
|
return mipmap;
|
|
}
|
|
|
|
/**
|
|
* meta_texture_mipmap_free:
|
|
* @mipmap: a #MetaTextureMipmap
|
|
*
|
|
* Frees a texture mipmap handler created with meta_texture_mipmap_new().
|
|
*/
|
|
void
|
|
meta_texture_mipmap_free (MetaTextureMipmap *mipmap)
|
|
{
|
|
g_return_if_fail (mipmap != NULL);
|
|
|
|
cogl_clear_object (&mipmap->pipeline);
|
|
cogl_clear_object (&mipmap->base_texture);
|
|
cogl_clear_object (&mipmap->mipmap_texture);
|
|
g_clear_object (&mipmap->fb);
|
|
|
|
g_free (mipmap);
|
|
}
|
|
|
|
/**
|
|
* meta_texture_mipmap_set_base_texture:
|
|
* @mipmap: a #MetaTextureMipmap
|
|
* @texture: the new texture used as a base for scaled down versions
|
|
*
|
|
* Sets the base texture that is the scaled texture that the
|
|
* scaled textures of the tower are derived from. The texture itself
|
|
* will be used as level 0 of the tower and will be referenced until
|
|
* unset or until the tower is freed.
|
|
*/
|
|
void
|
|
meta_texture_mipmap_set_base_texture (MetaTextureMipmap *mipmap,
|
|
CoglTexture *texture)
|
|
{
|
|
g_return_if_fail (mipmap != NULL);
|
|
|
|
if (texture == mipmap->base_texture)
|
|
return;
|
|
|
|
cogl_clear_object (&mipmap->base_texture);
|
|
|
|
mipmap->base_texture = texture;
|
|
|
|
if (mipmap->base_texture != NULL)
|
|
{
|
|
cogl_object_ref (mipmap->base_texture);
|
|
mipmap->invalid = TRUE;
|
|
}
|
|
}
|
|
|
|
void
|
|
meta_texture_mipmap_invalidate (MetaTextureMipmap *mipmap)
|
|
{
|
|
g_return_if_fail (mipmap != NULL);
|
|
|
|
mipmap->invalid = TRUE;
|
|
}
|
|
|
|
static void
|
|
free_mipmaps (MetaTextureMipmap *mipmap)
|
|
{
|
|
g_clear_object (&mipmap->fb);
|
|
cogl_clear_object (&mipmap->mipmap_texture);
|
|
}
|
|
|
|
void
|
|
meta_texture_mipmap_clear (MetaTextureMipmap *mipmap)
|
|
{
|
|
g_return_if_fail (mipmap != NULL);
|
|
|
|
free_mipmaps (mipmap);
|
|
}
|
|
|
|
static void
|
|
ensure_mipmap_texture (MetaTextureMipmap *mipmap)
|
|
{
|
|
CoglContext *ctx =
|
|
clutter_backend_get_cogl_context (clutter_get_default_backend ());
|
|
int width, height;
|
|
|
|
/* Let's avoid spending any texture memory copying the base level texture
|
|
* because we'll never need that one and it would have used most of the
|
|
* memory;
|
|
* S(0) = W x H
|
|
* S(n) = S(n-1) / 4
|
|
* sum to infinity of S(n) = 4/3 * S(0)
|
|
* So subtracting S(0) means even infinite mipmap levels only need one third
|
|
* of the original texture's memory. Finite levels need less.
|
|
*
|
|
* The fact that mipmap level 0 of mipmap texture is half the
|
|
* resolution of original texture makes no visual difference, so long as you're
|
|
* never trying to view a level of detail higher than half. If you need that
|
|
* then just use the original texture instead of mipmap texture, which is
|
|
* faster anyway.
|
|
*/
|
|
width = cogl_texture_get_width (mipmap->base_texture) / 2;
|
|
height = cogl_texture_get_height (mipmap->base_texture) / 2;
|
|
|
|
if (!width || !height)
|
|
{
|
|
free_mipmaps (mipmap);
|
|
return;
|
|
}
|
|
|
|
if (!mipmap->mipmap_texture ||
|
|
cogl_texture_get_width (mipmap->mipmap_texture) != width ||
|
|
cogl_texture_get_height (mipmap->mipmap_texture) != height)
|
|
{
|
|
CoglOffscreen *offscreen;
|
|
CoglTexture2D *tex2d;
|
|
|
|
free_mipmaps (mipmap);
|
|
|
|
tex2d = cogl_texture_2d_new_with_size (ctx, width, height);
|
|
if (!tex2d)
|
|
return;
|
|
|
|
mipmap->mipmap_texture = COGL_TEXTURE (tex2d);
|
|
|
|
offscreen = cogl_offscreen_new_with_texture (mipmap->mipmap_texture);
|
|
if (!offscreen)
|
|
{
|
|
free_mipmaps (mipmap);
|
|
return;
|
|
}
|
|
|
|
mipmap->fb = COGL_FRAMEBUFFER (offscreen);
|
|
|
|
if (!cogl_framebuffer_allocate (mipmap->fb, NULL))
|
|
{
|
|
free_mipmaps (mipmap);
|
|
return;
|
|
}
|
|
|
|
cogl_framebuffer_orthographic (mipmap->fb,
|
|
0, 0, width, height, -1.0, 1.0);
|
|
|
|
mipmap->invalid = TRUE;
|
|
}
|
|
|
|
if (mipmap->invalid)
|
|
{
|
|
if (!mipmap->pipeline)
|
|
{
|
|
mipmap->pipeline = cogl_pipeline_new (ctx);
|
|
cogl_pipeline_set_blend (mipmap->pipeline, "RGBA = ADD (SRC_COLOR, 0)", NULL);
|
|
cogl_pipeline_set_layer_filters (mipmap->pipeline, 0,
|
|
COGL_PIPELINE_FILTER_LINEAR,
|
|
COGL_PIPELINE_FILTER_LINEAR);
|
|
}
|
|
|
|
cogl_pipeline_set_layer_texture (mipmap->pipeline, 0, mipmap->base_texture);
|
|
cogl_framebuffer_draw_textured_rectangle (mipmap->fb,
|
|
mipmap->pipeline,
|
|
0, 0, width, height,
|
|
0.0, 0.0, 1.0, 1.0);
|
|
|
|
mipmap->invalid = FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* meta_texture_tower_get_paint_texture:
|
|
* @mipmap: a #MetaTextureMipmap
|
|
*
|
|
* Gets the texture from the tower that best matches the current
|
|
* rendering scale. (On the assumption here the texture is going to
|
|
* be rendered with vertex coordinates that correspond to its
|
|
* size in pixels, so a 200x200 texture will be rendered on the
|
|
* rectangle (0, 0, 200, 200).
|
|
*
|
|
* Return value: the COGL texture handle to use for painting, or
|
|
* %NULL if no base texture has yet been set.
|
|
*/
|
|
CoglTexture *
|
|
meta_texture_mipmap_get_paint_texture (MetaTextureMipmap *mipmap)
|
|
{
|
|
g_return_val_if_fail (mipmap != NULL, NULL);
|
|
|
|
ensure_mipmap_texture (mipmap);
|
|
|
|
return mipmap->mipmap_texture;
|
|
}
|