mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-28 14:31:10 +00:00
38f64c2357
So when uploading a texture, we will automatically put it into the cache now.
2077 lines
75 KiB
C
2077 lines
75 KiB
C
#include "config.h"
|
|
|
|
#include "gskgpunodeprocessorprivate.h"
|
|
|
|
#include "gskgpuborderopprivate.h"
|
|
#include "gskgpuboxshadowopprivate.h"
|
|
#include "gskgpubluropprivate.h"
|
|
#include "gskgpuclearopprivate.h"
|
|
#include "gskgpuclipprivate.h"
|
|
#include "gskgpucolorizeopprivate.h"
|
|
#include "gskgpucoloropprivate.h"
|
|
#include "gskgpudescriptorsprivate.h"
|
|
#include "gskgpudeviceprivate.h"
|
|
#include "gskgpuframeprivate.h"
|
|
#include "gskgpuglobalsopprivate.h"
|
|
#include "gskgpuimageprivate.h"
|
|
#include "gskgpurenderpassopprivate.h"
|
|
#include "gskgpuroundedcoloropprivate.h"
|
|
#include "gskgpuscissoropprivate.h"
|
|
#include "gskgputextureopprivate.h"
|
|
#include "gskgpuuberopprivate.h"
|
|
#include "gskgpuuploadopprivate.h"
|
|
|
|
#include "gskcairoblurprivate.h"
|
|
#include "gskdebugprivate.h"
|
|
#include "gskrectprivate.h"
|
|
#include "gskrendernodeprivate.h"
|
|
#include "gskroundedrectprivate.h"
|
|
#include "gsktransformprivate.h"
|
|
|
|
/* A note about coordinate systems
|
|
*
|
|
* The rendering code keeps track of multiple coordinate systems to optimize rendering as
|
|
* much as possible and in the coordinate system it makes most sense in.
|
|
* Sometimes there are cases where GL requires a certain coordinate system, too.
|
|
*
|
|
* 1. the node coordinate system
|
|
* This is the coordinate system of the rendernode. It is basically not used outside of
|
|
* looking at the node and basically never hits the GPU (it does for paths). We immediately
|
|
* convert it to:
|
|
*
|
|
* 2. the basic coordinate system
|
|
* convert on CPU: NodeProcessor.offset
|
|
* convert on GPU: ---
|
|
* This is the coordinate system we emit vertex state in, the clip is tracked here.
|
|
* The main benefit is that most transform nodes only change the offset, so we can avoid
|
|
* updating any state in this coordinate system when that happens.
|
|
*
|
|
* 3. the scaled coordinate system
|
|
* converts on CPU: NodeProcessor.scale
|
|
* converts on GPU: GSK_GLOBAL_SCALE
|
|
* This includes the current scale of the transform. It is usually equal to the scale factor
|
|
* of the window we are rendering to (which is bad because devs without hidpi screens can
|
|
* forget this and then everyone else will see bugs). We make decisions about pixel sizes in
|
|
* this coordinate system, like picking glyphs from the glyph cache or the sizes of offscreens
|
|
* for offscreen rendering.
|
|
*
|
|
* 4. the device coordinate system
|
|
* converts on CPU: NodeProcessor.modelview
|
|
* converts on GPU: ---
|
|
* The scissor rect is tracked in this coordinate system. It represents the actual device pixels.
|
|
* A bunch of optimizations (like glScissor() and glClear()) can be done here, so in the case
|
|
* that modelview == NULL and we end up with integer coordinates (because pixels), we try to go
|
|
* here.
|
|
* This coordinate system does not exist on shaders as they rarely reason about pixels, and if
|
|
* they need to, they can ask the fragment shader via gl_FragCoord.
|
|
*
|
|
* 5. the GL coordinate system
|
|
* converts on CPU: NodeProcessor.projection
|
|
* converts on GPU: GSK_GLOBAL_MVP (from scaled coordinate system)
|
|
* This coordinate system is what GL (or Vulkan) expect coordinates to appear in, and is usually
|
|
* (-1, -1) => (1, 1), but may be flipped etc depending on the render target. The CPU essentially
|
|
* never uses it, other than to allow the vertex shaders to emit its vertices.
|
|
*/
|
|
|
|
typedef struct _GskGpuNodeProcessor GskGpuNodeProcessor;
|
|
typedef struct _GskGpuPatternWriter GskGpuPatternWriter;
|
|
|
|
typedef enum {
|
|
GSK_GPU_GLOBAL_MATRIX = (1 << 0),
|
|
GSK_GPU_GLOBAL_SCALE = (1 << 1),
|
|
GSK_GPU_GLOBAL_CLIP = (1 << 2),
|
|
GSK_GPU_GLOBAL_SCISSOR = (1 << 3),
|
|
} GskGpuGlobals;
|
|
|
|
struct _GskGpuNodeProcessor
|
|
{
|
|
GskGpuFrame *frame;
|
|
GskGpuDescriptors *desc;
|
|
cairo_rectangle_int_t scissor;
|
|
graphene_point_t offset;
|
|
graphene_matrix_t projection;
|
|
graphene_vec2_t scale;
|
|
GskTransform *modelview;
|
|
GskGpuClip clip;
|
|
|
|
GskGpuGlobals pending_globals;
|
|
};
|
|
|
|
struct _GskGpuPatternWriter
|
|
{
|
|
GskGpuFrame *frame;
|
|
GskGpuDescriptors *desc;
|
|
|
|
graphene_rect_t bounds;
|
|
graphene_point_t offset;
|
|
graphene_vec2_t scale;
|
|
guint stack;
|
|
|
|
GskGpuBufferWriter writer;
|
|
};
|
|
|
|
static void gsk_gpu_node_processor_add_node (GskGpuNodeProcessor *self,
|
|
GskRenderNode *node);
|
|
static gboolean gsk_gpu_node_processor_create_node_pattern (GskGpuPatternWriter *self,
|
|
GskRenderNode *node);
|
|
|
|
static void
|
|
gsk_gpu_node_processor_finish (GskGpuNodeProcessor *self)
|
|
{
|
|
g_clear_pointer (&self->modelview, gsk_transform_unref);
|
|
g_clear_object (&self->desc);
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_node_processor_init (GskGpuNodeProcessor *self,
|
|
GskGpuFrame *frame,
|
|
GskGpuDescriptors *desc,
|
|
GskGpuImage *target,
|
|
const cairo_rectangle_int_t *clip,
|
|
const graphene_rect_t *viewport)
|
|
{
|
|
self->frame = frame;
|
|
if (desc)
|
|
self->desc = g_object_ref (desc);
|
|
else
|
|
self->desc = NULL;
|
|
|
|
self->scissor = *clip;
|
|
gsk_gpu_clip_init_empty (&self->clip, &GRAPHENE_RECT_INIT (0, 0, viewport->size.width, viewport->size.height));
|
|
|
|
self->modelview = NULL;
|
|
gsk_gpu_image_get_projection_matrix (target, &self->projection);
|
|
graphene_vec2_init (&self->scale, gsk_gpu_image_get_width (target) / viewport->size.width,
|
|
gsk_gpu_image_get_height (target) / viewport->size.height);
|
|
self->offset = GRAPHENE_POINT_INIT (-viewport->origin.x,
|
|
-viewport->origin.y);
|
|
self->pending_globals = GSK_GPU_GLOBAL_MATRIX | GSK_GPU_GLOBAL_SCALE | GSK_GPU_GLOBAL_CLIP | GSK_GPU_GLOBAL_SCISSOR;
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_node_processor_emit_globals_op (GskGpuNodeProcessor *self)
|
|
{
|
|
graphene_matrix_t mvp;
|
|
|
|
if (self->modelview)
|
|
{
|
|
gsk_transform_to_matrix (self->modelview, &mvp);
|
|
graphene_matrix_multiply (&mvp, &self->projection, &mvp);
|
|
}
|
|
else
|
|
graphene_matrix_init_from_matrix (&mvp, &self->projection);
|
|
|
|
gsk_gpu_globals_op (self->frame,
|
|
&self->scale,
|
|
&mvp,
|
|
&self->clip.rect);
|
|
|
|
self->pending_globals &= ~(GSK_GPU_GLOBAL_MATRIX | GSK_GPU_GLOBAL_SCALE | GSK_GPU_GLOBAL_CLIP);
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_node_processor_emit_scissor_op (GskGpuNodeProcessor *self)
|
|
{
|
|
gsk_gpu_scissor_op (self->frame,
|
|
&self->scissor);
|
|
self->pending_globals &= ~GSK_GPU_GLOBAL_SCISSOR;
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_node_processor_sync_globals (GskGpuNodeProcessor *self,
|
|
GskGpuGlobals ignored)
|
|
{
|
|
GskGpuGlobals required;
|
|
|
|
required = self->pending_globals & ~ignored;
|
|
|
|
if (required & (GSK_GPU_GLOBAL_MATRIX | GSK_GPU_GLOBAL_SCALE | GSK_GPU_GLOBAL_CLIP))
|
|
gsk_gpu_node_processor_emit_globals_op (self);
|
|
if (required & GSK_GPU_GLOBAL_SCISSOR)
|
|
gsk_gpu_node_processor_emit_scissor_op (self);
|
|
}
|
|
|
|
static guint32
|
|
gsk_gpu_node_processor_add_image (GskGpuNodeProcessor *self,
|
|
GskGpuImage *image,
|
|
GskGpuSampler sampler)
|
|
{
|
|
guint32 descriptor;
|
|
|
|
if (self->desc != NULL)
|
|
{
|
|
if (gsk_gpu_descriptors_add_image (self->desc, image, sampler, &descriptor))
|
|
return descriptor;
|
|
|
|
g_object_unref (self->desc);
|
|
}
|
|
|
|
self->desc = gsk_gpu_frame_create_descriptors (self->frame);
|
|
|
|
if (!gsk_gpu_descriptors_add_image (self->desc, image, sampler, &descriptor))
|
|
{
|
|
g_assert_not_reached ();
|
|
return 0;
|
|
}
|
|
|
|
return descriptor;
|
|
}
|
|
|
|
static void
|
|
rect_round_to_pixels (const graphene_rect_t *src,
|
|
const graphene_vec2_t *pixel_scale,
|
|
graphene_rect_t *dest)
|
|
{
|
|
float x, y, xscale, yscale, inv_xscale, inv_yscale;
|
|
|
|
xscale = graphene_vec2_get_x (pixel_scale);
|
|
yscale = graphene_vec2_get_y (pixel_scale);
|
|
inv_xscale = 1.0f / xscale;
|
|
inv_yscale = 1.0f / yscale;
|
|
|
|
x = floorf (src->origin.x * xscale);
|
|
y = floorf (src->origin.y * yscale);
|
|
*dest = GRAPHENE_RECT_INIT (
|
|
x * inv_xscale,
|
|
y * inv_yscale,
|
|
(ceil ((src->origin.x + src->size.width) * xscale) - x) * inv_xscale,
|
|
(ceil ((src->origin.y + src->size.height) * yscale) - y) * inv_yscale);
|
|
}
|
|
|
|
void
|
|
gsk_gpu_node_processor_process (GskGpuFrame *frame,
|
|
GskGpuImage *target,
|
|
const cairo_rectangle_int_t *clip,
|
|
GskRenderNode *node,
|
|
const graphene_rect_t *viewport)
|
|
{
|
|
GskGpuNodeProcessor self;
|
|
|
|
gsk_gpu_node_processor_init (&self,
|
|
frame,
|
|
NULL,
|
|
target,
|
|
clip,
|
|
viewport);
|
|
|
|
gsk_gpu_node_processor_add_node (&self, node);
|
|
|
|
gsk_gpu_node_processor_finish (&self);
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_pattern_writer_init (GskGpuPatternWriter *self,
|
|
GskGpuFrame *frame,
|
|
const graphene_vec2_t *scale,
|
|
const graphene_point_t *offset,
|
|
const graphene_rect_t *bounds)
|
|
{
|
|
self->frame = frame;
|
|
self->desc = NULL;
|
|
self->bounds = GRAPHENE_RECT_INIT (bounds->origin.x + offset->x,
|
|
bounds->origin.y + offset->y,
|
|
bounds->size.width,
|
|
bounds->size.height);
|
|
self->offset = *offset;
|
|
self->scale = *scale;
|
|
self->stack = 0;
|
|
|
|
gsk_gpu_frame_write_buffer_memory (frame, &self->writer);
|
|
}
|
|
|
|
static gboolean
|
|
gsk_gpu_pattern_writer_push_stack (GskGpuPatternWriter *self)
|
|
{
|
|
if (self->stack >= GSK_GPU_PATTERN_STACK_SIZE)
|
|
return FALSE;
|
|
|
|
self->stack++;
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_pattern_writer_pop_stack (GskGpuPatternWriter *self)
|
|
{
|
|
g_assert (self->stack > 0);
|
|
self->stack--;
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_pattern_writer_finish (GskGpuPatternWriter *self)
|
|
{
|
|
g_assert (self->stack == 0);
|
|
g_clear_object (&self->desc);
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_pattern_writer_abort (GskGpuPatternWriter *self)
|
|
{
|
|
gsk_gpu_buffer_writer_abort (&self->writer);
|
|
|
|
gsk_gpu_pattern_writer_finish (self);
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_pattern_writer_commit_op (GskGpuPatternWriter *self,
|
|
GskGpuShaderClip clip)
|
|
{
|
|
guint32 pattern_id;
|
|
|
|
pattern_id = gsk_gpu_buffer_writer_commit (&self->writer) / sizeof (float);
|
|
|
|
gsk_gpu_uber_op (self->frame,
|
|
clip,
|
|
&self->bounds,
|
|
&self->offset,
|
|
self->desc,
|
|
pattern_id);
|
|
|
|
gsk_gpu_pattern_writer_finish (self);
|
|
}
|
|
|
|
static gboolean
|
|
gsk_gpu_pattern_writer_add_image (GskGpuPatternWriter *self,
|
|
GskGpuImage *image,
|
|
GskGpuSampler sampler,
|
|
guint32 *out_descriptor)
|
|
{
|
|
if (self->desc == NULL)
|
|
self->desc = gsk_gpu_frame_create_descriptors (self->frame);
|
|
|
|
return gsk_gpu_descriptors_add_image (self->desc, image, sampler, out_descriptor);
|
|
}
|
|
|
|
static void
|
|
extract_scale_from_transform (GskTransform *transform,
|
|
float *out_scale_x,
|
|
float *out_scale_y)
|
|
{
|
|
switch (gsk_transform_get_category (transform))
|
|
{
|
|
default:
|
|
g_assert_not_reached ();
|
|
G_GNUC_FALLTHROUGH;
|
|
case GSK_TRANSFORM_CATEGORY_IDENTITY:
|
|
case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE:
|
|
*out_scale_x = 1.0f;
|
|
*out_scale_y = 1.0f;
|
|
return;
|
|
|
|
case GSK_TRANSFORM_CATEGORY_2D_AFFINE:
|
|
{
|
|
float scale_x, scale_y, dx, dy;
|
|
gsk_transform_to_affine (transform, &scale_x, &scale_y, &dx, &dy);
|
|
*out_scale_x = fabs (scale_x);
|
|
*out_scale_y = fabs (scale_y);
|
|
}
|
|
return;
|
|
|
|
case GSK_TRANSFORM_CATEGORY_2D:
|
|
{
|
|
float skew_x, skew_y, scale_x, scale_y, angle, dx, dy;
|
|
gsk_transform_to_2d_components (transform,
|
|
&skew_x, &skew_y,
|
|
&scale_x, &scale_y,
|
|
&angle,
|
|
&dx, &dy);
|
|
*out_scale_x = fabs (scale_x);
|
|
*out_scale_y = fabs (scale_y);
|
|
}
|
|
return;
|
|
|
|
case GSK_TRANSFORM_CATEGORY_UNKNOWN:
|
|
case GSK_TRANSFORM_CATEGORY_ANY:
|
|
case GSK_TRANSFORM_CATEGORY_3D:
|
|
{
|
|
graphene_quaternion_t rotation;
|
|
graphene_matrix_t matrix;
|
|
graphene_vec4_t perspective;
|
|
graphene_vec3_t translation;
|
|
graphene_vec3_t matrix_scale;
|
|
graphene_vec3_t shear;
|
|
|
|
gsk_transform_to_matrix (transform, &matrix);
|
|
graphene_matrix_decompose (&matrix,
|
|
&translation,
|
|
&matrix_scale,
|
|
&rotation,
|
|
&shear,
|
|
&perspective);
|
|
|
|
*out_scale_x = fabs (graphene_vec3_get_x (&matrix_scale));
|
|
*out_scale_y = fabs (graphene_vec3_get_y (&matrix_scale));
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gsk_gpu_node_processor_rect_is_integer (GskGpuNodeProcessor *self,
|
|
const graphene_rect_t *rect,
|
|
cairo_rectangle_int_t *int_rect)
|
|
{
|
|
graphene_rect_t transformed_rect;
|
|
float scale_x = graphene_vec2_get_x (&self->scale);
|
|
float scale_y = graphene_vec2_get_y (&self->scale);
|
|
|
|
switch (gsk_transform_get_category (self->modelview))
|
|
{
|
|
case GSK_TRANSFORM_CATEGORY_UNKNOWN:
|
|
case GSK_TRANSFORM_CATEGORY_ANY:
|
|
case GSK_TRANSFORM_CATEGORY_3D:
|
|
case GSK_TRANSFORM_CATEGORY_2D:
|
|
/* FIXME: We could try to handle 90° rotation here,
|
|
* but I don't think there's a use case */
|
|
return FALSE;
|
|
|
|
case GSK_TRANSFORM_CATEGORY_2D_AFFINE:
|
|
case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE:
|
|
gsk_transform_transform_bounds (self->modelview, rect, &transformed_rect);
|
|
rect = &transformed_rect;
|
|
break;
|
|
|
|
case GSK_TRANSFORM_CATEGORY_IDENTITY:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
int_rect->x = rect->origin.x * scale_x;
|
|
int_rect->y = rect->origin.y * scale_y;
|
|
int_rect->width = rect->size.width * scale_x;
|
|
int_rect->height = rect->size.height * scale_y;
|
|
|
|
return int_rect->x == rect->origin.x * scale_x
|
|
&& int_rect->y == rect->origin.y * scale_y
|
|
&& int_rect->width == rect->size.width * scale_x
|
|
&& int_rect->height == rect->size.height * scale_y;
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_node_processor_get_clip_bounds (GskGpuNodeProcessor *self,
|
|
graphene_rect_t *out_bounds)
|
|
{
|
|
graphene_rect_offset_r (&self->clip.rect.bounds,
|
|
- self->offset.x,
|
|
- self->offset.y,
|
|
out_bounds);
|
|
|
|
/* FIXME: We could try the scissor rect here.
|
|
* But how often is that smaller than the clip bounds?
|
|
*/
|
|
}
|
|
|
|
static gboolean G_GNUC_WARN_UNUSED_RESULT
|
|
gsk_gpu_node_processor_clip_node_bounds (GskGpuNodeProcessor *self,
|
|
GskRenderNode *node,
|
|
graphene_rect_t *out_bounds)
|
|
{
|
|
graphene_rect_t tmp;
|
|
|
|
gsk_gpu_node_processor_get_clip_bounds (self, &tmp);
|
|
|
|
if (!gsk_rect_intersection (&tmp, &node->bounds, out_bounds))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* gsk_gpu_get_node_as_image:
|
|
* @frame: frame to render in
|
|
* @clip_bounds: region of node that must be included in image
|
|
* @scale: scale factor to use for the image
|
|
* @node: the node to render
|
|
* @out_bounds: the actual bounds of the result
|
|
*
|
|
* Get the part of the node indicated by the clip bounds as an image.
|
|
*
|
|
* It is perfectly valid for this function to return an image covering
|
|
* a larger or smaller rectangle than the given clip bounds.
|
|
* It can be smaller if the node is actually smaller than the clip
|
|
* bounds and it's not necessary to create such a large offscreen, and
|
|
* it can be larger if only part of a node is drawn but a cached image
|
|
* for the full node (usually a texture node) already exists.
|
|
*
|
|
* The rectangle that is actually covered by the image is returned in
|
|
* out_bounds.
|
|
*
|
|
* Returns: the image or %NULL if there was nothing to render
|
|
**/
|
|
static GskGpuImage *
|
|
gsk_gpu_get_node_as_image (GskGpuFrame *frame,
|
|
const graphene_rect_t *clip_bounds,
|
|
const graphene_vec2_t *scale,
|
|
GskRenderNode *node,
|
|
graphene_rect_t *out_bounds)
|
|
{
|
|
graphene_rect_t clipped;
|
|
GskGpuImage *result;
|
|
|
|
switch ((guint) gsk_render_node_get_node_type (node))
|
|
{
|
|
case GSK_TEXTURE_NODE:
|
|
{
|
|
GdkTexture *texture = gsk_texture_node_get_texture (node);
|
|
GskGpuDevice *device = gsk_gpu_frame_get_device (frame);
|
|
gint64 timestamp = gsk_gpu_frame_get_timestamp (frame);
|
|
result = gsk_gpu_device_lookup_texture_image (device, texture, timestamp);
|
|
if (result == NULL)
|
|
result = gsk_gpu_frame_upload_texture (frame, texture);
|
|
|
|
if (result)
|
|
{
|
|
*out_bounds = node->bounds;
|
|
return result;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case GSK_CAIRO_NODE:
|
|
gsk_rect_intersection (clip_bounds, &node->bounds, &clipped);
|
|
if (gsk_rect_is_empty (&clipped))
|
|
return NULL;
|
|
rect_round_to_pixels (&clipped, scale, &clipped);
|
|
|
|
result = gsk_gpu_upload_cairo_op (frame,
|
|
node,
|
|
scale,
|
|
&clipped);
|
|
|
|
g_object_ref (result);
|
|
|
|
*out_bounds = clipped;
|
|
return result;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
gsk_rect_intersection (clip_bounds, &node->bounds, &clipped);
|
|
if (gsk_rect_is_empty (&clipped))
|
|
return NULL;
|
|
rect_round_to_pixels (&clipped, scale, &clipped);
|
|
|
|
GSK_DEBUG (FALLBACK, "Offscreening node '%s'", g_type_name_from_instance ((GTypeInstance *) node));
|
|
result = gsk_gpu_render_pass_op_offscreen (frame,
|
|
scale,
|
|
&clipped,
|
|
node);
|
|
|
|
*out_bounds = clipped;
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_node_processor_blur_op (GskGpuNodeProcessor *self,
|
|
const graphene_rect_t *rect,
|
|
const graphene_point_t *shadow_offset,
|
|
float blur_radius,
|
|
const GdkRGBA *blur_color_or_null,
|
|
GskGpuDescriptors *source_desc,
|
|
guint32 source_descriptor,
|
|
GdkMemoryDepth source_depth,
|
|
const graphene_rect_t *source_rect)
|
|
{
|
|
GskGpuNodeProcessor other;
|
|
GskGpuImage *intermediate;
|
|
guint32 intermediate_descriptor;
|
|
graphene_vec2_t direction;
|
|
graphene_rect_t clip_rect, intermediate_rect;
|
|
graphene_point_t real_offset;
|
|
int width, height;
|
|
float clip_radius;
|
|
|
|
clip_radius = gsk_cairo_blur_compute_pixels (blur_radius / 2.0);
|
|
|
|
/* FIXME: Handle clip radius growing the clip too much */
|
|
gsk_gpu_node_processor_get_clip_bounds (self, &clip_rect);
|
|
graphene_rect_inset (&clip_rect, 0.f, -clip_radius);
|
|
if (!gsk_rect_intersection (rect, &clip_rect, &intermediate_rect))
|
|
return;
|
|
|
|
width = ceil (graphene_vec2_get_x (&self->scale) * intermediate_rect.size.width);
|
|
height = ceil (graphene_vec2_get_y (&self->scale) * intermediate_rect.size.height);
|
|
|
|
intermediate = gsk_gpu_device_create_offscreen_image (gsk_gpu_frame_get_device (self->frame),
|
|
source_depth,
|
|
width, height);
|
|
|
|
gsk_gpu_node_processor_init (&other,
|
|
self->frame,
|
|
source_desc,
|
|
intermediate,
|
|
&(cairo_rectangle_int_t) { 0, 0, width, height },
|
|
&intermediate_rect);
|
|
|
|
gsk_gpu_render_pass_begin_op (other.frame,
|
|
intermediate,
|
|
&(cairo_rectangle_int_t) { 0, 0, width, height },
|
|
GSK_RENDER_PASS_OFFSCREEN);
|
|
|
|
gsk_gpu_node_processor_sync_globals (&other, 0);
|
|
|
|
graphene_vec2_init (&direction, blur_radius, 0.0f);
|
|
gsk_gpu_blur_op (other.frame,
|
|
gsk_gpu_clip_get_shader_clip (&other.clip, &other.offset, &intermediate_rect),
|
|
source_desc,
|
|
source_descriptor,
|
|
&intermediate_rect,
|
|
&other.offset,
|
|
source_rect,
|
|
&direction,
|
|
&(GdkRGBA) { 0, 0, 0, 0 });
|
|
|
|
gsk_gpu_render_pass_end_op (other.frame,
|
|
intermediate,
|
|
GSK_RENDER_PASS_OFFSCREEN);
|
|
|
|
gsk_gpu_node_processor_finish (&other);
|
|
|
|
real_offset = GRAPHENE_POINT_INIT (self->offset.x + shadow_offset->x,
|
|
self->offset.y + shadow_offset->y);
|
|
graphene_vec2_init (&direction, 0.0f, blur_radius);
|
|
intermediate_descriptor = gsk_gpu_node_processor_add_image (self, intermediate, GSK_GPU_SAMPLER_TRANSPARENT);
|
|
gsk_gpu_blur_op (self->frame,
|
|
gsk_gpu_clip_get_shader_clip (&self->clip, &real_offset, rect),
|
|
self->desc,
|
|
intermediate_descriptor,
|
|
rect,
|
|
&real_offset,
|
|
&intermediate_rect,
|
|
&direction,
|
|
blur_color_or_null ? blur_color_or_null : &(GdkRGBA) { 0, 0, 0, 0 });
|
|
|
|
g_object_unref (intermediate);
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_node_processor_add_fallback_node (GskGpuNodeProcessor *self,
|
|
GskRenderNode *node)
|
|
{
|
|
GskGpuImage *image;
|
|
graphene_rect_t clipped_bounds;
|
|
guint32 descriptor;
|
|
|
|
if (!gsk_gpu_node_processor_clip_node_bounds (self, node, &clipped_bounds))
|
|
return;
|
|
|
|
gsk_gpu_node_processor_sync_globals (self, 0);
|
|
|
|
image = gsk_gpu_upload_cairo_op (self->frame,
|
|
node,
|
|
&self->scale,
|
|
&clipped_bounds);
|
|
descriptor = gsk_gpu_node_processor_add_image (self, image, GSK_GPU_SAMPLER_DEFAULT);
|
|
|
|
gsk_gpu_texture_op (self->frame,
|
|
gsk_gpu_clip_get_shader_clip (&self->clip, &self->offset, &clipped_bounds),
|
|
self->desc,
|
|
descriptor,
|
|
&node->bounds,
|
|
&self->offset,
|
|
&clipped_bounds);
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_node_processor_add_node_as_pattern (GskGpuNodeProcessor *self,
|
|
GskRenderNode *node)
|
|
{
|
|
GskGpuPatternWriter writer;
|
|
graphene_rect_t clipped;
|
|
|
|
g_assert (self->pending_globals == 0);
|
|
|
|
if (!gsk_gpu_node_processor_clip_node_bounds (self, node, &clipped))
|
|
return;
|
|
|
|
gsk_gpu_pattern_writer_init (&writer,
|
|
self->frame,
|
|
&self->scale,
|
|
&self->offset,
|
|
&clipped);
|
|
|
|
if (!gsk_gpu_node_processor_create_node_pattern (&writer, node))
|
|
{
|
|
gsk_gpu_pattern_writer_abort (&writer);
|
|
GSK_DEBUG (FALLBACK, "Pattern shader for node %s failed", g_type_name_from_instance ((GTypeInstance *) node));
|
|
gsk_gpu_node_processor_add_fallback_node (self, node);
|
|
return;
|
|
}
|
|
|
|
gsk_gpu_buffer_writer_append_uint (&writer.writer, GSK_GPU_PATTERN_DONE);
|
|
|
|
gsk_gpu_pattern_writer_commit_op (&writer,
|
|
gsk_gpu_clip_get_shader_clip (&self->clip, &self->offset, &node->bounds));
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_node_processor_add_clip_node (GskGpuNodeProcessor *self,
|
|
GskRenderNode *node)
|
|
{
|
|
GskRenderNode *child;
|
|
GskGpuClip old_clip;
|
|
graphene_rect_t clip;
|
|
cairo_rectangle_int_t scissor;
|
|
|
|
child = gsk_clip_node_get_child (node);
|
|
graphene_rect_offset_r (gsk_clip_node_get_clip (node),
|
|
self->offset.x, self->offset.y,
|
|
&clip);
|
|
|
|
gsk_gpu_clip_init_copy (&old_clip, &self->clip);
|
|
|
|
/* Check if we can use scissoring for the clip */
|
|
if (gsk_gpu_node_processor_rect_is_integer (self, &clip, &scissor))
|
|
{
|
|
cairo_rectangle_int_t old_scissor;
|
|
|
|
if (!gdk_rectangle_intersect (&scissor, &self->scissor, &scissor))
|
|
return;
|
|
|
|
old_scissor = self->scissor;
|
|
|
|
if (gsk_gpu_clip_intersect_rect (&self->clip, &old_clip, &clip))
|
|
{
|
|
if (self->clip.type == GSK_GPU_CLIP_ALL_CLIPPED)
|
|
{
|
|
gsk_gpu_clip_init_copy (&self->clip, &old_clip);
|
|
return;
|
|
}
|
|
else if (self->clip.type == GSK_GPU_CLIP_RECT)
|
|
{
|
|
self->clip.type = GSK_GPU_CLIP_NONE;
|
|
}
|
|
|
|
self->scissor = scissor;
|
|
self->pending_globals |= GSK_GPU_GLOBAL_SCISSOR | GSK_GPU_GLOBAL_CLIP;
|
|
|
|
gsk_gpu_node_processor_add_node (self, child);
|
|
|
|
gsk_gpu_clip_init_copy (&self->clip, &old_clip);
|
|
self->scissor = old_scissor;
|
|
self->pending_globals |= GSK_GPU_GLOBAL_SCISSOR | GSK_GPU_GLOBAL_CLIP;
|
|
}
|
|
else
|
|
{
|
|
self->scissor = scissor;
|
|
self->pending_globals |= GSK_GPU_GLOBAL_SCISSOR;
|
|
|
|
gsk_gpu_clip_init_copy (&self->clip, &old_clip);
|
|
|
|
gsk_gpu_node_processor_add_node (self, child);
|
|
|
|
self->scissor = old_scissor;
|
|
self->pending_globals |= GSK_GPU_GLOBAL_SCISSOR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!gsk_gpu_clip_intersect_rect (&self->clip, &old_clip, &clip))
|
|
{
|
|
GSK_DEBUG (FALLBACK, "Failed to find intersection between clip of type %u and rectangle", self->clip.type);
|
|
gsk_gpu_clip_init_copy (&self->clip, &old_clip);
|
|
gsk_gpu_node_processor_add_fallback_node (self, node);
|
|
return;
|
|
}
|
|
|
|
if (self->clip.type == GSK_GPU_CLIP_ALL_CLIPPED)
|
|
{
|
|
gsk_gpu_clip_init_copy (&self->clip, &old_clip);
|
|
return;
|
|
}
|
|
|
|
self->pending_globals |= GSK_GPU_GLOBAL_CLIP;
|
|
|
|
gsk_gpu_node_processor_add_node (self, child);
|
|
|
|
gsk_gpu_clip_init_copy (&self->clip, &old_clip);
|
|
self->pending_globals |= GSK_GPU_GLOBAL_CLIP;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gsk_gpu_node_processor_create_clip_pattern (GskGpuPatternWriter *self,
|
|
GskRenderNode *node)
|
|
{
|
|
if (!gsk_gpu_node_processor_create_node_pattern (self, gsk_opacity_node_get_child (node)))
|
|
return FALSE;
|
|
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_CLIP);
|
|
gsk_gpu_buffer_writer_append_rect (&self->writer,
|
|
gsk_clip_node_get_clip (node),
|
|
&self->offset);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_node_processor_add_rounded_clip_node (GskGpuNodeProcessor *self,
|
|
GskRenderNode *node)
|
|
{
|
|
GskGpuClip old_clip;
|
|
GskRoundedRect clip;
|
|
const GskRoundedRect *original_clip;
|
|
GskRenderNode *child;
|
|
|
|
child = gsk_rounded_clip_node_get_child (node);
|
|
original_clip = gsk_rounded_clip_node_get_clip (node);
|
|
|
|
/* Common case for entries etc: rounded solid color background.
|
|
* And we have a shader for that */
|
|
if (gsk_render_node_get_node_type (child) == GSK_COLOR_NODE &&
|
|
gsk_rect_contains_rect (&child->bounds, &original_clip->bounds))
|
|
{
|
|
gsk_gpu_node_processor_sync_globals (self, 0);
|
|
gsk_gpu_rounded_color_op (self->frame,
|
|
gsk_gpu_clip_get_shader_clip (&self->clip, &self->offset, &original_clip->bounds),
|
|
original_clip,
|
|
&self->offset,
|
|
gsk_color_node_get_color (child));
|
|
return;
|
|
}
|
|
|
|
gsk_gpu_clip_init_copy (&old_clip, &self->clip);
|
|
|
|
clip = *original_clip;
|
|
gsk_rounded_rect_offset (&clip, self->offset.x, self->offset.y);
|
|
|
|
if (!gsk_gpu_clip_intersect_rounded_rect (&self->clip, &old_clip, &clip))
|
|
{
|
|
GSK_DEBUG (FALLBACK, "Failed to find intersection between clip of type %u and rounded rectangle", self->clip.type);
|
|
gsk_gpu_clip_init_copy (&self->clip, &old_clip);
|
|
gsk_gpu_node_processor_add_fallback_node (self, node);
|
|
return;
|
|
}
|
|
|
|
if (self->clip.type == GSK_GPU_CLIP_ALL_CLIPPED)
|
|
{
|
|
gsk_gpu_clip_init_copy (&self->clip, &old_clip);
|
|
return;
|
|
}
|
|
|
|
self->pending_globals |= GSK_GPU_GLOBAL_CLIP;
|
|
|
|
gsk_gpu_node_processor_add_node (self, gsk_rounded_clip_node_get_child (node));
|
|
|
|
gsk_gpu_clip_init_copy (&self->clip, &old_clip);
|
|
self->pending_globals |= GSK_GPU_GLOBAL_CLIP;
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_node_processor_add_transform_node (GskGpuNodeProcessor *self,
|
|
GskRenderNode *node)
|
|
{
|
|
GskRenderNode *child;
|
|
GskTransform *transform;
|
|
graphene_point_t old_offset;
|
|
graphene_vec2_t old_scale;
|
|
GskTransform *old_modelview;
|
|
GskGpuClip old_clip;
|
|
|
|
child = gsk_transform_node_get_child (node);
|
|
transform = gsk_transform_node_get_transform (node);
|
|
|
|
switch (gsk_transform_get_category (transform))
|
|
{
|
|
case GSK_TRANSFORM_CATEGORY_IDENTITY:
|
|
case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE:
|
|
{
|
|
float dx, dy;
|
|
gsk_transform_to_translate (transform, &dx, &dy);
|
|
old_offset = self->offset;
|
|
self->offset.x += dx;
|
|
self->offset.y += dy;
|
|
gsk_gpu_node_processor_add_node (self, child);
|
|
self->offset = old_offset;
|
|
}
|
|
return;
|
|
|
|
case GSK_TRANSFORM_CATEGORY_2D_AFFINE:
|
|
{
|
|
float dx, dy, scale_x, scale_y;
|
|
|
|
gsk_gpu_clip_init_copy (&old_clip, &self->clip);
|
|
old_offset = self->offset;
|
|
old_scale = self->scale;
|
|
old_modelview = gsk_transform_ref (self->modelview);
|
|
|
|
gsk_transform_to_affine (transform, &scale_x, &scale_y, &dx, &dy);
|
|
gsk_gpu_clip_scale (&self->clip, &old_clip, scale_x, scale_y);
|
|
self->offset.x = (self->offset.x + dx) / scale_x;
|
|
self->offset.y = (self->offset.y + dy) / scale_y;
|
|
graphene_vec2_init (&self->scale, fabs (scale_x), fabs (scale_y));
|
|
graphene_vec2_multiply (&self->scale, &old_scale, &self->scale);
|
|
self->modelview = gsk_transform_scale (self->modelview,
|
|
scale_x / fabs (scale_x),
|
|
scale_y / fabs (scale_y));
|
|
}
|
|
break;
|
|
|
|
case GSK_TRANSFORM_CATEGORY_2D:
|
|
case GSK_TRANSFORM_CATEGORY_UNKNOWN:
|
|
case GSK_TRANSFORM_CATEGORY_ANY:
|
|
case GSK_TRANSFORM_CATEGORY_3D:
|
|
{
|
|
GskTransform *clip_transform;
|
|
float scale_x, scale_y, old_pixels, new_pixels;
|
|
|
|
clip_transform = gsk_transform_transform (gsk_transform_translate (NULL, &self->offset), transform);
|
|
gsk_gpu_clip_init_copy (&old_clip, &self->clip);
|
|
|
|
if (gsk_gpu_clip_contains_rect (&self->clip, &self->offset, &node->bounds))
|
|
{
|
|
gsk_gpu_clip_init_empty (&self->clip, &child->bounds);
|
|
}
|
|
else if (!gsk_gpu_clip_transform (&self->clip, &old_clip, clip_transform, &child->bounds))
|
|
{
|
|
gsk_transform_unref (clip_transform);
|
|
GSK_DEBUG (FALLBACK, "Transform nodes can't deal with clip type %u", self->clip.type);
|
|
gsk_gpu_node_processor_add_fallback_node (self, node);
|
|
return;
|
|
}
|
|
|
|
old_offset = self->offset;
|
|
old_scale = self->scale;
|
|
old_modelview = gsk_transform_ref (self->modelview);
|
|
|
|
self->modelview = gsk_transform_scale (self->modelview,
|
|
graphene_vec2_get_x (&self->scale),
|
|
graphene_vec2_get_y (&self->scale));
|
|
self->modelview = gsk_transform_transform (self->modelview, clip_transform);
|
|
gsk_transform_unref (clip_transform);
|
|
|
|
extract_scale_from_transform (self->modelview, &scale_x, &scale_y);
|
|
|
|
old_pixels = graphene_vec2_get_x (&old_scale) * graphene_vec2_get_y (&old_scale) *
|
|
old_clip.rect.bounds.size.width * old_clip.rect.bounds.size.height;
|
|
new_pixels = scale_x * scale_y * self->clip.rect.bounds.size.width * self->clip.rect.bounds.size.height;
|
|
if (new_pixels > 2 * old_pixels)
|
|
{
|
|
float forced_downscale = 2 * old_pixels / new_pixels;
|
|
scale_x *= forced_downscale;
|
|
scale_y *= forced_downscale;
|
|
}
|
|
|
|
self->modelview = gsk_transform_scale (self->modelview, 1 / scale_x, 1 / scale_y);
|
|
graphene_vec2_init (&self->scale, scale_x, scale_y);
|
|
self->offset = *graphene_point_zero ();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
|
|
self->pending_globals |= GSK_GPU_GLOBAL_MATRIX | GSK_GPU_GLOBAL_SCALE | GSK_GPU_GLOBAL_CLIP;
|
|
|
|
gsk_gpu_node_processor_add_node (self, child);
|
|
|
|
self->offset = old_offset;
|
|
self->scale = old_scale;
|
|
gsk_transform_unref (self->modelview);
|
|
self->modelview = old_modelview;
|
|
gsk_gpu_clip_init_copy (&self->clip, &old_clip);
|
|
self->pending_globals |= GSK_GPU_GLOBAL_MATRIX | GSK_GPU_GLOBAL_SCALE | GSK_GPU_GLOBAL_CLIP;
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_node_processor_add_color_node (GskGpuNodeProcessor *self,
|
|
GskRenderNode *node)
|
|
{
|
|
cairo_rectangle_int_t int_clipped;
|
|
graphene_rect_t rect, clipped;
|
|
const GdkRGBA *color;
|
|
|
|
color = gsk_color_node_get_color (node);
|
|
graphene_rect_offset_r (&node->bounds,
|
|
self->offset.x, self->offset.y,
|
|
&rect);
|
|
gsk_rect_intersection (&self->clip.rect.bounds, &rect, &clipped);
|
|
|
|
if (gsk_gpu_frame_should_optimize (self->frame, GSK_GPU_OPTIMIZE_CLEAR) &&
|
|
gdk_rgba_is_opaque (color) &&
|
|
node->bounds.size.width * node->bounds.size.height > 100 * 100 && /* not worth the effort for small images */
|
|
gsk_gpu_node_processor_rect_is_integer (self, &clipped, &int_clipped))
|
|
{
|
|
/* now handle all the clip */
|
|
if (!gdk_rectangle_intersect (&int_clipped, &self->scissor, &int_clipped))
|
|
return;
|
|
|
|
/* we have handled the bounds, now do the corners */
|
|
if (self->clip.type == GSK_GPU_CLIP_ROUNDED)
|
|
{
|
|
graphene_rect_t cover;
|
|
GskGpuShaderClip shader_clip;
|
|
float scale_x, scale_y;
|
|
|
|
if (self->modelview)
|
|
{
|
|
/* Yuck, rounded clip and modelview. I give up. */
|
|
gsk_gpu_color_op (self->frame,
|
|
gsk_gpu_clip_get_shader_clip (&self->clip, &self->offset, &node->bounds),
|
|
&node->bounds,
|
|
&self->offset,
|
|
gsk_color_node_get_color (node));
|
|
return;
|
|
}
|
|
|
|
scale_x = graphene_vec2_get_x (&self->scale);
|
|
scale_y = graphene_vec2_get_y (&self->scale);
|
|
clipped = GRAPHENE_RECT_INIT (int_clipped.x / scale_x, int_clipped.y / scale_y,
|
|
int_clipped.width / scale_x, int_clipped.height / scale_y);
|
|
shader_clip = gsk_gpu_clip_get_shader_clip (&self->clip, graphene_point_zero(), &clipped);
|
|
if (shader_clip != GSK_GPU_SHADER_CLIP_NONE)
|
|
{
|
|
gsk_rounded_rect_get_largest_cover (&self->clip.rect, &clipped, &cover);
|
|
int_clipped.x = ceil (cover.origin.x * scale_x);
|
|
int_clipped.y = ceil (cover.origin.y * scale_y);
|
|
int_clipped.width = floor ((cover.origin.x + cover.size.width) * scale_x) - int_clipped.x;
|
|
int_clipped.height = floor ((cover.origin.y + cover.size.height) * scale_y) - int_clipped.y;
|
|
if (int_clipped.width == 0 || int_clipped.height == 0)
|
|
{
|
|
gsk_gpu_color_op (self->frame,
|
|
shader_clip,
|
|
&clipped,
|
|
graphene_point_zero (),
|
|
color);
|
|
return;
|
|
}
|
|
cover = GRAPHENE_RECT_INIT (int_clipped.x / scale_x, int_clipped.y / scale_y,
|
|
int_clipped.width / scale_x, int_clipped.height / scale_y);
|
|
if (clipped.origin.x != cover.origin.x)
|
|
gsk_gpu_color_op (self->frame,
|
|
shader_clip,
|
|
&GRAPHENE_RECT_INIT (clipped.origin.x, clipped.origin.y, cover.origin.x - clipped.origin.x, clipped.size.height),
|
|
graphene_point_zero (),
|
|
color);
|
|
if (clipped.origin.y != cover.origin.y)
|
|
gsk_gpu_color_op (self->frame,
|
|
shader_clip,
|
|
&GRAPHENE_RECT_INIT (clipped.origin.x, clipped.origin.y, clipped.size.width, cover.origin.y - clipped.origin.y),
|
|
graphene_point_zero (),
|
|
color);
|
|
if (clipped.origin.x + clipped.size.width != cover.origin.x + cover.size.width)
|
|
gsk_gpu_color_op (self->frame,
|
|
shader_clip,
|
|
&GRAPHENE_RECT_INIT (cover.origin.x + cover.size.width,
|
|
clipped.origin.y,
|
|
clipped.origin.x + clipped.size.width - cover.origin.x - cover.size.width,
|
|
clipped.size.height),
|
|
graphene_point_zero (),
|
|
color);
|
|
if (clipped.origin.y + clipped.size.height != cover.origin.y + cover.size.height)
|
|
gsk_gpu_color_op (self->frame,
|
|
shader_clip,
|
|
&GRAPHENE_RECT_INIT (clipped.origin.x,
|
|
cover.origin.y + cover.size.height,
|
|
clipped.size.width,
|
|
clipped.origin.y + clipped.size.height - cover.origin.y - cover.size.height),
|
|
graphene_point_zero (),
|
|
color);
|
|
}
|
|
}
|
|
|
|
gsk_gpu_clear_op (self->frame,
|
|
&int_clipped,
|
|
color);
|
|
return;
|
|
}
|
|
|
|
gsk_gpu_color_op (self->frame,
|
|
gsk_gpu_clip_get_shader_clip (&self->clip, &self->offset, &node->bounds),
|
|
&node->bounds,
|
|
&self->offset,
|
|
gsk_color_node_get_color (node));
|
|
}
|
|
|
|
static gboolean
|
|
gsk_gpu_node_processor_create_color_pattern (GskGpuPatternWriter *self,
|
|
GskRenderNode *node)
|
|
{
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_COLOR);
|
|
gsk_gpu_buffer_writer_append_rgba (&self->writer, gsk_color_node_get_color (node));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_node_processor_add_border_node (GskGpuNodeProcessor *self,
|
|
GskRenderNode *node)
|
|
{
|
|
gsk_gpu_border_op (self->frame,
|
|
gsk_gpu_clip_get_shader_clip (&self->clip, &self->offset, &node->bounds),
|
|
gsk_border_node_get_outline (node),
|
|
&self->offset,
|
|
graphene_point_zero (),
|
|
gsk_border_node_get_widths (node),
|
|
gsk_border_node_get_colors (node));
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_node_processor_add_texture_node (GskGpuNodeProcessor *self,
|
|
GskRenderNode *node)
|
|
{
|
|
GskGpuDevice *device;
|
|
GskGpuImage *image;
|
|
GdkTexture *texture;
|
|
gint64 timestamp;
|
|
guint32 descriptor;
|
|
|
|
device = gsk_gpu_frame_get_device (self->frame);
|
|
texture = gsk_texture_node_get_texture (node);
|
|
timestamp = gsk_gpu_frame_get_timestamp (self->frame);
|
|
|
|
image = gsk_gpu_device_lookup_texture_image (device, texture, timestamp);
|
|
if (image == NULL)
|
|
{
|
|
image = gsk_gpu_frame_upload_texture (self->frame, texture);
|
|
if (image == NULL)
|
|
{
|
|
GSK_DEBUG (FALLBACK, "Unsupported texture format %u for size %dx%d",
|
|
gdk_texture_get_format (texture),
|
|
gdk_texture_get_width (texture),
|
|
gdk_texture_get_height (texture));
|
|
gsk_gpu_node_processor_add_fallback_node (self, node);
|
|
return;
|
|
}
|
|
}
|
|
descriptor = gsk_gpu_node_processor_add_image (self, image, GSK_GPU_SAMPLER_DEFAULT);
|
|
|
|
gsk_gpu_texture_op (self->frame,
|
|
gsk_gpu_clip_get_shader_clip (&self->clip, &self->offset, &node->bounds),
|
|
self->desc,
|
|
descriptor,
|
|
&node->bounds,
|
|
&self->offset,
|
|
&node->bounds);
|
|
|
|
g_object_unref (image);
|
|
}
|
|
|
|
static gboolean
|
|
gsk_gpu_node_processor_create_texture_pattern (GskGpuPatternWriter *self,
|
|
GskRenderNode *node)
|
|
{
|
|
GskGpuDevice *device;
|
|
GdkTexture *texture;
|
|
gint64 timestamp;
|
|
guint32 descriptor;
|
|
GskGpuImage *image;
|
|
|
|
device = gsk_gpu_frame_get_device (self->frame);
|
|
texture = gsk_texture_node_get_texture (node);
|
|
timestamp = gsk_gpu_frame_get_timestamp (self->frame);
|
|
|
|
image = gsk_gpu_device_lookup_texture_image (device, texture, timestamp);
|
|
if (image == NULL)
|
|
{
|
|
image = gsk_gpu_frame_upload_texture (self->frame, texture);
|
|
if (image == NULL)
|
|
return FALSE;
|
|
}
|
|
|
|
if (!gsk_gpu_pattern_writer_add_image (self, image, GSK_GPU_SAMPLER_DEFAULT, &descriptor))
|
|
{
|
|
g_object_unref (image);
|
|
return FALSE;
|
|
}
|
|
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_TEXTURE);
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, descriptor);
|
|
gsk_gpu_buffer_writer_append_rect (&self->writer, &node->bounds, &self->offset);
|
|
|
|
g_object_unref (image);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_node_processor_add_inset_shadow_node (GskGpuNodeProcessor *self,
|
|
GskRenderNode *node)
|
|
{
|
|
const GdkRGBA *color;
|
|
float spread, blur_radius;
|
|
|
|
spread = gsk_inset_shadow_node_get_spread (node);
|
|
color = gsk_inset_shadow_node_get_color (node);
|
|
blur_radius = gsk_inset_shadow_node_get_blur_radius (node);
|
|
|
|
if (blur_radius == 0)
|
|
{
|
|
gsk_gpu_border_op (self->frame,
|
|
gsk_gpu_clip_get_shader_clip (&self->clip, &self->offset, &node->bounds),
|
|
gsk_inset_shadow_node_get_outline (node),
|
|
&self->offset,
|
|
&GRAPHENE_POINT_INIT (gsk_inset_shadow_node_get_dx (node),
|
|
gsk_inset_shadow_node_get_dy (node)),
|
|
(float[4]) { spread, spread, spread, spread },
|
|
(GdkRGBA[4]) { *color, *color, *color, *color });
|
|
}
|
|
else
|
|
{
|
|
gsk_gpu_box_shadow_op (self->frame,
|
|
gsk_gpu_clip_get_shader_clip (&self->clip, &self->offset, &node->bounds),
|
|
TRUE,
|
|
&node->bounds,
|
|
gsk_inset_shadow_node_get_outline (node),
|
|
&GRAPHENE_POINT_INIT (gsk_inset_shadow_node_get_dx (node),
|
|
gsk_inset_shadow_node_get_dy (node)),
|
|
spread,
|
|
blur_radius,
|
|
&self->offset,
|
|
color);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_node_processor_add_outset_shadow_node (GskGpuNodeProcessor *self,
|
|
GskRenderNode *node)
|
|
{
|
|
const GdkRGBA *color;
|
|
float spread, blur_radius, dx, dy;
|
|
|
|
spread = gsk_outset_shadow_node_get_spread (node);
|
|
color = gsk_outset_shadow_node_get_color (node);
|
|
blur_radius = gsk_outset_shadow_node_get_blur_radius (node);
|
|
dx = gsk_outset_shadow_node_get_dx (node);
|
|
dy = gsk_outset_shadow_node_get_dy (node);
|
|
|
|
if (blur_radius == 0)
|
|
{
|
|
GskRoundedRect outline;
|
|
|
|
gsk_rounded_rect_init_copy (&outline, gsk_outset_shadow_node_get_outline (node));
|
|
gsk_rounded_rect_shrink (&outline, -spread, -spread, -spread, -spread);
|
|
graphene_rect_offset (&outline.bounds, dx, dy);
|
|
|
|
gsk_gpu_border_op (self->frame,
|
|
gsk_gpu_clip_get_shader_clip (&self->clip, &self->offset, &node->bounds),
|
|
&outline,
|
|
&self->offset,
|
|
&GRAPHENE_POINT_INIT (-dx, -dy),
|
|
(float[4]) { spread, spread, spread, spread },
|
|
(GdkRGBA[4]) { *color, *color, *color, *color });
|
|
}
|
|
else
|
|
{
|
|
gsk_gpu_box_shadow_op (self->frame,
|
|
gsk_gpu_clip_get_shader_clip (&self->clip, &self->offset, &node->bounds),
|
|
FALSE,
|
|
&node->bounds,
|
|
gsk_outset_shadow_node_get_outline (node),
|
|
&GRAPHENE_POINT_INIT (dx, dy),
|
|
spread,
|
|
blur_radius,
|
|
&self->offset,
|
|
color);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gsk_gpu_node_processor_create_linear_gradient_pattern (GskGpuPatternWriter *self,
|
|
GskRenderNode *node)
|
|
{
|
|
if (gsk_render_node_get_node_type (node) == GSK_REPEATING_LINEAR_GRADIENT_NODE)
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_REPEATING_LINEAR_GRADIENT);
|
|
else
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_LINEAR_GRADIENT);
|
|
|
|
gsk_gpu_buffer_writer_append_point (&self->writer,
|
|
gsk_linear_gradient_node_get_start (node),
|
|
&self->offset);
|
|
gsk_gpu_buffer_writer_append_point (&self->writer,
|
|
gsk_linear_gradient_node_get_end (node),
|
|
&self->offset);
|
|
gsk_gpu_buffer_writer_append_color_stops (&self->writer,
|
|
gsk_linear_gradient_node_get_color_stops (node, NULL),
|
|
gsk_linear_gradient_node_get_n_color_stops (node));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gsk_gpu_node_processor_create_radial_gradient_pattern (GskGpuPatternWriter *self,
|
|
GskRenderNode *node)
|
|
{
|
|
if (gsk_render_node_get_node_type (node) == GSK_REPEATING_RADIAL_GRADIENT_NODE)
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_REPEATING_RADIAL_GRADIENT);
|
|
else
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_RADIAL_GRADIENT);
|
|
|
|
gsk_gpu_buffer_writer_append_point (&self->writer,
|
|
gsk_radial_gradient_node_get_center (node),
|
|
&self->offset);
|
|
gsk_gpu_buffer_writer_append_float (&self->writer, gsk_radial_gradient_node_get_hradius (node));
|
|
gsk_gpu_buffer_writer_append_float (&self->writer, gsk_radial_gradient_node_get_vradius (node));
|
|
gsk_gpu_buffer_writer_append_float (&self->writer, gsk_radial_gradient_node_get_start (node));
|
|
gsk_gpu_buffer_writer_append_float (&self->writer, gsk_radial_gradient_node_get_end (node));
|
|
gsk_gpu_buffer_writer_append_color_stops (&self->writer,
|
|
gsk_radial_gradient_node_get_color_stops (node, NULL),
|
|
gsk_radial_gradient_node_get_n_color_stops (node));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gsk_gpu_node_processor_create_conic_gradient_pattern (GskGpuPatternWriter *self,
|
|
GskRenderNode *node)
|
|
{
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_CONIC_GRADIENT);
|
|
gsk_gpu_buffer_writer_append_point (&self->writer,
|
|
gsk_conic_gradient_node_get_center (node),
|
|
&self->offset);
|
|
gsk_gpu_buffer_writer_append_float (&self->writer, gsk_conic_gradient_node_get_angle (node));
|
|
gsk_gpu_buffer_writer_append_color_stops (&self->writer,
|
|
gsk_conic_gradient_node_get_color_stops (node, NULL),
|
|
gsk_conic_gradient_node_get_n_color_stops (node));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_node_processor_add_blur_node (GskGpuNodeProcessor *self,
|
|
GskRenderNode *node)
|
|
{
|
|
GskRenderNode *child;
|
|
GskGpuImage *image;
|
|
graphene_rect_t tex_rect, clip_rect;
|
|
float blur_radius, clip_radius;
|
|
guint32 descriptor;
|
|
|
|
child = gsk_blur_node_get_child (node);
|
|
blur_radius = gsk_blur_node_get_radius (node);
|
|
if (blur_radius <= 0.f)
|
|
{
|
|
gsk_gpu_node_processor_add_node (self, child);
|
|
return;
|
|
}
|
|
|
|
clip_radius = gsk_cairo_blur_compute_pixels (blur_radius / 2.0);
|
|
gsk_gpu_node_processor_get_clip_bounds (self, &clip_rect);
|
|
graphene_rect_inset (&clip_rect, -clip_radius, -clip_radius);
|
|
image = gsk_gpu_get_node_as_image (self->frame,
|
|
&clip_rect,
|
|
&self->scale,
|
|
child,
|
|
&tex_rect);
|
|
descriptor = gsk_gpu_node_processor_add_image (self, image, GSK_GPU_SAMPLER_TRANSPARENT);
|
|
|
|
gsk_gpu_node_processor_blur_op (self,
|
|
&node->bounds,
|
|
graphene_point_zero (),
|
|
blur_radius,
|
|
NULL,
|
|
self->desc,
|
|
descriptor,
|
|
gdk_memory_format_get_depth (gsk_gpu_image_get_format (image)),
|
|
&tex_rect);
|
|
|
|
g_object_unref (image);
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_node_processor_add_shadow_node (GskGpuNodeProcessor *self,
|
|
GskRenderNode *node)
|
|
{
|
|
GskGpuImage *image;
|
|
graphene_rect_t clip_bounds, tex_rect;
|
|
GskRenderNode *child;
|
|
gsize i, n_shadows;
|
|
GskGpuDescriptors *desc;
|
|
guint32 descriptor;
|
|
|
|
n_shadows = gsk_shadow_node_get_n_shadows (node);
|
|
child = gsk_shadow_node_get_child (node);
|
|
/* enlarge clip for shadow offsets */
|
|
gsk_gpu_node_processor_get_clip_bounds (self, &clip_bounds);
|
|
clip_bounds = GRAPHENE_RECT_INIT (clip_bounds.origin.x - node->bounds.size.width + child->bounds.size.width - node->bounds.origin.x + child->bounds.origin.x,
|
|
clip_bounds.origin.y - node->bounds.size.height + child->bounds.size.height - node->bounds.origin.y + child->bounds.origin.y,
|
|
clip_bounds.size.width + node->bounds.size.width - child->bounds.size.width,
|
|
clip_bounds.size.height + node->bounds.size.height - child->bounds.size.height);
|
|
|
|
image = gsk_gpu_get_node_as_image (self->frame,
|
|
&clip_bounds,
|
|
&self->scale,
|
|
child,
|
|
&tex_rect);
|
|
descriptor = gsk_gpu_node_processor_add_image (self, image, GSK_GPU_SAMPLER_TRANSPARENT);
|
|
desc = self->desc;
|
|
|
|
for (i = 0; i < n_shadows; i++)
|
|
{
|
|
const GskShadow *shadow = gsk_shadow_node_get_shadow (node, i);
|
|
if (shadow->radius == 0)
|
|
{
|
|
graphene_point_t shadow_offset = GRAPHENE_POINT_INIT (self->offset.x + shadow->dx,
|
|
self->offset.y + shadow->dy);
|
|
gsk_gpu_colorize_op (self->frame,
|
|
gsk_gpu_clip_get_shader_clip (&self->clip, &shadow_offset, &child->bounds),
|
|
desc,
|
|
descriptor,
|
|
&child->bounds,
|
|
&shadow_offset,
|
|
&tex_rect,
|
|
&shadow->color);
|
|
}
|
|
else
|
|
{
|
|
graphene_rect_t bounds;
|
|
float clip_radius = gsk_cairo_blur_compute_pixels (0.5 * shadow->radius);
|
|
graphene_rect_inset_r (&child->bounds, - clip_radius, - clip_radius, &bounds);
|
|
gsk_gpu_node_processor_blur_op (self,
|
|
&bounds,
|
|
&GRAPHENE_POINT_INIT (shadow->dx, shadow->dy),
|
|
shadow->radius,
|
|
&shadow->color,
|
|
desc,
|
|
descriptor,
|
|
gdk_memory_format_get_depth (gsk_gpu_image_get_format (image)),
|
|
&tex_rect);
|
|
}
|
|
}
|
|
|
|
descriptor = gsk_gpu_node_processor_add_image (self, image, GSK_GPU_SAMPLER_DEFAULT);
|
|
gsk_gpu_texture_op (self->frame,
|
|
gsk_gpu_clip_get_shader_clip (&self->clip, &self->offset, &child->bounds),
|
|
self->desc,
|
|
descriptor,
|
|
&child->bounds,
|
|
&self->offset,
|
|
&tex_rect);
|
|
|
|
g_object_unref (image);
|
|
}
|
|
|
|
static gboolean
|
|
gsk_gpu_node_processor_create_cross_fade_pattern (GskGpuPatternWriter *self,
|
|
GskRenderNode *node)
|
|
{
|
|
GskRenderNode *start_child, *end_child;
|
|
|
|
start_child = gsk_cross_fade_node_get_start_child (node);
|
|
end_child = gsk_cross_fade_node_get_end_child (node);
|
|
|
|
if (!gsk_gpu_node_processor_create_node_pattern (self, start_child))
|
|
return FALSE;
|
|
if (!gsk_rect_contains_rect (&start_child->bounds, &node->bounds))
|
|
{
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_CLIP);
|
|
gsk_gpu_buffer_writer_append_rect (&self->writer, &start_child->bounds, &self->offset);
|
|
}
|
|
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_PUSH_COLOR);
|
|
|
|
if (!gsk_gpu_pattern_writer_push_stack (self))
|
|
return FALSE;
|
|
|
|
if (!gsk_gpu_node_processor_create_node_pattern (self, end_child))
|
|
{
|
|
gsk_gpu_pattern_writer_pop_stack (self);
|
|
return FALSE;
|
|
}
|
|
if (!gsk_rect_contains_rect (&end_child->bounds, &node->bounds))
|
|
{
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_CLIP);
|
|
gsk_gpu_buffer_writer_append_rect (&self->writer, &end_child->bounds, &self->offset);
|
|
}
|
|
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_POP_CROSS_FADE);
|
|
gsk_gpu_buffer_writer_append_float (&self->writer, gsk_cross_fade_node_get_progress (node));
|
|
|
|
gsk_gpu_pattern_writer_pop_stack (self);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gsk_gpu_node_processor_create_mask_pattern (GskGpuPatternWriter *self,
|
|
GskRenderNode *node)
|
|
{
|
|
GskRenderNode *source_child, *mask_child;
|
|
|
|
source_child = gsk_mask_node_get_source (node);
|
|
mask_child = gsk_mask_node_get_mask (node);
|
|
|
|
if (!gsk_gpu_node_processor_create_node_pattern (self, source_child))
|
|
return FALSE;
|
|
if (!gsk_rect_contains_rect (&source_child->bounds, &node->bounds))
|
|
{
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_CLIP);
|
|
gsk_gpu_buffer_writer_append_rect (&self->writer, &source_child->bounds, &self->offset);
|
|
}
|
|
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_PUSH_COLOR);
|
|
|
|
if (!gsk_gpu_pattern_writer_push_stack (self))
|
|
return FALSE;
|
|
|
|
if (!gsk_gpu_node_processor_create_node_pattern (self, mask_child))
|
|
{
|
|
gsk_gpu_pattern_writer_pop_stack (self);
|
|
return FALSE;
|
|
}
|
|
if (!gsk_rect_contains_rect (&mask_child->bounds, &node->bounds))
|
|
{
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_CLIP);
|
|
gsk_gpu_buffer_writer_append_rect (&self->writer, &mask_child->bounds, &self->offset);
|
|
}
|
|
|
|
switch (gsk_mask_node_get_mask_mode (node))
|
|
{
|
|
case GSK_MASK_MODE_ALPHA:
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_POP_MASK_ALPHA);
|
|
break;
|
|
|
|
case GSK_MASK_MODE_INVERTED_ALPHA:
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_POP_MASK_INVERTED_ALPHA);
|
|
break;
|
|
|
|
case GSK_MASK_MODE_LUMINANCE:
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_POP_MASK_LUMINANCE);
|
|
break;
|
|
|
|
case GSK_MASK_MODE_INVERTED_LUMINANCE:
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_POP_MASK_INVERTED_LUMINANCE);
|
|
break;
|
|
|
|
default:
|
|
g_return_val_if_reached (FALSE);
|
|
}
|
|
|
|
|
|
gsk_gpu_pattern_writer_pop_stack (self);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_node_processor_add_glyph_node (GskGpuNodeProcessor *self,
|
|
GskRenderNode *node)
|
|
{
|
|
GskGpuDevice *device;
|
|
const PangoGlyphInfo *glyphs;
|
|
PangoFont *font;
|
|
graphene_point_t offset;
|
|
guint i, num_glyphs;
|
|
float scale, inv_scale;
|
|
|
|
device = gsk_gpu_frame_get_device (self->frame);
|
|
num_glyphs = gsk_text_node_get_num_glyphs (node);
|
|
glyphs = gsk_text_node_get_glyphs (node, NULL);
|
|
font = gsk_text_node_get_font (node);
|
|
offset = *gsk_text_node_get_offset (node);
|
|
offset.x += self->offset.x;
|
|
offset.y += self->offset.y;
|
|
|
|
scale = MAX (graphene_vec2_get_x (&self->scale), graphene_vec2_get_y (&self->scale));
|
|
inv_scale = 1.f / scale;
|
|
|
|
for (i = 0; i < num_glyphs; i++)
|
|
{
|
|
GskGpuImage *image;
|
|
graphene_rect_t glyph_bounds, glyph_tex_rect;
|
|
graphene_point_t glyph_offset;
|
|
guint32 descriptor;
|
|
|
|
image = gsk_gpu_device_lookup_glyph_image (device,
|
|
self->frame,
|
|
font,
|
|
glyphs[i].glyph,
|
|
0,
|
|
scale,
|
|
&glyph_bounds,
|
|
&glyph_offset);
|
|
|
|
graphene_rect_scale (&GRAPHENE_RECT_INIT (-glyph_bounds.origin.x, -glyph_bounds.origin.y, gsk_gpu_image_get_width (image), gsk_gpu_image_get_height (image)), inv_scale, inv_scale, &glyph_tex_rect);
|
|
graphene_rect_scale (&GRAPHENE_RECT_INIT(0, 0, glyph_bounds.size.width, glyph_bounds.size.height), inv_scale, inv_scale, &glyph_bounds);
|
|
glyph_offset = GRAPHENE_POINT_INIT (offset.x - glyph_offset.x * inv_scale + (float) glyphs[i].geometry.x_offset / PANGO_SCALE,
|
|
offset.y - glyph_offset.y * inv_scale + (float) glyphs[i].geometry.y_offset / PANGO_SCALE);
|
|
descriptor = gsk_gpu_node_processor_add_image (self, image, GSK_GPU_SAMPLER_DEFAULT);
|
|
if (gsk_text_node_has_color_glyphs (node))
|
|
gsk_gpu_texture_op (self->frame,
|
|
gsk_gpu_clip_get_shader_clip (&self->clip, &glyph_offset, &glyph_bounds),
|
|
self->desc,
|
|
descriptor,
|
|
&glyph_bounds,
|
|
&glyph_offset,
|
|
&glyph_tex_rect);
|
|
else
|
|
gsk_gpu_colorize_op (self->frame,
|
|
gsk_gpu_clip_get_shader_clip (&self->clip, &glyph_offset, &glyph_bounds),
|
|
self->desc,
|
|
descriptor,
|
|
&glyph_bounds,
|
|
&glyph_offset,
|
|
&glyph_tex_rect,
|
|
gsk_text_node_get_color (node));
|
|
|
|
offset.x += (float) glyphs[i].geometry.width / PANGO_SCALE;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gsk_gpu_node_processor_create_glyph_pattern (GskGpuPatternWriter *self,
|
|
GskRenderNode *node)
|
|
{
|
|
GskGpuDevice *device;
|
|
const PangoGlyphInfo *glyphs;
|
|
PangoFont *font;
|
|
guint num_glyphs;
|
|
gsize i;
|
|
float scale, inv_scale;
|
|
guint32 tex_id;
|
|
GskGpuImage *last_image;
|
|
graphene_point_t offset;
|
|
|
|
if (gsk_text_node_has_color_glyphs (node))
|
|
return FALSE;
|
|
|
|
device = gsk_gpu_frame_get_device (self->frame);
|
|
num_glyphs = gsk_text_node_get_num_glyphs (node);
|
|
glyphs = gsk_text_node_get_glyphs (node, NULL);
|
|
font = gsk_text_node_get_font (node);
|
|
offset = *gsk_text_node_get_offset (node);
|
|
offset.x += self->offset.x;
|
|
offset.y += self->offset.y;
|
|
|
|
scale = MAX (graphene_vec2_get_x (&self->scale), graphene_vec2_get_y (&self->scale));
|
|
inv_scale = 1.f / scale;
|
|
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_GLYPHS);
|
|
gsk_gpu_buffer_writer_append_rgba (&self->writer, gsk_text_node_get_color (node));
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, num_glyphs);
|
|
|
|
last_image = NULL;
|
|
for (i = 0; i < num_glyphs; i++)
|
|
{
|
|
GskGpuImage *image;
|
|
graphene_rect_t glyph_bounds;
|
|
graphene_point_t glyph_offset;
|
|
|
|
image = gsk_gpu_device_lookup_glyph_image (device,
|
|
self->frame,
|
|
font,
|
|
glyphs[i].glyph,
|
|
0,
|
|
scale,
|
|
&glyph_bounds,
|
|
&glyph_offset);
|
|
|
|
if (image != last_image)
|
|
{
|
|
if (!gsk_gpu_pattern_writer_add_image (self, image, GSK_GPU_SAMPLER_DEFAULT, &tex_id))
|
|
return FALSE;
|
|
|
|
last_image = image;
|
|
}
|
|
|
|
graphene_rect_scale (&glyph_bounds, inv_scale, inv_scale, &glyph_bounds);
|
|
glyph_offset = GRAPHENE_POINT_INIT (offset.x - glyph_offset.x * inv_scale + (float) glyphs[i].geometry.x_offset / PANGO_SCALE,
|
|
offset.y - glyph_offset.y * inv_scale + (float) glyphs[i].geometry.y_offset / PANGO_SCALE);
|
|
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, tex_id);
|
|
gsk_gpu_buffer_writer_append_rect (&self->writer,
|
|
&glyph_bounds,
|
|
&glyph_offset);
|
|
gsk_gpu_buffer_writer_append_rect (&self->writer,
|
|
&GRAPHENE_RECT_INIT (
|
|
0, 0,
|
|
gsk_gpu_image_get_width (image) * inv_scale,
|
|
gsk_gpu_image_get_height (image) * inv_scale
|
|
),
|
|
&glyph_offset);
|
|
|
|
offset.x += (float) glyphs[i].geometry.width / PANGO_SCALE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gsk_gpu_node_processor_create_opacity_pattern (GskGpuPatternWriter *self,
|
|
GskRenderNode *node)
|
|
{
|
|
if (!gsk_gpu_node_processor_create_node_pattern (self, gsk_opacity_node_get_child (node)))
|
|
return FALSE;
|
|
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_OPACITY);
|
|
gsk_gpu_buffer_writer_append_float (&self->writer, gsk_opacity_node_get_opacity (node));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gsk_gpu_node_processor_create_color_matrix_pattern (GskGpuPatternWriter *self,
|
|
GskRenderNode *node)
|
|
{
|
|
if (!gsk_gpu_node_processor_create_node_pattern (self, gsk_color_matrix_node_get_child (node)))
|
|
return FALSE;
|
|
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_COLOR_MATRIX);
|
|
gsk_gpu_buffer_writer_append_matrix (&self->writer, gsk_color_matrix_node_get_color_matrix (node));
|
|
gsk_gpu_buffer_writer_append_vec4 (&self->writer, gsk_color_matrix_node_get_color_offset (node));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_node_processor_add_subsurface_node (GskGpuNodeProcessor *self,
|
|
GskRenderNode *node)
|
|
{
|
|
gsk_gpu_node_processor_add_node (self, gsk_subsurface_node_get_child (node));
|
|
}
|
|
|
|
static gboolean
|
|
gsk_gpu_node_processor_create_repeat_pattern (GskGpuPatternWriter *self,
|
|
GskRenderNode *node)
|
|
{
|
|
GskRenderNode *child;
|
|
const graphene_rect_t *child_bounds;
|
|
graphene_rect_t old_bounds;
|
|
|
|
child = gsk_repeat_node_get_child (node);
|
|
child_bounds = gsk_repeat_node_get_child_bounds (node);
|
|
|
|
if (gsk_rect_is_empty (child_bounds))
|
|
{
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_COLOR);
|
|
gsk_gpu_buffer_writer_append_rgba (&self->writer, &(GdkRGBA) { 0, 0, 0, 0 });
|
|
return TRUE;
|
|
}
|
|
|
|
if (!gsk_gpu_pattern_writer_push_stack (self))
|
|
return FALSE;
|
|
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_REPEAT_PUSH);
|
|
gsk_gpu_buffer_writer_append_rect (&self->writer, child_bounds, &self->offset);
|
|
|
|
old_bounds = self->bounds;
|
|
self->bounds = GRAPHENE_RECT_INIT (child_bounds->origin.x + self->offset.x,
|
|
child_bounds->origin.y + self->offset.y,
|
|
child_bounds->size.width,
|
|
child_bounds->size.height);
|
|
|
|
if (!gsk_gpu_node_processor_create_node_pattern (self, child))
|
|
{
|
|
gsk_gpu_pattern_writer_pop_stack (self);
|
|
return FALSE;
|
|
}
|
|
self->bounds = old_bounds;
|
|
|
|
if (!gsk_rect_contains_rect (&child->bounds, child_bounds))
|
|
{
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_CLIP);
|
|
gsk_gpu_buffer_writer_append_rect (&self->writer, &child->bounds, &self->offset);
|
|
}
|
|
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_POSITION_POP);
|
|
gsk_gpu_pattern_writer_pop_stack (self);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gsk_gpu_node_processor_create_subsurface_pattern (GskGpuPatternWriter *self,
|
|
GskRenderNode *node)
|
|
{
|
|
return gsk_gpu_node_processor_create_node_pattern (self, gsk_subsurface_node_get_child (node));
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_node_processor_add_container_node (GskGpuNodeProcessor *self,
|
|
GskRenderNode *node)
|
|
{
|
|
for (guint i = 0; i < gsk_container_node_get_n_children (node); i++)
|
|
gsk_gpu_node_processor_add_node (self, gsk_container_node_get_child (node, i));
|
|
}
|
|
|
|
static gboolean
|
|
gsk_gpu_node_processor_create_debug_pattern (GskGpuPatternWriter *self,
|
|
GskRenderNode *node)
|
|
{
|
|
return gsk_gpu_node_processor_create_node_pattern (self, gsk_debug_node_get_child (node));
|
|
}
|
|
|
|
static void
|
|
gsk_gpu_node_processor_add_debug_node (GskGpuNodeProcessor *self,
|
|
GskRenderNode *node)
|
|
{
|
|
gsk_gpu_node_processor_add_node (self, gsk_debug_node_get_child (node));
|
|
}
|
|
|
|
static const struct
|
|
{
|
|
GskGpuGlobals ignored_globals;
|
|
void (* process_node) (GskGpuNodeProcessor *self,
|
|
GskRenderNode *node);
|
|
gboolean (* create_pattern) (GskGpuPatternWriter *self,
|
|
GskRenderNode *node);
|
|
} nodes_vtable[] = {
|
|
[GSK_NOT_A_RENDER_NODE] = {
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
},
|
|
[GSK_CONTAINER_NODE] = {
|
|
GSK_GPU_GLOBAL_MATRIX | GSK_GPU_GLOBAL_SCALE | GSK_GPU_GLOBAL_CLIP | GSK_GPU_GLOBAL_SCISSOR,
|
|
gsk_gpu_node_processor_add_container_node,
|
|
NULL,
|
|
},
|
|
[GSK_CAIRO_NODE] = {
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
},
|
|
[GSK_COLOR_NODE] = {
|
|
0,
|
|
gsk_gpu_node_processor_add_color_node,
|
|
gsk_gpu_node_processor_create_color_pattern,
|
|
},
|
|
[GSK_LINEAR_GRADIENT_NODE] = {
|
|
0,
|
|
gsk_gpu_node_processor_add_node_as_pattern,
|
|
gsk_gpu_node_processor_create_linear_gradient_pattern,
|
|
},
|
|
[GSK_REPEATING_LINEAR_GRADIENT_NODE] = {
|
|
0,
|
|
gsk_gpu_node_processor_add_node_as_pattern,
|
|
gsk_gpu_node_processor_create_linear_gradient_pattern,
|
|
},
|
|
[GSK_RADIAL_GRADIENT_NODE] = {
|
|
0,
|
|
gsk_gpu_node_processor_add_node_as_pattern,
|
|
gsk_gpu_node_processor_create_radial_gradient_pattern,
|
|
},
|
|
[GSK_REPEATING_RADIAL_GRADIENT_NODE] = {
|
|
0,
|
|
gsk_gpu_node_processor_add_node_as_pattern,
|
|
gsk_gpu_node_processor_create_radial_gradient_pattern,
|
|
},
|
|
[GSK_CONIC_GRADIENT_NODE] = {
|
|
0,
|
|
gsk_gpu_node_processor_add_node_as_pattern,
|
|
gsk_gpu_node_processor_create_conic_gradient_pattern,
|
|
},
|
|
[GSK_BORDER_NODE] = {
|
|
0,
|
|
gsk_gpu_node_processor_add_border_node,
|
|
NULL,
|
|
},
|
|
[GSK_TEXTURE_NODE] = {
|
|
0,
|
|
gsk_gpu_node_processor_add_texture_node,
|
|
gsk_gpu_node_processor_create_texture_pattern,
|
|
},
|
|
[GSK_INSET_SHADOW_NODE] = {
|
|
0,
|
|
gsk_gpu_node_processor_add_inset_shadow_node,
|
|
NULL,
|
|
},
|
|
[GSK_OUTSET_SHADOW_NODE] = {
|
|
0,
|
|
gsk_gpu_node_processor_add_outset_shadow_node,
|
|
NULL,
|
|
},
|
|
[GSK_TRANSFORM_NODE] = {
|
|
GSK_GPU_GLOBAL_MATRIX | GSK_GPU_GLOBAL_SCALE | GSK_GPU_GLOBAL_CLIP | GSK_GPU_GLOBAL_SCISSOR,
|
|
gsk_gpu_node_processor_add_transform_node,
|
|
NULL,
|
|
},
|
|
[GSK_OPACITY_NODE] = {
|
|
0,
|
|
gsk_gpu_node_processor_add_node_as_pattern,
|
|
gsk_gpu_node_processor_create_opacity_pattern,
|
|
},
|
|
[GSK_COLOR_MATRIX_NODE] = {
|
|
0,
|
|
gsk_gpu_node_processor_add_node_as_pattern,
|
|
gsk_gpu_node_processor_create_color_matrix_pattern
|
|
},
|
|
[GSK_REPEAT_NODE] = {
|
|
0,
|
|
gsk_gpu_node_processor_add_node_as_pattern,
|
|
gsk_gpu_node_processor_create_repeat_pattern
|
|
},
|
|
[GSK_CLIP_NODE] = {
|
|
GSK_GPU_GLOBAL_MATRIX | GSK_GPU_GLOBAL_SCALE | GSK_GPU_GLOBAL_CLIP | GSK_GPU_GLOBAL_SCISSOR,
|
|
gsk_gpu_node_processor_add_clip_node,
|
|
gsk_gpu_node_processor_create_clip_pattern,
|
|
},
|
|
[GSK_ROUNDED_CLIP_NODE] = {
|
|
GSK_GPU_GLOBAL_MATRIX | GSK_GPU_GLOBAL_SCALE | GSK_GPU_GLOBAL_CLIP | GSK_GPU_GLOBAL_SCISSOR,
|
|
gsk_gpu_node_processor_add_rounded_clip_node,
|
|
NULL,
|
|
},
|
|
[GSK_SHADOW_NODE] = {
|
|
0,
|
|
gsk_gpu_node_processor_add_shadow_node,
|
|
NULL,
|
|
},
|
|
[GSK_BLEND_NODE] = {
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
},
|
|
[GSK_CROSS_FADE_NODE] = {
|
|
0,
|
|
gsk_gpu_node_processor_add_node_as_pattern,
|
|
gsk_gpu_node_processor_create_cross_fade_pattern,
|
|
},
|
|
[GSK_TEXT_NODE] = {
|
|
0,
|
|
gsk_gpu_node_processor_add_glyph_node,
|
|
gsk_gpu_node_processor_create_glyph_pattern,
|
|
},
|
|
[GSK_BLUR_NODE] = {
|
|
0,
|
|
gsk_gpu_node_processor_add_blur_node,
|
|
NULL,
|
|
},
|
|
[GSK_DEBUG_NODE] = {
|
|
GSK_GPU_GLOBAL_MATRIX | GSK_GPU_GLOBAL_SCALE | GSK_GPU_GLOBAL_CLIP | GSK_GPU_GLOBAL_SCISSOR,
|
|
gsk_gpu_node_processor_add_debug_node,
|
|
gsk_gpu_node_processor_create_debug_pattern,
|
|
},
|
|
[GSK_GL_SHADER_NODE] = {
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
},
|
|
[GSK_TEXTURE_SCALE_NODE] = {
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
},
|
|
[GSK_MASK_NODE] = {
|
|
0,
|
|
gsk_gpu_node_processor_add_node_as_pattern,
|
|
gsk_gpu_node_processor_create_mask_pattern,
|
|
},
|
|
[GSK_FILL_NODE] = {
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
},
|
|
[GSK_STROKE_NODE] = {
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
},
|
|
[GSK_SUBSURFACE_NODE] = {
|
|
GSK_GPU_GLOBAL_MATRIX | GSK_GPU_GLOBAL_SCALE | GSK_GPU_GLOBAL_CLIP | GSK_GPU_GLOBAL_SCISSOR,
|
|
gsk_gpu_node_processor_add_subsurface_node,
|
|
gsk_gpu_node_processor_create_subsurface_pattern,
|
|
},
|
|
};
|
|
|
|
static void
|
|
gsk_gpu_node_processor_add_node (GskGpuNodeProcessor *self,
|
|
GskRenderNode *node)
|
|
{
|
|
GskRenderNodeType node_type;
|
|
|
|
/* This catches the corner cases of empty nodes, so after this check
|
|
* there's quaranteed to be at least 1 pixel that needs to be drawn */
|
|
if (node->bounds.size.width == 0 || node->bounds.size.height == 0)
|
|
return;
|
|
if (!gsk_gpu_clip_may_intersect_rect (&self->clip, &self->offset, &node->bounds))
|
|
return;
|
|
|
|
node_type = gsk_render_node_get_node_type (node);
|
|
if (node_type >= G_N_ELEMENTS (nodes_vtable))
|
|
{
|
|
g_critical ("unkonwn node type %u for %s", node_type, g_type_name_from_instance ((GTypeInstance *) node));
|
|
gsk_gpu_node_processor_add_fallback_node (self, node);
|
|
return;
|
|
}
|
|
|
|
gsk_gpu_node_processor_sync_globals (self, nodes_vtable[node_type].ignored_globals);
|
|
g_assert ((self->pending_globals & ~nodes_vtable[node_type].ignored_globals) == 0);
|
|
|
|
if (nodes_vtable[node_type].process_node)
|
|
{
|
|
nodes_vtable[node_type].process_node (self, node);
|
|
}
|
|
else
|
|
{
|
|
GSK_DEBUG (FALLBACK, "Unsupported node '%s'",
|
|
g_type_name_from_instance ((GTypeInstance *) node));
|
|
gsk_gpu_node_processor_add_fallback_node (self, node);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gsk_gpu_node_processor_create_node_pattern (GskGpuPatternWriter *self,
|
|
GskRenderNode *node)
|
|
{
|
|
GskRenderNodeType node_type;
|
|
graphene_rect_t bounds;
|
|
guchar *tmp_data;
|
|
GskGpuImage *image;
|
|
gsize tmp_size;
|
|
guint32 tex_id;
|
|
|
|
if (!gsk_gpu_frame_should_optimize (self->frame, GSK_GPU_OPTIMIZE_UBER))
|
|
return FALSE;
|
|
|
|
node_type = gsk_render_node_get_node_type (node);
|
|
if (node_type >= G_N_ELEMENTS (nodes_vtable))
|
|
{
|
|
g_critical ("unkonwn node type %u for %s", node_type, g_type_name_from_instance ((GTypeInstance *) node));
|
|
return FALSE;
|
|
}
|
|
|
|
if (nodes_vtable[node_type].create_pattern != NULL)
|
|
{
|
|
gsize size_before = gsk_gpu_buffer_writer_get_size (&self->writer);
|
|
gsize images_before = self->desc ? gsk_gpu_descriptors_get_size (self->desc) : 0;
|
|
if (nodes_vtable[node_type].create_pattern (self, node))
|
|
return TRUE;
|
|
gsk_gpu_buffer_writer_rewind (&self->writer, size_before);
|
|
if (self->desc)
|
|
gsk_gpu_descriptors_set_size (self->desc, images_before);
|
|
}
|
|
|
|
tmp_data = gsk_gpu_buffer_writer_backup (&self->writer, &tmp_size);
|
|
gsk_gpu_buffer_writer_abort (&self->writer);
|
|
image = gsk_gpu_get_node_as_image (self->frame,
|
|
&GRAPHENE_RECT_INIT (
|
|
self->bounds.origin.x - self->offset.x,
|
|
self->bounds.origin.y - self->offset.y,
|
|
self->bounds.size.width,
|
|
self->bounds.size.height
|
|
),
|
|
&self->scale,
|
|
node,
|
|
&bounds);
|
|
if (image == NULL)
|
|
{
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_COLOR);
|
|
gsk_gpu_buffer_writer_append_rgba (&self->writer, &(GdkRGBA) { 0, 0, 0, 0 });
|
|
return TRUE;
|
|
}
|
|
|
|
gsk_gpu_frame_write_buffer_memory (self->frame, &self->writer);
|
|
if (tmp_size)
|
|
{
|
|
gsk_gpu_buffer_writer_append (&self->writer, sizeof (float), tmp_data, tmp_size);
|
|
g_free (tmp_data);
|
|
}
|
|
|
|
if (!gsk_gpu_pattern_writer_add_image (self, image, GSK_GPU_SAMPLER_DEFAULT, &tex_id))
|
|
{
|
|
g_object_unref (image);
|
|
return FALSE;
|
|
}
|
|
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, GSK_GPU_PATTERN_TEXTURE);
|
|
gsk_gpu_buffer_writer_append_uint (&self->writer, tex_id);
|
|
gsk_gpu_buffer_writer_append_rect (&self->writer, &bounds, &self->offset);
|
|
|
|
g_object_unref (image);
|
|
|
|
return TRUE;
|
|
}
|
|
|