From 877dc33545e76cf71358d304caf39755b74081a7 Mon Sep 17 00:00:00 2001 From: Neil Moore Date: Fri, 10 Jun 2022 19:13:57 -0400 Subject: [PATCH] shaped-texture: Move texture mipmap implementation to 'meta-texture-mipmap' Part-of: --- src/compositor/meta-shaped-texture.c | 120 +------ src/compositor/meta-texture-mipmap.c | 240 +++++++++++++ src/compositor/meta-texture-mipmap.h | 54 +++ src/compositor/meta-texture-tower.c | 513 --------------------------- src/compositor/meta-texture-tower.h | 71 ---- src/meson.build | 4 +- 6 files changed, 307 insertions(+), 695 deletions(-) create mode 100644 src/compositor/meta-texture-mipmap.c create mode 100644 src/compositor/meta-texture-mipmap.h delete mode 100644 src/compositor/meta-texture-tower.c delete mode 100644 src/compositor/meta-texture-tower.h diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c index 031e21551..b8e4dff5d 100644 --- a/src/compositor/meta-shaped-texture.c +++ b/src/compositor/meta-shaped-texture.c @@ -41,6 +41,7 @@ #include "cogl/cogl.h" #include "compositor/clutter-utils.h" +#include "compositor/meta-texture-mipmap.h" #include "compositor/region-utils.h" #include "core/boxes-private.h" #include "meta/meta-shaped-texture.h" @@ -93,10 +94,7 @@ struct _MetaShapedTexture CoglPipeline *unblended_pipeline; CoglPipeline *unblended_tower_pipeline; - CoglTexture *mipmap_texture; - gboolean mipmap_texture_out_of_date; - CoglFramebuffer *mipmap_fb; - CoglPipeline *mipmap_pipeline; + MetaTextureMipmap *texture_mipmap; gboolean is_y_inverted; @@ -156,6 +154,7 @@ invalidate_size (MetaShapedTexture *stex) static void meta_shaped_texture_init (MetaShapedTexture *stex) { + stex->texture_mipmap = meta_texture_mipmap_new (); stex->buffer_scale = 1; stex->texture = NULL; stex->mask_texture = NULL; @@ -252,13 +251,6 @@ meta_shaped_texture_reset_pipelines (MetaShapedTexture *stex) g_clear_pointer (&stex->unblended_tower_pipeline, cogl_object_unref); } -static void -free_mipmaps (MetaShapedTexture *stex) -{ - g_clear_object (&stex->mipmap_fb); - cogl_clear_object (&stex->mipmap_texture); -} - static void meta_shaped_texture_dispose (GObject *object) { @@ -266,8 +258,7 @@ meta_shaped_texture_dispose (GObject *object) g_clear_handle_id (&stex->remipmap_timeout_id, g_source_remove); - free_mipmaps (stex); - cogl_clear_object (&stex->mipmap_pipeline); + g_clear_pointer (&stex->texture_mipmap, meta_texture_mipmap_free); g_clear_pointer (&stex->texture, cogl_object_unref); @@ -624,7 +615,8 @@ set_cogl_texture (MetaShapedTexture *stex, update_size (stex); } - stex->mipmap_texture_out_of_date = TRUE; + meta_texture_mipmap_set_base_texture (stex->texture_mipmap, stex->texture); + meta_texture_mipmap_invalidate (stex->texture_mipmap); } static gboolean @@ -723,7 +715,7 @@ do_paint_content (MetaShapedTexture *stex, mag_filter = COGL_PIPELINE_FILTER_NEAREST; /* Back to normal desktop viewing. Save some memory */ - free_mipmaps (stex); + meta_texture_mipmap_clear (stex->texture_mipmap); } else { @@ -740,8 +732,6 @@ do_paint_content (MetaShapedTexture *stex, transforms.y_scale < 0.5) { paint_tex = select_texture_for_paint (stex, paint_context); - if (paint_tex == stex->mipmap_texture) - min_filter = COGL_PIPELINE_FILTER_LINEAR_MIPMAP_NEAREST; } } @@ -922,94 +912,6 @@ do_paint_content (MetaShapedTexture *stex, g_clear_pointer (&blended_tex_region, cairo_region_destroy); } -static void -ensure_mipmap_texture (MetaShapedTexture *stex) -{ - 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 stex->mipmap_texture is half the - * resolution of stex->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 stex->texture instead of stex->mipmap_texture, which is - * faster anyway. - */ - width = cogl_texture_get_width (stex->texture) / 2; - height = cogl_texture_get_height (stex->texture) / 2; - - if (!width || !height) - { - free_mipmaps (stex); - return; - } - - if (!stex->mipmap_texture || - cogl_texture_get_width (stex->mipmap_texture) != width || - cogl_texture_get_height (stex->mipmap_texture) != height) - { - CoglOffscreen *offscreen; - CoglTexture2D *tex2d; - - free_mipmaps (stex); - - tex2d = cogl_texture_2d_new_with_size (ctx, width, height); - if (!tex2d) - return; - - stex->mipmap_texture = COGL_TEXTURE (tex2d); - - offscreen = cogl_offscreen_new_with_texture (stex->mipmap_texture); - if (!offscreen) - { - free_mipmaps (stex); - return; - } - - stex->mipmap_fb = COGL_FRAMEBUFFER (offscreen); - - if (!cogl_framebuffer_allocate (stex->mipmap_fb, NULL)) - { - free_mipmaps (stex); - return; - } - - cogl_framebuffer_orthographic (stex->mipmap_fb, - 0, 0, width, height, -1.0, 1.0); - - stex->mipmap_texture_out_of_date = TRUE; - } - - if (stex->mipmap_texture_out_of_date) - { - if (!stex->mipmap_pipeline) - { - stex->mipmap_pipeline = cogl_pipeline_new (ctx); - cogl_pipeline_set_blend (stex->mipmap_pipeline, "RGBA = ADD (SRC_COLOR, 0)", NULL); - cogl_pipeline_set_layer_filters (stex->mipmap_pipeline, 0, - COGL_PIPELINE_FILTER_LINEAR, - COGL_PIPELINE_FILTER_LINEAR); - } - - cogl_pipeline_set_layer_texture (stex->mipmap_pipeline, 0, stex->texture); - cogl_framebuffer_draw_textured_rectangle (stex->mipmap_fb, - stex->mipmap_pipeline, - 0, 0, width, height, - 0.0, 0.0, 1.0, 1.0); - - stex->mipmap_texture_out_of_date = FALSE; - } -} - static CoglTexture * select_texture_for_paint (MetaShapedTexture *stex, ClutterPaintContext *paint_context) @@ -1029,8 +931,7 @@ select_texture_for_paint (MetaShapedTexture *stex, if (age >= MIN_MIPMAP_AGE_USEC || stex->fast_updates < MIN_FAST_UPDATES_BEFORE_UNMIPMAP) { - ensure_mipmap_texture (stex); - texture = stex->mipmap_texture; + texture = meta_texture_mipmap_get_paint_texture (stex->texture_mipmap); } } @@ -1129,7 +1030,7 @@ meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex, stex->create_mipmaps = create_mipmaps; if (!stex->create_mipmaps) - free_mipmaps (stex); + meta_texture_mipmap_clear (stex->texture_mipmap); } } @@ -1258,7 +1159,8 @@ meta_shaped_texture_update_area (MetaShapedTexture *stex, clip); } - stex->mipmap_texture_out_of_date = TRUE; + meta_texture_mipmap_invalidate (stex->texture_mipmap); + stex->prev_invalidation = stex->last_invalidation; stex->last_invalidation = g_get_monotonic_time (); diff --git a/src/compositor/meta-texture-mipmap.c b/src/compositor/meta-texture-mipmap.c new file mode 100644 index 000000000..2ee9190e2 --- /dev/null +++ b/src/compositor/meta-texture-mipmap.c @@ -0,0 +1,240 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * MetaTextureMipmap + * + * Mipmap management object using OpenGL + * + * Copyright (C) 2009 Red Hat, Inc. + * + * 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 . + */ + +#include "config.h" + +#include "compositor/meta-texture-mipmap.h" + +#include +#include + +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); + + meta_texture_mipmap_set_base_texture (mipmap, NULL); + + 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; + + if (mipmap->base_texture != NULL) + { + cogl_object_unref (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; +} diff --git a/src/compositor/meta-texture-mipmap.h b/src/compositor/meta-texture-mipmap.h new file mode 100644 index 000000000..fd83a49c2 --- /dev/null +++ b/src/compositor/meta-texture-mipmap.h @@ -0,0 +1,54 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* + * MetaTextureMipmap + * + * Mipmap management object using OpenGL + * + * Copyright (C) 2009 Red Hat, Inc. + * + * 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 . + */ + +#ifndef META_TEXTURE_MIPMAP_H +#define META_TEXTURE_MIPMAP_H + +#include "clutter/clutter.h" + +G_BEGIN_DECLS + +/** + * SECTION:MetaTextureMipmap + * @short_description: mipmap handling for textures + * + * A #MetaTextureMipmap is used to get GL mipmaps for a texture + */ + +typedef struct _MetaTextureMipmap MetaTextureMipmap; + +MetaTextureMipmap *meta_texture_mipmap_new (void); + +void meta_texture_mipmap_free (MetaTextureMipmap *mipmap); + +void meta_texture_mipmap_set_base_texture (MetaTextureMipmap *mipmap, + CoglTexture *texture); + +CoglTexture *meta_texture_mipmap_get_paint_texture (MetaTextureMipmap *mipmap); + +void meta_texture_mipmap_invalidate (MetaTextureMipmap *mipmap); + +void meta_texture_mipmap_clear (MetaTextureMipmap *mipmap); + +G_END_DECLS + +#endif /* META_TEXTURE_MIPMAP_H */ diff --git a/src/compositor/meta-texture-tower.c b/src/compositor/meta-texture-tower.c deleted file mode 100644 index 510e85f79..000000000 --- a/src/compositor/meta-texture-tower.c +++ /dev/null @@ -1,513 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ -/* - * MetaTextureTower - * - * Mipmap emulation by creation of scaled down images - * - * Copyright (C) 2009 Red Hat, Inc. - * - * 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 . - */ - -#include "config.h" - -#include -#include - -#include "compositor/meta-texture-tower.h" - -#ifndef M_LOG2E -#define M_LOG2E 1.4426950408889634074 -#endif - -#define MAX_TEXTURE_LEVELS 12 - -/* If the texture format in memory doesn't match this, then Mesa - * will do the conversion, so things will still work, but it might - * be slow depending on how efficient Mesa is. These should be the - * native formats unless the display is 16bpp. If conversions - * here are a bottleneck, investigate whether we are converting when - * storing window data *into* the texture before adding extra code - * to handle multiple texture formats. - */ -#if G_BYTE_ORDER == G_LITTLE_ENDIAN -#define TEXTURE_FORMAT COGL_PIXEL_FORMAT_BGRA_8888_PRE -#else -#define TEXTURE_FORMAT COGL_PIXEL_FORMAT_ARGB_8888_PRE -#endif - -typedef struct -{ - guint16 x1; - guint16 y1; - guint16 x2; - guint16 y2; -} Box; - -struct _MetaTextureTower -{ - int n_levels; - CoglTexture *textures[MAX_TEXTURE_LEVELS]; - CoglOffscreen *fbos[MAX_TEXTURE_LEVELS]; - Box invalid[MAX_TEXTURE_LEVELS]; - CoglPipeline *pipeline_template; - CoglSnippet *snippet; -}; - -/** - * meta_texture_tower_new: - * - * Creates a new texture tower. The base texture has to be set with - * meta_texture_tower_set_base_texture() before use. - * - * Return value: the new texture tower. Free with meta_texture_tower_free() - */ -MetaTextureTower * -meta_texture_tower_new (void) -{ - MetaTextureTower *tower; - - tower = g_new0 (MetaTextureTower, 1); - - return tower; -} - -/** - * meta_texture_tower_free: - * @tower: a #MetaTextureTower - * - * Frees a texture tower created with meta_texture_tower_new(). - */ -void -meta_texture_tower_free (MetaTextureTower *tower) -{ - g_return_if_fail (tower != NULL); - - if (tower->pipeline_template != NULL) - cogl_object_unref (tower->pipeline_template); - - meta_texture_tower_set_base_texture (tower, NULL); - cogl_clear_object (&tower->snippet); - - g_free (tower); -} - -/** - * meta_texture_tower_set_base_texture: - * @tower: a #MetaTextureTower - * @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_tower_set_base_texture (MetaTextureTower *tower, - CoglTexture *texture) -{ - int i; - - g_return_if_fail (tower != NULL); - - if (texture == tower->textures[0]) - return; - - if (tower->textures[0] != NULL) - { - for (i = 1; i < tower->n_levels; i++) - { - cogl_clear_object (&tower->textures[i]); - g_clear_object (&tower->fbos[i]); - } - - cogl_object_unref (tower->textures[0]); - } - - tower->textures[0] = texture; - - if (tower->textures[0] != NULL) - { - int width, height; - - cogl_object_ref (tower->textures[0]); - - width = cogl_texture_get_width (tower->textures[0]); - height = cogl_texture_get_height (tower->textures[0]); - - tower->n_levels = 1 + MAX ((int)(M_LOG2E * log (width)), (int)(M_LOG2E * log (height))); - tower->n_levels = MIN (tower->n_levels, MAX_TEXTURE_LEVELS); - - meta_texture_tower_update_area (tower, 0, 0, width, height); - } - else - { - tower->n_levels = 0; - } -} - -/** - * meta_texture_tower_update_area: - * @tower: a #MetaTextureTower - * @x: X coordinate of upper left of rectangle that changed - * @y: Y coordinate of upper left of rectangle that changed - * @width: width of rectangle that changed - * @height: height rectangle that changed - * - * Mark a region of the base texture as having changed; the next - * time a scaled down version of the base texture is retrieved, - * the appropriate area of the scaled down texture will be updated. - */ -void -meta_texture_tower_update_area (MetaTextureTower *tower, - int x, - int y, - int width, - int height) -{ - int texture_width, texture_height; - Box invalid; - int i; - - g_return_if_fail (tower != NULL); - - if (tower->textures[0] == NULL) - return; - - texture_width = cogl_texture_get_width (tower->textures[0]); - texture_height = cogl_texture_get_height (tower->textures[0]); - - invalid.x1 = x; - invalid.y1 = y; - invalid.x2 = x + width; - invalid.y2 = y + height; - - for (i = 1; i < tower->n_levels; i++) - { - texture_width = MAX (1, texture_width / 2); - texture_height = MAX (1, texture_height / 2); - - invalid.x1 = invalid.x1 / 2; - invalid.y1 = invalid.y1 / 2; - invalid.x2 = MIN (texture_width, (invalid.x2 + 1) / 2); - invalid.y2 = MIN (texture_height, (invalid.y2 + 1) / 2); - - if (tower->invalid[i].x1 == tower->invalid[i].x2 || - tower->invalid[i].y1 == tower->invalid[i].y2) - { - tower->invalid[i] = invalid; - } - else - { - tower->invalid[i].x1 = MIN (tower->invalid[i].x1, invalid.x1); - tower->invalid[i].y1 = MIN (tower->invalid[i].y1, invalid.y1); - tower->invalid[i].x2 = MAX (tower->invalid[i].x2, invalid.x2); - tower->invalid[i].y2 = MAX (tower->invalid[i].y2, invalid.y2); - } - } -} - -void -meta_texture_tower_set_snippet (MetaTextureTower *tower, - CoglSnippet *snippet) -{ - int i; - - if (tower->snippet == snippet) - return; - - g_clear_pointer (&tower->snippet, cogl_object_unref); - - if (snippet) - tower->snippet = cogl_object_ref (snippet); - - for (i = 1; i < tower->n_levels; i++) - { - cogl_clear_object (&tower->textures[i]); - g_clear_object (&tower->fbos[i]); - } - cogl_clear_object (&tower->pipeline_template); -} - -/* It generally looks worse if we scale up a window texture by even a - * small amount than if we scale it down using bilinear filtering, so - * we always pick the *larger* adjacent level. */ -#define LOD_BIAS (-0.49) - -/* This determines the appropriate level of detail to use when drawing the - * texture, in a way that corresponds to what the GL specification does - * when mip-mapping. This is probably fancier and slower than what we need, - * but we do the computation only once each time we paint a window, and - * its easier to just use the equations from the specification than to - * come up with something simpler. - * - * If window is being painted at an angle from the viewer, then we have to - * pick a point in the texture; we use the middle of the texture (which is - * why the width/height are passed in.) This is not the normal case for - * Meta. - */ -static int -get_paint_level (ClutterPaintContext *paint_context, - int width, - int height) -{ - CoglFramebuffer *framebuffer; - graphene_matrix_t projection, modelview, pm; - float xx, xy, xw; - float yx, yy, yw; - float wx, wy, ww; - float v[4]; - double viewport_width, viewport_height; - double u0, v0; - double xc, yc, wc; - double dxdu_, dxdv_, dydu_, dydv_; - double det_, det_sq; - double rho_sq; - double lambda; - - /* See - * http://www.opengl.org/registry/doc/glspec32.core.20090803.pdf - * Section 3.8.9, p. 1.6.2. Here we have - * - * u(x,y) = x_o; - * v(x,y) = y_o; - * - * Since we are mapping 1:1 from object coordinates into pixel - * texture coordinates, the clip coordinates are: - * - * (x_c) (x_o) (u) - * (y_c) = (M_projection)(M_modelview) (y_o) = (PM) (v) - * (z_c) (z_o) (0) - * (w_c) (w_o) (1) - */ - - framebuffer = clutter_paint_context_get_framebuffer (paint_context); - cogl_framebuffer_get_projection_matrix (framebuffer, &projection); - cogl_framebuffer_get_modelview_matrix (framebuffer, &modelview); - - graphene_matrix_multiply (&modelview, &projection, &pm); - - xx = graphene_matrix_get_value (&pm, 0, 0); - xy = graphene_matrix_get_value (&pm, 0, 1); - xw = graphene_matrix_get_value (&pm, 0, 3); - yx = graphene_matrix_get_value (&pm, 1, 0); - yy = graphene_matrix_get_value (&pm, 1, 1); - yw = graphene_matrix_get_value (&pm, 1, 3); - wx = graphene_matrix_get_value (&pm, 3, 0); - wy = graphene_matrix_get_value (&pm, 3, 1); - ww = graphene_matrix_get_value (&pm, 3, 3); - - cogl_framebuffer_get_viewport4fv (framebuffer, v); - viewport_width = v[2]; - viewport_height = v[3]; - - u0 = width / 2.; - v0 = height / 2.; - - xc = xx * u0 + yx * v0 + wx; - yc = xy * u0 + yy * v0 + wy; - wc = xw * u0 + yw * v0 + ww; - - /* We'll simplify the equations below for a bit of micro-optimization. - * The commented out code is the unsimplified version. - - // Partial derivates of window coordinates: - // - // x_w = 0.5 * viewport_width * x_c / w_c + viewport_center_x - // y_w = 0.5 * viewport_height * y_c / w_c + viewport_center_y - // - // with respect to u, v, using - // d(a/b)/dx = da/dx * (1/b) - a * db/dx / (b^2) - - dxdu = 0.5 * viewport_width * (xx - xw * (xc/wc)) / wc; - dxdv = 0.5 * viewport_width * (yx - yw * (xc/wc)) / wc; - dydu = 0.5 * viewport_height * (xy - xw * (yc/wc)) / wc; - dydv = 0.5 * viewport_height * (yy - yw * (yc/wc)) / wc; - - // Compute the inverse partials as the matrix inverse - det = dxdu * dydv - dxdv * dydu; - - dudx = dydv / det; - dudy = - dxdv / det; - dvdx = - dydu / det; - dvdy = dvdu / det; - - // Scale factor; maximum of the distance in texels for a change of 1 pixel - // in the X direction or 1 pixel in the Y direction - rho = MAX (sqrt (dudx * dudx + dvdx * dvdx), sqrt(dudy * dudy + dvdy * dvdy)); - - // Level of detail - lambda = log2 (rho) + LOD_BIAS; - */ - - /* dxdu * wc, etc */ - dxdu_ = 0.5 * viewport_width * (xx - xw * (xc/wc)); - dxdv_ = 0.5 * viewport_width * (yx - yw * (xc/wc)); - dydu_ = 0.5 * viewport_height * (xy - xw * (yc/wc)); - dydv_ = 0.5 * viewport_height * (yy - yw * (yc/wc)); - - /* det * wc^2 */ - det_ = dxdu_ * dydv_ - dxdv_ * dydu_; - det_sq = det_ * det_; - if (det_sq == 0.0) - return -1; - - /* (rho * det * wc)^2 */ - rho_sq = MAX (dydv_ * dydv_ + dydu_ * dydu_, dxdv_ * dxdv_ + dxdu_ * dxdu_); - lambda = 0.5 * M_LOG2E * log (rho_sq * wc * wc / det_sq) + LOD_BIAS; - -#if 0 - g_print ("%g %g %g\n", 0.5 * viewport_width * xx / ww, 0.5 * viewport_height * yy / ww, lambda); -#endif - - if (lambda <= 0.) - return 0; - else - return (int)(0.5 + lambda); -} - -static void -texture_tower_create_texture (MetaTextureTower *tower, - int level, - int width, - int height) -{ - CoglContext *ctx = - clutter_backend_get_cogl_context (clutter_get_default_backend ()); - - tower->textures[level] = - COGL_TEXTURE (cogl_texture_2d_new_with_size (ctx, width, height)); - - tower->invalid[level].x1 = 0; - tower->invalid[level].y1 = 0; - tower->invalid[level].x2 = width; - tower->invalid[level].y2 = height; -} - -static void -texture_tower_revalidate (MetaTextureTower *tower, - int level) -{ - CoglTexture *source_texture = tower->textures[level - 1]; - int source_texture_width = cogl_texture_get_width (source_texture); - int source_texture_height = cogl_texture_get_height (source_texture); - CoglTexture *dest_texture = tower->textures[level]; - int dest_texture_width = cogl_texture_get_width (dest_texture); - int dest_texture_height = cogl_texture_get_height (dest_texture); - Box *invalid = &tower->invalid[level]; - CoglFramebuffer *fb; - GError *catch_error = NULL; - CoglPipeline *pipeline; - - if (tower->fbos[level] == NULL) - tower->fbos[level] = cogl_offscreen_new_with_texture (dest_texture); - - fb = COGL_FRAMEBUFFER (tower->fbos[level]); - - if (!cogl_framebuffer_allocate (fb, &catch_error)) - { - g_error_free (catch_error); - return; - } - - cogl_framebuffer_orthographic (fb, 0, 0, dest_texture_width, dest_texture_height, -1., 1.); - - if (!tower->pipeline_template) - { - CoglContext *ctx = - clutter_backend_get_cogl_context (clutter_get_default_backend ()); - tower->pipeline_template = cogl_pipeline_new (ctx); - cogl_pipeline_set_blend (tower->pipeline_template, "RGBA = ADD (SRC_COLOR, 0)", NULL); - } - - pipeline = cogl_pipeline_copy (tower->pipeline_template); - cogl_pipeline_set_layer_texture (pipeline, 0, tower->textures[level - 1]); - - if (tower->snippet && level == 1) - cogl_pipeline_add_layer_snippet (pipeline, 0, tower->snippet); - - cogl_framebuffer_draw_textured_rectangle (fb, pipeline, - invalid->x1, invalid->y1, - invalid->x2, invalid->y2, - (2. * invalid->x1) / source_texture_width, - (2. * invalid->y1) / source_texture_height, - (2. * invalid->x2) / source_texture_width, - (2. * invalid->y2) / source_texture_height); - - cogl_object_unref (pipeline); - - tower->invalid[level].x1 = tower->invalid[level].x2 = 0; - tower->invalid[level].y1 = tower->invalid[level].y2 = 0; -} - -/** - * meta_texture_tower_get_paint_texture: - * @tower: a #MetaTextureTower - * @paint_context: a #ClutterPaintContext - * - * 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_tower_get_paint_texture (MetaTextureTower *tower, - ClutterPaintContext *paint_context) -{ - int texture_width, texture_height; - int level; - - g_return_val_if_fail (tower != NULL, NULL); - - if (tower->textures[0] == NULL) - return NULL; - - texture_width = cogl_texture_get_width (tower->textures[0]); - texture_height = cogl_texture_get_height (tower->textures[0]); - - level = get_paint_level (paint_context, texture_width, texture_height); - if (level < 0) /* singular paint matrix, scaled to nothing */ - return NULL; - level = MIN (level, tower->n_levels - 1); - - if (tower->textures[level] == NULL || - (tower->invalid[level].x2 != tower->invalid[level].x1 && - tower->invalid[level].y2 != tower->invalid[level].y1)) - { - int i; - - for (i = 1; i <= level; i++) - { - /* Use "floor" convention here to be consistent with the NPOT texture extension */ - texture_width = MAX (1, texture_width / 2); - texture_height = MAX (1, texture_height / 2); - - if (tower->textures[i] == NULL) - texture_tower_create_texture (tower, i, texture_width, texture_height); - } - - for (i = 1; i <= level; i++) - { - if (tower->invalid[level].x2 != tower->invalid[level].x1 && - tower->invalid[level].y2 != tower->invalid[level].y1) - texture_tower_revalidate (tower, i); - } - } - - return tower->textures[level]; -} diff --git a/src/compositor/meta-texture-tower.h b/src/compositor/meta-texture-tower.h deleted file mode 100644 index 5522dfbb1..000000000 --- a/src/compositor/meta-texture-tower.h +++ /dev/null @@ -1,71 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ -/* - * MetaTextureTower - * - * Mipmap emulation by creation of scaled down images - * - * Copyright (C) 2009 Red Hat, Inc. - * - * 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 . - */ - -#ifndef __META_TEXTURE_TOWER_H__ -#define __META_TEXTURE_TOWER_H__ - -#include "clutter/clutter.h" - -G_BEGIN_DECLS - -/** - * SECTION:MetaTextureTower - * @short_description: mipmap emulation by creation of scaled down images - * - * A #MetaTextureTower is used to get good looking scaled down images when - * we can't use the GL drivers mipmap support. There are two separate reasons - * - * - Some cards (including radeon cards <= r5xx) only support - * TEXTURE_RECTANGLE_ARB and not NPOT textures. Rectangular textures - * are defined not to support mipmapping. - * - Even when NPOT textures are available, the combination of NPOT - * textures, texture_from_pixmap, and mipmapping doesn't typically - * work, since the X server doesn't allocate pixmaps in the right - * layout for mipmapping. - * - * So, what we do is create the "mipmap" levels ourselves by successive - * power-of-two scaledowns, and when rendering pick the single texture - * that best matches the scale we are rendering at. (Since we aren't - * typically using perspective transforms, we'll frequently have a single - * scale for the entire texture.) - */ - -typedef struct _MetaTextureTower MetaTextureTower; - -MetaTextureTower *meta_texture_tower_new (void); -void meta_texture_tower_free (MetaTextureTower *tower); -void meta_texture_tower_set_base_texture (MetaTextureTower *tower, - CoglTexture *texture); -void meta_texture_tower_update_area (MetaTextureTower *tower, - int x, - int y, - int width, - int height); -CoglTexture *meta_texture_tower_get_paint_texture (MetaTextureTower *tower, - ClutterPaintContext *paint_context); - -void meta_texture_tower_set_snippet (MetaTextureTower *tower, - CoglSnippet *snippet); - -G_END_DECLS - -#endif /* __META_TEXTURE_TOWER_H__ */ diff --git a/src/meson.build b/src/meson.build index f752f03d9..6dda94b69 100644 --- a/src/meson.build +++ b/src/meson.build @@ -286,8 +286,8 @@ mutter_sources = [ 'compositor/meta-shaped-texture-private.h', 'compositor/meta-surface-actor.c', 'compositor/meta-surface-actor.h', - 'compositor/meta-texture-tower.c', - 'compositor/meta-texture-tower.h', + 'compositor/meta-texture-mipmap.c', + 'compositor/meta-texture-mipmap.h', 'compositor/meta-window-actor.c', 'compositor/meta-window-actor-private.h', 'compositor/meta-window-group.c',