mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-26 13:41:07 +00:00
7fa159e94a
Instead of recreating the same renderpass object in every frame and for every offscreen, just reuse it. Technically, we can save this per-renderer or even per-display (it should really be cached by VkDevice), but we have no infrastructure for that.
1484 lines
56 KiB
C
1484 lines
56 KiB
C
#include "config.h"
|
|
|
|
#include "gskvulkanrenderpassprivate.h"
|
|
|
|
#include "gskdebugprivate.h"
|
|
#include "gskprofilerprivate.h"
|
|
#include "gskrendernodeprivate.h"
|
|
#include "gskrenderer.h"
|
|
#include "gskrendererprivate.h"
|
|
#include "gskroundedrectprivate.h"
|
|
#include "gsktransform.h"
|
|
#include "gskvulkanblendmodeopprivate.h"
|
|
#include "gskvulkanbluropprivate.h"
|
|
#include "gskvulkanborderopprivate.h"
|
|
#include "gskvulkanclipprivate.h"
|
|
#include "gskvulkancolormatrixopprivate.h"
|
|
#include "gskvulkancoloropprivate.h"
|
|
#include "gskvulkancrossfadeopprivate.h"
|
|
#include "gskvulkanglyphopprivate.h"
|
|
#include "gskvulkaninsetshadowopprivate.h"
|
|
#include "gskvulkanlineargradientopprivate.h"
|
|
#include "gskvulkanmaskopprivate.h"
|
|
#include "gskvulkanopprivate.h"
|
|
#include "gskvulkanprivate.h"
|
|
#include "gskvulkanrendererprivate.h"
|
|
#include "gskvulkanimageprivate.h"
|
|
#include "gskvulkanoffscreenopprivate.h"
|
|
#include "gskvulkanoutsetshadowopprivate.h"
|
|
#include "gskvulkanpushconstantsopprivate.h"
|
|
#include "gskvulkanscissoropprivate.h"
|
|
#include "gskvulkantextureopprivate.h"
|
|
#include "gskvulkanuploadcairoopprivate.h"
|
|
#include "gskvulkanuploadopprivate.h"
|
|
#include "gskprivate.h"
|
|
|
|
#include "gdk/gdkvulkancontextprivate.h"
|
|
|
|
#define GDK_ARRAY_NAME gsk_vulkan_render_ops
|
|
#define GDK_ARRAY_TYPE_NAME GskVulkanRenderOps
|
|
#define GDK_ARRAY_ELEMENT_TYPE guchar
|
|
#define GDK_ARRAY_BY_VALUE 1
|
|
#include "gdk/gdkarrayimpl.c"
|
|
|
|
#define ORTHO_NEAR_PLANE -10000
|
|
#define ORTHO_FAR_PLANE 10000
|
|
|
|
typedef struct _GskVulkanParseState GskVulkanParseState;
|
|
|
|
struct _GskVulkanRenderPass
|
|
{
|
|
GdkVulkanContext *vulkan;
|
|
|
|
GskVulkanRenderOps render_ops;
|
|
|
|
GskVulkanImage *target;
|
|
graphene_rect_t viewport;
|
|
cairo_region_t *clip;
|
|
|
|
graphene_vec2_t scale;
|
|
|
|
VkRenderPass render_pass;
|
|
};
|
|
|
|
struct _GskVulkanParseState
|
|
{
|
|
cairo_rectangle_int_t scissor;
|
|
graphene_point_t offset;
|
|
graphene_vec2_t scale;
|
|
GskTransform *modelview;
|
|
graphene_matrix_t projection;
|
|
GskVulkanClip clip;
|
|
};
|
|
|
|
#ifdef G_ENABLE_DEBUG
|
|
static GQuark fallback_pixels_quark;
|
|
static GQuark texture_pixels_quark;
|
|
#endif
|
|
|
|
static void
|
|
gsk_vulkan_render_pass_seal (GskVulkanRenderPass *self)
|
|
{
|
|
GskVulkanOp *last, *op;
|
|
guint i;
|
|
|
|
last = (GskVulkanOp *) gsk_vulkan_render_ops_index (&self->render_ops, 0);
|
|
|
|
for (i = last->op_class->size; i < gsk_vulkan_render_ops_get_size (&self->render_ops); i += op->op_class->size)
|
|
{
|
|
op = (GskVulkanOp *) gsk_vulkan_render_ops_index (&self->render_ops, i);
|
|
|
|
last->next = op;
|
|
last = op;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gsk_vulkan_render_pass_add (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
GskRenderNode *node);
|
|
|
|
GskVulkanRenderPass *
|
|
gsk_vulkan_render_pass_new (GdkVulkanContext *context,
|
|
GskVulkanRender *render,
|
|
GskVulkanImage *target,
|
|
const graphene_vec2_t *scale,
|
|
const graphene_rect_t *viewport,
|
|
cairo_region_t *clip,
|
|
GskRenderNode *node,
|
|
gboolean is_root)
|
|
{
|
|
GskVulkanRenderPass *self;
|
|
|
|
self = g_new0 (GskVulkanRenderPass, 1);
|
|
self->vulkan = g_object_ref (context);
|
|
gsk_vulkan_render_ops_init (&self->render_ops);
|
|
|
|
self->target = g_object_ref (target);
|
|
self->clip = cairo_region_copy (clip);
|
|
self->viewport = *viewport;
|
|
graphene_vec2_init_from_vec2 (&self->scale, scale);
|
|
|
|
if (is_root)
|
|
{
|
|
/* this is a swapchain target */
|
|
self->render_pass = gsk_vulkan_render_get_render_pass (render,
|
|
gsk_vulkan_image_get_vk_format (target),
|
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
|
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
|
}
|
|
else
|
|
{
|
|
/* this is an offscreen */
|
|
self->render_pass = gsk_vulkan_render_get_render_pass (render,
|
|
gsk_vulkan_image_get_vk_format (target),
|
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
|
}
|
|
|
|
#ifdef G_ENABLE_DEBUG
|
|
if (fallback_pixels_quark == 0)
|
|
{
|
|
fallback_pixels_quark = g_quark_from_static_string ("fallback-pixels");
|
|
texture_pixels_quark = g_quark_from_static_string ("texture-pixels");
|
|
}
|
|
#endif
|
|
|
|
gsk_vulkan_render_pass_add (self, render, node);
|
|
|
|
gsk_vulkan_render_pass_seal (self);
|
|
|
|
return self;
|
|
}
|
|
|
|
void
|
|
gsk_vulkan_render_pass_free (GskVulkanRenderPass *self)
|
|
{
|
|
GskVulkanOp *op;
|
|
gsize i;
|
|
|
|
for (i = 0; i < gsk_vulkan_render_ops_get_size (&self->render_ops); i += op->op_class->size)
|
|
{
|
|
op = (GskVulkanOp *) gsk_vulkan_render_ops_index (&self->render_ops, i);
|
|
|
|
gsk_vulkan_op_finish (op);
|
|
}
|
|
gsk_vulkan_render_ops_clear (&self->render_ops);
|
|
|
|
g_object_unref (self->vulkan);
|
|
g_object_unref (self->target);
|
|
cairo_region_destroy (self->clip);
|
|
|
|
g_free (self);
|
|
}
|
|
|
|
void
|
|
gsk_vulkan_render_pass_print (GskVulkanRenderPass *self,
|
|
GString *string,
|
|
guint indent)
|
|
{
|
|
GskVulkanOp *op;
|
|
gsize i;
|
|
|
|
for (i = 0; i < gsk_vulkan_render_ops_get_size (&self->render_ops); i += op->op_class->size)
|
|
{
|
|
op = (GskVulkanOp *) gsk_vulkan_render_ops_index (&self->render_ops, i);
|
|
|
|
gsk_vulkan_op_print (op, string, indent);
|
|
}
|
|
}
|
|
|
|
gpointer
|
|
gsk_vulkan_render_pass_alloc_op (GskVulkanRenderPass *self,
|
|
gsize size)
|
|
{
|
|
gsize pos;
|
|
|
|
pos = gsk_vulkan_render_ops_get_size (&self->render_ops);
|
|
|
|
gsk_vulkan_render_ops_splice (&self->render_ops,
|
|
pos,
|
|
0, FALSE,
|
|
NULL,
|
|
size);
|
|
|
|
return gsk_vulkan_render_ops_index (&self->render_ops, pos);
|
|
}
|
|
|
|
static void
|
|
gsk_vulkan_render_pass_append_scissor (GskVulkanRenderPass *self,
|
|
GskRenderNode *node,
|
|
const GskVulkanParseState *state)
|
|
{
|
|
gsk_vulkan_scissor_op (self, &state->scissor);
|
|
}
|
|
|
|
static void
|
|
gsk_vulkan_render_pass_append_push_constants (GskVulkanRenderPass *self,
|
|
GskRenderNode *node,
|
|
const GskVulkanParseState *state)
|
|
{
|
|
graphene_matrix_t mvp;
|
|
|
|
if (state->modelview)
|
|
{
|
|
gsk_transform_to_matrix (state->modelview, &mvp);
|
|
graphene_matrix_multiply (&mvp, &state->projection, &mvp);
|
|
}
|
|
else
|
|
graphene_matrix_init_from_matrix (&mvp, &state->projection);
|
|
|
|
gsk_vulkan_push_constants_op (self, &state->scale, &mvp, &state->clip.rect);
|
|
}
|
|
|
|
#define FALLBACK(...) G_STMT_START { \
|
|
GSK_RENDERER_DEBUG (gsk_vulkan_render_get_renderer (render), FALLBACK, __VA_ARGS__); \
|
|
return FALSE; \
|
|
}G_STMT_END
|
|
|
|
static GskVulkanImage *
|
|
gsk_vulkan_render_pass_get_node_as_image (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node,
|
|
graphene_rect_t *tex_bounds)
|
|
{
|
|
GskVulkanImage *result;
|
|
|
|
switch ((guint) gsk_render_node_get_node_type (node))
|
|
{
|
|
case GSK_TEXTURE_NODE:
|
|
{
|
|
GdkTexture *texture = gsk_texture_node_get_texture (node);
|
|
GskVulkanRenderer *renderer = GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render));
|
|
result = gsk_vulkan_renderer_get_texture_image (renderer, texture);
|
|
if (result == NULL)
|
|
{
|
|
result = gsk_vulkan_upload_op (self, self->vulkan, texture);
|
|
gsk_vulkan_renderer_add_texture_image (renderer, texture, result);
|
|
}
|
|
|
|
*tex_bounds = node->bounds;
|
|
return result;
|
|
}
|
|
|
|
case GSK_CAIRO_NODE:
|
|
{
|
|
graphene_rect_t clipped;
|
|
|
|
graphene_rect_offset_r (&state->clip.rect.bounds, - state->offset.x, - state->offset.y, &clipped);
|
|
graphene_rect_intersection (&clipped, &node->bounds, &clipped);
|
|
|
|
if (clipped.size.width == 0 || clipped.size.height == 0)
|
|
return NULL;
|
|
|
|
result = gsk_vulkan_upload_cairo_op (self,
|
|
self->vulkan,
|
|
node,
|
|
&state->scale,
|
|
&clipped);
|
|
|
|
*tex_bounds = clipped;
|
|
return result;
|
|
}
|
|
|
|
default:
|
|
{
|
|
graphene_rect_t clipped;
|
|
|
|
graphene_rect_offset_r (&state->clip.rect.bounds, - state->offset.x, - state->offset.y, &clipped);
|
|
graphene_rect_intersection (&clipped, &node->bounds, &clipped);
|
|
|
|
if (clipped.size.width == 0 || clipped.size.height == 0)
|
|
return NULL;
|
|
|
|
/* assuming the unclipped bounds should go to texture coordinates 0..1,
|
|
* calculate the coordinates for the clipped texture size
|
|
*/
|
|
*tex_bounds = clipped;
|
|
|
|
result = gsk_vulkan_offscreen_op (self,
|
|
self->vulkan,
|
|
render,
|
|
&state->scale,
|
|
&clipped,
|
|
node);
|
|
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node);
|
|
|
|
static inline gboolean
|
|
gsk_vulkan_render_pass_add_fallback_node (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node)
|
|
{
|
|
GskVulkanImage *image;
|
|
graphene_rect_t clipped;
|
|
|
|
graphene_rect_offset_r (&state->clip.rect.bounds, - state->offset.x, - state->offset.y, &clipped);
|
|
graphene_rect_intersection (&clipped, &node->bounds, &clipped);
|
|
|
|
if (clipped.size.width == 0 || clipped.size.height == 0)
|
|
return TRUE;
|
|
|
|
image = gsk_vulkan_upload_cairo_op (self,
|
|
self->vulkan,
|
|
node,
|
|
&state->scale,
|
|
&clipped);
|
|
|
|
gsk_vulkan_texture_op (self,
|
|
gsk_vulkan_clip_get_clip_type (&state->clip, &state->offset, &node->bounds),
|
|
image,
|
|
GSK_VULKAN_SAMPLER_DEFAULT,
|
|
&node->bounds,
|
|
&state->offset,
|
|
&clipped);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static inline gboolean
|
|
gsk_vulkan_render_pass_implode (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node)
|
|
{
|
|
g_assert_not_reached ();
|
|
return TRUE;
|
|
}
|
|
|
|
static inline gboolean
|
|
gsk_vulkan_render_pass_add_container_node (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node)
|
|
{
|
|
for (guint i = 0; i < gsk_container_node_get_n_children (node); i++)
|
|
gsk_vulkan_render_pass_add_node (self, render, state, gsk_container_node_get_child (node, i));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static inline gboolean
|
|
gsk_vulkan_render_pass_add_cairo_node (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node)
|
|
{
|
|
/* We're using recording surfaces, so drawing them to an image
|
|
* surface and uploading them is the right thing.
|
|
* But that's exactly what the fallback code does.
|
|
*/
|
|
if (gsk_cairo_node_get_surface (node) != NULL)
|
|
return gsk_vulkan_render_pass_add_fallback_node (self, render, state, node);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static inline gboolean
|
|
gsk_vulkan_render_pass_add_color_node (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node)
|
|
{
|
|
gsk_vulkan_color_op (self,
|
|
gsk_vulkan_clip_get_clip_type (&state->clip, &state->offset, &node->bounds),
|
|
&node->bounds,
|
|
&state->offset,
|
|
gsk_color_node_get_color (node));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static inline gboolean
|
|
gsk_vulkan_render_pass_add_linear_gradient_node (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node)
|
|
{
|
|
gsk_vulkan_linear_gradient_op (self,
|
|
gsk_vulkan_clip_get_clip_type (&state->clip, &state->offset, &node->bounds),
|
|
&node->bounds,
|
|
&state->offset,
|
|
gsk_linear_gradient_node_get_start (node),
|
|
gsk_linear_gradient_node_get_end (node),
|
|
gsk_render_node_get_node_type (node) == GSK_REPEATING_LINEAR_GRADIENT_NODE,
|
|
gsk_linear_gradient_node_get_color_stops (node, NULL),
|
|
gsk_linear_gradient_node_get_n_color_stops (node));
|
|
return TRUE;
|
|
}
|
|
|
|
static inline gboolean
|
|
gsk_vulkan_render_pass_add_border_node (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node)
|
|
{
|
|
gsk_vulkan_border_op (self,
|
|
gsk_vulkan_clip_get_clip_type (&state->clip, &state->offset, &node->bounds),
|
|
gsk_border_node_get_outline (node),
|
|
&state->offset,
|
|
gsk_border_node_get_widths (node),
|
|
gsk_border_node_get_colors (node));
|
|
return TRUE;
|
|
}
|
|
|
|
static inline gboolean
|
|
gsk_vulkan_render_pass_add_texture_node (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node)
|
|
{
|
|
GskVulkanImage *image;
|
|
GskVulkanRenderer *renderer;
|
|
GdkTexture *texture;
|
|
|
|
renderer = GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render));
|
|
texture = gsk_texture_node_get_texture (node);
|
|
image = gsk_vulkan_renderer_get_texture_image (renderer, texture);
|
|
if (image == NULL)
|
|
{
|
|
image = gsk_vulkan_upload_op (self, self->vulkan, texture);
|
|
gsk_vulkan_renderer_add_texture_image (renderer, texture, image);
|
|
}
|
|
|
|
gsk_vulkan_texture_op (self,
|
|
gsk_vulkan_clip_get_clip_type (&state->clip, &state->offset, &node->bounds),
|
|
image,
|
|
GSK_VULKAN_SAMPLER_DEFAULT,
|
|
&node->bounds,
|
|
&state->offset,
|
|
&node->bounds);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static inline gboolean
|
|
gsk_vulkan_render_pass_add_texture_scale_node (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node)
|
|
{
|
|
GskVulkanImage *image;
|
|
GskVulkanRenderer *renderer;
|
|
GskVulkanRenderSampler sampler;
|
|
GdkTexture *texture;
|
|
|
|
renderer = GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render));
|
|
texture = gsk_texture_scale_node_get_texture (node);
|
|
switch (gsk_texture_scale_node_get_filter (node))
|
|
{
|
|
default:
|
|
g_assert_not_reached ();
|
|
case GSK_SCALING_FILTER_LINEAR:
|
|
case GSK_SCALING_FILTER_TRILINEAR:
|
|
sampler = GSK_VULKAN_SAMPLER_DEFAULT;
|
|
break;
|
|
case GSK_SCALING_FILTER_NEAREST:
|
|
sampler = GSK_VULKAN_SAMPLER_NEAREST;
|
|
break;
|
|
}
|
|
image = gsk_vulkan_renderer_get_texture_image (renderer, texture);
|
|
if (image == NULL)
|
|
{
|
|
image = gsk_vulkan_upload_op (self, self->vulkan, texture);
|
|
gsk_vulkan_renderer_add_texture_image (renderer, texture, image);
|
|
}
|
|
|
|
gsk_vulkan_texture_op (self,
|
|
gsk_vulkan_clip_get_clip_type (&state->clip, &state->offset, &node->bounds),
|
|
image,
|
|
sampler,
|
|
&node->bounds,
|
|
&state->offset,
|
|
&node->bounds);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static inline gboolean
|
|
gsk_vulkan_render_pass_add_inset_shadow_node (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node)
|
|
{
|
|
if (gsk_inset_shadow_node_get_blur_radius (node) > 0)
|
|
FALLBACK ("Blur support not implemented for inset shadows");
|
|
|
|
gsk_vulkan_inset_shadow_op (self,
|
|
gsk_vulkan_clip_get_clip_type (&state->clip, &state->offset, &node->bounds),
|
|
gsk_inset_shadow_node_get_outline (node),
|
|
&state->offset,
|
|
gsk_inset_shadow_node_get_color (node),
|
|
&GRAPHENE_POINT_INIT (gsk_inset_shadow_node_get_dx (node),
|
|
gsk_inset_shadow_node_get_dy (node)),
|
|
gsk_inset_shadow_node_get_spread (node),
|
|
gsk_inset_shadow_node_get_blur_radius (node));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static inline gboolean
|
|
gsk_vulkan_render_pass_add_outset_shadow_node (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node)
|
|
{
|
|
if (gsk_outset_shadow_node_get_blur_radius (node) > 0)
|
|
FALLBACK ("Blur support not implemented for outset shadows");
|
|
|
|
gsk_vulkan_outset_shadow_op (self,
|
|
gsk_vulkan_clip_get_clip_type (&state->clip, &state->offset, &node->bounds),
|
|
gsk_outset_shadow_node_get_outline (node),
|
|
&state->offset,
|
|
gsk_outset_shadow_node_get_color (node),
|
|
&GRAPHENE_POINT_INIT (gsk_outset_shadow_node_get_dx (node),
|
|
gsk_outset_shadow_node_get_dy (node)),
|
|
gsk_outset_shadow_node_get_spread (node),
|
|
gsk_outset_shadow_node_get_blur_radius (node));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static inline gboolean
|
|
gsk_vulkan_render_pass_add_transform_node (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node)
|
|
{
|
|
GskRenderNode *child;
|
|
GskTransform *transform;
|
|
GskVulkanParseState new_state;
|
|
|
|
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);
|
|
new_state = *state;
|
|
new_state.offset.x += dx;
|
|
new_state.offset.y += dy;
|
|
gsk_vulkan_render_pass_add_node (self, render, &new_state, child);
|
|
}
|
|
return TRUE;
|
|
|
|
case GSK_TRANSFORM_CATEGORY_2D_AFFINE:
|
|
{
|
|
float dx, dy, scale_x, scale_y;
|
|
gsk_transform_to_affine (transform, &scale_x, &scale_y, &dx, &dy);
|
|
gsk_vulkan_clip_scale (&new_state.clip, &state->clip, scale_x, scale_y);
|
|
new_state.offset.x = (state->offset.x + dx) / scale_x;
|
|
new_state.offset.y = (state->offset.y + dy) / scale_y;
|
|
graphene_vec2_init (&new_state.scale, fabs (scale_x), fabs (scale_y));
|
|
graphene_vec2_multiply (&new_state.scale, &state->scale, &new_state.scale);
|
|
new_state.modelview = gsk_transform_scale (gsk_transform_ref (state->modelview),
|
|
scale_x / fabs (scale_x),
|
|
scale_y / fabs (scale_y));
|
|
}
|
|
break;
|
|
|
|
case GSK_TRANSFORM_CATEGORY_2D:
|
|
{
|
|
float skew_x, skew_y, scale_x, scale_y, angle, dx, dy;
|
|
GskTransform *clip_transform;
|
|
|
|
clip_transform = gsk_transform_transform (gsk_transform_translate (NULL, &state->offset), transform);
|
|
|
|
if (gsk_vulkan_clip_contains_rect (&state->clip, &state->offset, &node->bounds))
|
|
{
|
|
gsk_vulkan_clip_init_empty (&new_state.clip, &child->bounds);
|
|
}
|
|
else if (!gsk_vulkan_clip_transform (&new_state.clip, &state->clip, clip_transform, &child->bounds))
|
|
{
|
|
gsk_transform_unref (clip_transform);
|
|
FALLBACK ("Transform nodes can't deal with clip type %u", state->clip.type);
|
|
}
|
|
|
|
new_state.modelview = gsk_transform_ref (state->modelview);
|
|
new_state.modelview = gsk_transform_scale (state->modelview,
|
|
graphene_vec2_get_x (&state->scale),
|
|
graphene_vec2_get_y (&state->scale));
|
|
new_state.modelview = gsk_transform_transform (new_state.modelview, clip_transform);
|
|
gsk_transform_unref (clip_transform);
|
|
|
|
gsk_transform_to_2d_components (new_state.modelview,
|
|
&skew_x, &skew_y,
|
|
&scale_x, &scale_y,
|
|
&angle,
|
|
&dx, &dy);
|
|
scale_x = fabs (scale_x);
|
|
scale_y = fabs (scale_y);
|
|
new_state.modelview = gsk_transform_scale (new_state.modelview, 1 / scale_x, 1 / scale_y);
|
|
graphene_vec2_init (&new_state.scale, scale_x, scale_y);
|
|
new_state.offset = *graphene_point_zero ();
|
|
}
|
|
break;
|
|
|
|
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;
|
|
GskTransform *clip_transform;
|
|
float scale_x, scale_y, old_pixels, new_pixels;
|
|
|
|
clip_transform = gsk_transform_transform (gsk_transform_translate (NULL, &state->offset), transform);
|
|
|
|
if (gsk_vulkan_clip_contains_rect (&state->clip, &state->offset, &node->bounds))
|
|
{
|
|
gsk_vulkan_clip_init_empty (&new_state.clip, &child->bounds);
|
|
}
|
|
else if (!gsk_vulkan_clip_transform (&new_state.clip, &state->clip, clip_transform, &child->bounds))
|
|
{
|
|
gsk_transform_unref (clip_transform);
|
|
FALLBACK ("Transform nodes can't deal with clip type %u", state->clip.type);
|
|
}
|
|
|
|
new_state.modelview = gsk_transform_ref (state->modelview);
|
|
new_state.modelview = gsk_transform_scale (state->modelview,
|
|
graphene_vec2_get_x (&state->scale),
|
|
graphene_vec2_get_y (&state->scale));
|
|
new_state.modelview = gsk_transform_transform (new_state.modelview, clip_transform);
|
|
gsk_transform_unref (clip_transform);
|
|
|
|
gsk_transform_to_matrix (new_state.modelview, &matrix);
|
|
graphene_matrix_decompose (&matrix,
|
|
&translation,
|
|
&matrix_scale,
|
|
&rotation,
|
|
&shear,
|
|
&perspective);
|
|
|
|
scale_x = fabs (graphene_vec3_get_x (&matrix_scale));
|
|
scale_y = fabs (graphene_vec3_get_y (&matrix_scale));
|
|
old_pixels = graphene_vec2_get_x (&state->scale) * graphene_vec2_get_y (&state->scale) *
|
|
state->clip.rect.bounds.size.width * state->clip.rect.bounds.size.height;
|
|
new_pixels = scale_x * scale_y * new_state.clip.rect.bounds.size.width * new_state.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;
|
|
}
|
|
new_state.modelview = gsk_transform_scale (new_state.modelview, 1 / scale_x, 1 / scale_y);
|
|
graphene_vec2_init (&new_state.scale, scale_x, scale_y);
|
|
new_state.offset = *graphene_point_zero ();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
new_state.scissor = state->scissor;
|
|
graphene_matrix_init_from_matrix (&new_state.projection, &state->projection);
|
|
|
|
gsk_vulkan_render_pass_append_push_constants (self, node, &new_state);
|
|
|
|
gsk_vulkan_render_pass_add_node (self, render, &new_state, child);
|
|
|
|
gsk_vulkan_render_pass_append_push_constants (self, node, state);
|
|
|
|
gsk_transform_unref (new_state.modelview);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gsk_vulkan_render_pass_add_opacity_node (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node)
|
|
{
|
|
GskVulkanImage *image;
|
|
graphene_rect_t tex_rect;
|
|
|
|
image = gsk_vulkan_render_pass_get_node_as_image (self,
|
|
render,
|
|
state,
|
|
gsk_opacity_node_get_child (node),
|
|
&tex_rect);
|
|
if (image == NULL)
|
|
return TRUE;
|
|
|
|
gsk_vulkan_color_matrix_op_opacity (self,
|
|
gsk_vulkan_clip_get_clip_type (&state->clip, &state->offset, &node->bounds),
|
|
image,
|
|
&node->bounds,
|
|
&state->offset,
|
|
&tex_rect,
|
|
gsk_opacity_node_get_opacity (node));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static inline gboolean
|
|
gsk_vulkan_render_pass_add_color_matrix_node (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node)
|
|
{
|
|
GskVulkanImage *image;
|
|
graphene_rect_t tex_rect;
|
|
|
|
image = gsk_vulkan_render_pass_get_node_as_image (self,
|
|
render,
|
|
state,
|
|
gsk_color_matrix_node_get_child (node),
|
|
&tex_rect);
|
|
if (image == NULL)
|
|
return TRUE;
|
|
|
|
gsk_vulkan_color_matrix_op (self,
|
|
gsk_vulkan_clip_get_clip_type (&state->clip, &state->offset, &node->bounds),
|
|
image,
|
|
&node->bounds,
|
|
&state->offset,
|
|
&tex_rect,
|
|
gsk_color_matrix_node_get_color_matrix (node),
|
|
gsk_color_matrix_node_get_color_offset (node));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
clip_can_be_scissored (const graphene_rect_t *rect,
|
|
const graphene_vec2_t *scale,
|
|
GskTransform *modelview,
|
|
cairo_rectangle_int_t *int_rect)
|
|
{
|
|
graphene_rect_t transformed_rect;
|
|
float scale_x = graphene_vec2_get_x (scale);
|
|
float scale_y = graphene_vec2_get_y (scale);
|
|
|
|
switch (gsk_transform_get_category (modelview))
|
|
{
|
|
case GSK_TRANSFORM_CATEGORY_UNKNOWN:
|
|
case GSK_TRANSFORM_CATEGORY_ANY:
|
|
case GSK_TRANSFORM_CATEGORY_3D:
|
|
case GSK_TRANSFORM_CATEGORY_2D:
|
|
return FALSE;
|
|
|
|
case GSK_TRANSFORM_CATEGORY_2D_AFFINE:
|
|
case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE:
|
|
gsk_transform_transform_bounds (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 inline gboolean
|
|
gsk_vulkan_render_pass_add_clip_node (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node)
|
|
{
|
|
GskVulkanParseState new_state;
|
|
graphene_rect_t clip;
|
|
gboolean do_push_constants, do_scissor;
|
|
|
|
graphene_rect_offset_r (gsk_clip_node_get_clip (node),
|
|
state->offset.x, state->offset.y,
|
|
&clip);
|
|
|
|
/* Check if we can use scissoring for the clip */
|
|
if (clip_can_be_scissored (&clip, &state->scale, state->modelview, &new_state.scissor))
|
|
{
|
|
if (!gdk_rectangle_intersect (&new_state.scissor, &state->scissor, &new_state.scissor))
|
|
return TRUE;
|
|
|
|
if (gsk_vulkan_clip_intersect_rect (&new_state.clip, &state->clip, &clip))
|
|
{
|
|
if (new_state.clip.type == GSK_VULKAN_CLIP_RECT)
|
|
new_state.clip.type = GSK_VULKAN_CLIP_NONE;
|
|
|
|
do_push_constants = TRUE;
|
|
}
|
|
else
|
|
{
|
|
gsk_vulkan_clip_init_copy (&new_state.clip, &state->clip);
|
|
do_push_constants = FALSE;
|
|
}
|
|
|
|
do_scissor = TRUE;
|
|
}
|
|
else
|
|
{
|
|
if (!gsk_vulkan_clip_intersect_rect (&new_state.clip, &state->clip, &clip))
|
|
FALLBACK ("Failed to find intersection between clip of type %u and rectangle", state->clip.type);
|
|
|
|
new_state.scissor = state->scissor;
|
|
|
|
do_push_constants = TRUE;
|
|
do_scissor = FALSE;
|
|
}
|
|
|
|
if (new_state.clip.type == GSK_VULKAN_CLIP_ALL_CLIPPED)
|
|
return TRUE;
|
|
|
|
new_state.offset = state->offset;
|
|
graphene_vec2_init_from_vec2 (&new_state.scale, &state->scale);
|
|
new_state.modelview = state->modelview;
|
|
graphene_matrix_init_from_matrix (&new_state.projection, &state->projection);
|
|
|
|
if (do_scissor)
|
|
gsk_vulkan_render_pass_append_scissor (self, node, &new_state);
|
|
if (do_push_constants)
|
|
gsk_vulkan_render_pass_append_push_constants (self, node, &new_state);
|
|
|
|
gsk_vulkan_render_pass_add_node (self, render, &new_state, gsk_clip_node_get_child (node));
|
|
|
|
if (do_push_constants)
|
|
gsk_vulkan_render_pass_append_push_constants (self, node, state);
|
|
if (do_scissor)
|
|
gsk_vulkan_render_pass_append_scissor (self, node, state);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static inline gboolean
|
|
gsk_vulkan_render_pass_add_rounded_clip_node (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node)
|
|
{
|
|
GskVulkanParseState new_state;
|
|
GskRoundedRect clip;
|
|
|
|
clip = *gsk_rounded_clip_node_get_clip (node);
|
|
gsk_rounded_rect_offset (&clip, state->offset.x, state->offset.y);
|
|
|
|
if (!gsk_vulkan_clip_intersect_rounded_rect (&new_state.clip, &state->clip, &clip))
|
|
FALLBACK ("Failed to find intersection between clip of type %u and rounded rectangle", state->clip.type);
|
|
|
|
if (new_state.clip.type == GSK_VULKAN_CLIP_ALL_CLIPPED)
|
|
return TRUE;
|
|
|
|
new_state.scissor = state->scissor;
|
|
new_state.offset = state->offset;
|
|
graphene_vec2_init_from_vec2 (&new_state.scale, &state->scale);
|
|
new_state.modelview = state->modelview;
|
|
graphene_matrix_init_from_matrix (&new_state.projection, &state->projection);
|
|
|
|
gsk_vulkan_render_pass_append_push_constants (self, node, &new_state);
|
|
|
|
gsk_vulkan_render_pass_add_node (self, render, &new_state, gsk_rounded_clip_node_get_child (node));
|
|
|
|
gsk_vulkan_render_pass_append_push_constants (self, node, state);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static inline gboolean
|
|
gsk_vulkan_render_pass_add_repeat_node (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node)
|
|
{
|
|
const graphene_rect_t *child_bounds;
|
|
GskVulkanImage *image;
|
|
|
|
child_bounds = gsk_repeat_node_get_child_bounds (node);
|
|
|
|
if (graphene_rect_get_area (child_bounds) == 0)
|
|
return TRUE;
|
|
|
|
image = gsk_vulkan_offscreen_op (self,
|
|
self->vulkan,
|
|
render,
|
|
&state->scale,
|
|
child_bounds,
|
|
gsk_repeat_node_get_child (node));
|
|
|
|
gsk_vulkan_texture_op (self,
|
|
gsk_vulkan_clip_get_clip_type (&state->clip, &state->offset, &node->bounds),
|
|
image,
|
|
GSK_VULKAN_SAMPLER_REPEAT,
|
|
&node->bounds,
|
|
&state->offset,
|
|
child_bounds);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static inline gboolean
|
|
gsk_vulkan_render_pass_add_blend_node (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node)
|
|
{
|
|
GskRenderNode *top_child, *bottom_child;
|
|
GskVulkanImage *top_image, *bottom_image;
|
|
graphene_rect_t top_tex_rect, bottom_tex_rect;
|
|
|
|
top_child = gsk_blend_node_get_top_child (node);
|
|
top_image = gsk_vulkan_render_pass_get_node_as_image (self,
|
|
render,
|
|
state,
|
|
top_child,
|
|
&top_tex_rect);
|
|
bottom_child = gsk_blend_node_get_bottom_child (node);
|
|
bottom_image = gsk_vulkan_render_pass_get_node_as_image (self,
|
|
render,
|
|
state,
|
|
bottom_child,
|
|
&bottom_tex_rect);
|
|
if (top_image == NULL)
|
|
{
|
|
if (bottom_image == NULL)
|
|
return TRUE;
|
|
|
|
top_image = bottom_image;
|
|
top_tex_rect = *graphene_rect_zero ();
|
|
}
|
|
else if (bottom_image == NULL)
|
|
{
|
|
bottom_image = top_image;
|
|
bottom_tex_rect = *graphene_rect_zero ();
|
|
}
|
|
|
|
gsk_vulkan_blend_mode_op (self,
|
|
gsk_vulkan_clip_get_clip_type (&state->clip, &state->offset, &node->bounds),
|
|
&node->bounds,
|
|
&state->offset,
|
|
gsk_blend_node_get_blend_mode (node),
|
|
top_image,
|
|
&top_child->bounds,
|
|
&top_tex_rect,
|
|
bottom_image,
|
|
&bottom_child->bounds,
|
|
&bottom_tex_rect);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static inline gboolean
|
|
gsk_vulkan_render_pass_add_cross_fade_node (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node)
|
|
{
|
|
GskRenderNode *start_child, *end_child;
|
|
GskVulkanImage *start_image, *end_image;
|
|
graphene_rect_t start_tex_rect, end_tex_rect;
|
|
float progress;
|
|
|
|
progress = gsk_cross_fade_node_get_progress (node);
|
|
start_child = gsk_cross_fade_node_get_start_child (node);
|
|
start_image = gsk_vulkan_render_pass_get_node_as_image (self,
|
|
render,
|
|
state,
|
|
start_child,
|
|
&start_tex_rect);
|
|
end_child = gsk_cross_fade_node_get_end_child (node);
|
|
end_image = gsk_vulkan_render_pass_get_node_as_image (self,
|
|
render,
|
|
state,
|
|
end_child,
|
|
&end_tex_rect);
|
|
if (start_image == NULL)
|
|
{
|
|
if (end_image == NULL)
|
|
return TRUE;
|
|
|
|
gsk_vulkan_color_matrix_op_opacity (self,
|
|
gsk_vulkan_clip_get_clip_type (&state->clip, &state->offset, &end_child->bounds),
|
|
end_image,
|
|
&node->bounds,
|
|
&state->offset,
|
|
&end_tex_rect,
|
|
progress);
|
|
|
|
return TRUE;
|
|
}
|
|
else if (end_image == NULL)
|
|
{
|
|
gsk_vulkan_color_matrix_op_opacity (self,
|
|
gsk_vulkan_clip_get_clip_type (&state->clip, &state->offset, &start_child->bounds),
|
|
start_image,
|
|
&node->bounds,
|
|
&state->offset,
|
|
&start_tex_rect,
|
|
1.0f - progress);
|
|
return TRUE;
|
|
}
|
|
|
|
gsk_vulkan_cross_fade_op (self,
|
|
gsk_vulkan_clip_get_clip_type (&state->clip, &state->offset, &node->bounds),
|
|
&node->bounds,
|
|
&state->offset,
|
|
progress,
|
|
start_image,
|
|
&start_child->bounds,
|
|
&start_tex_rect,
|
|
end_image,
|
|
&end_child->bounds,
|
|
&end_tex_rect);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static inline gboolean
|
|
gsk_vulkan_render_pass_add_text_node (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node)
|
|
{
|
|
const PangoGlyphInfo *glyphs;
|
|
GskVulkanRenderer *renderer;
|
|
const graphene_point_t *node_offset;
|
|
const PangoFont *font;
|
|
guint num_glyphs;
|
|
int x_position;
|
|
int i;
|
|
float scale;
|
|
|
|
renderer = GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render));
|
|
num_glyphs = gsk_text_node_get_num_glyphs (node);
|
|
glyphs = gsk_text_node_get_glyphs (node, NULL);
|
|
font = gsk_text_node_get_font (node);
|
|
|
|
|
|
scale = MAX (graphene_vec2_get_x (&state->scale), graphene_vec2_get_y (&state->scale));
|
|
node_offset = gsk_text_node_get_offset (node);
|
|
|
|
x_position = 0;
|
|
for (i = 0; i < num_glyphs; i++)
|
|
{
|
|
GskVulkanCachedGlyph *glyph;
|
|
const PangoGlyphInfo *gi = &glyphs[i];
|
|
graphene_rect_t glyph_bounds, glyph_tex_rect;
|
|
|
|
glyph = gsk_vulkan_renderer_cache_glyph (renderer,
|
|
(PangoFont *)font,
|
|
gi->glyph,
|
|
x_position + gi->geometry.x_offset,
|
|
gi->geometry.y_offset,
|
|
scale);
|
|
|
|
glyph_bounds = GRAPHENE_RECT_INIT (glyph->draw_x + node_offset->x + (float) (x_position + gi->geometry.x_offset) / PANGO_SCALE,
|
|
glyph->draw_y + node_offset->y + (float) (gi->geometry.y_offset) / PANGO_SCALE,
|
|
glyph->draw_width,
|
|
glyph->draw_height);
|
|
graphene_rect_init (&glyph_tex_rect,
|
|
glyph_bounds.origin.x - glyph->draw_width * glyph->tx / glyph->tw,
|
|
glyph_bounds.origin.y - glyph->draw_height * glyph->ty / glyph->th,
|
|
glyph->draw_width / glyph->tw,
|
|
glyph->draw_height / glyph->th);
|
|
if (gsk_text_node_has_color_glyphs (node))
|
|
gsk_vulkan_texture_op (self,
|
|
gsk_vulkan_clip_get_clip_type (&state->clip, &state->offset, &glyph_bounds),
|
|
glyph->atlas_image,
|
|
GSK_VULKAN_SAMPLER_DEFAULT,
|
|
&glyph_bounds,
|
|
&state->offset,
|
|
&glyph_tex_rect);
|
|
else
|
|
gsk_vulkan_glyph_op (self,
|
|
gsk_vulkan_clip_get_clip_type (&state->clip, &state->offset, &glyph_bounds),
|
|
glyph->atlas_image,
|
|
&glyph_bounds,
|
|
&state->offset,
|
|
&glyph_tex_rect,
|
|
gsk_text_node_get_color (node));
|
|
|
|
x_position += gi->geometry.width;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static inline gboolean
|
|
gsk_vulkan_render_pass_add_blur_node (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node)
|
|
{
|
|
GskVulkanImage *image;
|
|
graphene_rect_t tex_rect;
|
|
float radius;
|
|
|
|
radius = gsk_blur_node_get_radius (node);
|
|
if (radius == 0)
|
|
{
|
|
gsk_vulkan_render_pass_add_node (self, render, state, gsk_blur_node_get_child (node));
|
|
return TRUE;
|
|
}
|
|
|
|
image = gsk_vulkan_render_pass_get_node_as_image (self,
|
|
render,
|
|
state,
|
|
gsk_blur_node_get_child (node),
|
|
&tex_rect);
|
|
if (image == NULL)
|
|
return TRUE;
|
|
|
|
gsk_vulkan_blur_op (self,
|
|
gsk_vulkan_clip_get_clip_type (&state->clip, &state->offset, &node->bounds),
|
|
image,
|
|
&node->bounds,
|
|
&state->offset,
|
|
&tex_rect,
|
|
radius);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static inline gboolean
|
|
gsk_vulkan_render_pass_add_mask_node (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node)
|
|
{
|
|
GskVulkanImage *source_image, *mask_image;
|
|
graphene_rect_t source_tex_rect, mask_tex_rect;
|
|
GskRenderNode *source, *mask;
|
|
GskMaskMode mode;
|
|
|
|
mode = gsk_mask_node_get_mask_mode (node);
|
|
source = gsk_mask_node_get_source (node);
|
|
mask = gsk_mask_node_get_mask (node);
|
|
mask_image = gsk_vulkan_render_pass_get_node_as_image (self,
|
|
render,
|
|
state,
|
|
mask,
|
|
&mask_tex_rect);
|
|
if (mask_image == NULL)
|
|
{
|
|
if (mode == GSK_MASK_MODE_INVERTED_ALPHA)
|
|
gsk_vulkan_render_pass_add_node (self, render, state, source);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Use the glyph shader as an optimization */
|
|
if (mode == GSK_MASK_MODE_ALPHA &&
|
|
gsk_render_node_get_node_type (source) == GSK_COLOR_NODE)
|
|
{
|
|
graphene_rect_t bounds;
|
|
if (graphene_rect_intersection (&source->bounds, &mask->bounds, &bounds))
|
|
gsk_vulkan_glyph_op (self,
|
|
gsk_vulkan_clip_get_clip_type (&state->clip, &state->offset, &bounds),
|
|
mask_image,
|
|
&bounds,
|
|
&state->offset,
|
|
&mask_tex_rect,
|
|
gsk_color_node_get_color (source));
|
|
return TRUE;
|
|
}
|
|
|
|
source_image = gsk_vulkan_render_pass_get_node_as_image (self,
|
|
render,
|
|
state,
|
|
source,
|
|
&source_tex_rect);
|
|
if (source_image == NULL)
|
|
return TRUE;
|
|
|
|
gsk_vulkan_mask_op (self,
|
|
gsk_vulkan_clip_get_clip_type (&state->clip, &state->offset, &node->bounds),
|
|
&state->offset,
|
|
source_image,
|
|
&source->bounds,
|
|
&source_tex_rect,
|
|
mask_image,
|
|
&mask->bounds,
|
|
&mask_tex_rect,
|
|
mode);
|
|
return TRUE;
|
|
}
|
|
|
|
static inline gboolean
|
|
gsk_vulkan_render_pass_add_debug_node (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node)
|
|
{
|
|
gsk_vulkan_render_pass_add_node (self, render, state, gsk_debug_node_get_child (node));
|
|
return TRUE;
|
|
}
|
|
|
|
#undef FALLBACK
|
|
|
|
typedef gboolean (*GskVulkanRenderPassNodeFunc) (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node);
|
|
|
|
/* TODO: implement remaining nodes */
|
|
static const GskVulkanRenderPassNodeFunc nodes_vtable[] = {
|
|
[GSK_NOT_A_RENDER_NODE] = gsk_vulkan_render_pass_implode,
|
|
[GSK_CONTAINER_NODE] = gsk_vulkan_render_pass_add_container_node,
|
|
[GSK_CAIRO_NODE] = gsk_vulkan_render_pass_add_cairo_node,
|
|
[GSK_COLOR_NODE] = gsk_vulkan_render_pass_add_color_node,
|
|
[GSK_LINEAR_GRADIENT_NODE] = gsk_vulkan_render_pass_add_linear_gradient_node,
|
|
[GSK_REPEATING_LINEAR_GRADIENT_NODE] = gsk_vulkan_render_pass_add_linear_gradient_node,
|
|
[GSK_RADIAL_GRADIENT_NODE] = NULL,
|
|
[GSK_REPEATING_RADIAL_GRADIENT_NODE] = NULL,
|
|
[GSK_CONIC_GRADIENT_NODE] = NULL,
|
|
[GSK_BORDER_NODE] = gsk_vulkan_render_pass_add_border_node,
|
|
[GSK_TEXTURE_NODE] = gsk_vulkan_render_pass_add_texture_node,
|
|
[GSK_INSET_SHADOW_NODE] = gsk_vulkan_render_pass_add_inset_shadow_node,
|
|
[GSK_OUTSET_SHADOW_NODE] = gsk_vulkan_render_pass_add_outset_shadow_node,
|
|
[GSK_TRANSFORM_NODE] = gsk_vulkan_render_pass_add_transform_node,
|
|
[GSK_OPACITY_NODE] = gsk_vulkan_render_pass_add_opacity_node,
|
|
[GSK_COLOR_MATRIX_NODE] = gsk_vulkan_render_pass_add_color_matrix_node,
|
|
[GSK_REPEAT_NODE] = gsk_vulkan_render_pass_add_repeat_node,
|
|
[GSK_CLIP_NODE] = gsk_vulkan_render_pass_add_clip_node,
|
|
[GSK_ROUNDED_CLIP_NODE] = gsk_vulkan_render_pass_add_rounded_clip_node,
|
|
[GSK_SHADOW_NODE] = NULL,
|
|
[GSK_BLEND_NODE] = gsk_vulkan_render_pass_add_blend_node,
|
|
[GSK_CROSS_FADE_NODE] = gsk_vulkan_render_pass_add_cross_fade_node,
|
|
[GSK_TEXT_NODE] = gsk_vulkan_render_pass_add_text_node,
|
|
[GSK_BLUR_NODE] = gsk_vulkan_render_pass_add_blur_node,
|
|
[GSK_DEBUG_NODE] = gsk_vulkan_render_pass_add_debug_node,
|
|
[GSK_GL_SHADER_NODE] = NULL,
|
|
[GSK_TEXTURE_SCALE_NODE] = gsk_vulkan_render_pass_add_texture_scale_node,
|
|
[GSK_MASK_NODE] = gsk_vulkan_render_pass_add_mask_node,
|
|
};
|
|
|
|
static void
|
|
gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
const GskVulkanParseState *state,
|
|
GskRenderNode *node)
|
|
{
|
|
GskVulkanRenderPassNodeFunc node_func;
|
|
GskRenderNodeType node_type;
|
|
gboolean fallback = FALSE;
|
|
|
|
/* 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 (!gsk_vulkan_clip_may_intersect_rect (&state->clip, &state->offset, &node->bounds))
|
|
return;
|
|
|
|
node_type = gsk_render_node_get_node_type (node);
|
|
if (node_type < G_N_ELEMENTS (nodes_vtable))
|
|
node_func = nodes_vtable[node_type];
|
|
else
|
|
node_func = NULL;
|
|
|
|
if (node_func)
|
|
{
|
|
if (!node_func (self, render, state, node))
|
|
fallback = TRUE;
|
|
}
|
|
else
|
|
{
|
|
GSK_RENDERER_DEBUG (gsk_vulkan_render_get_renderer (render),
|
|
FALLBACK, "Unsupported node '%s'",
|
|
g_type_name_from_instance ((GTypeInstance *) node));
|
|
fallback = TRUE;
|
|
}
|
|
|
|
if (fallback)
|
|
gsk_vulkan_render_pass_add_fallback_node (self, render, state, node);
|
|
}
|
|
|
|
static void
|
|
gsk_vulkan_render_pass_add (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
GskRenderNode *node)
|
|
{
|
|
GskVulkanParseState state;
|
|
graphene_rect_t clip;
|
|
float scale_x, scale_y;
|
|
|
|
scale_x = 1 / graphene_vec2_get_x (&self->scale);
|
|
scale_y = 1 / graphene_vec2_get_y (&self->scale);
|
|
cairo_region_get_extents (self->clip, &state.scissor);
|
|
clip = GRAPHENE_RECT_INIT(state.scissor.x, state.scissor.y,
|
|
state.scissor.width, state.scissor.height);
|
|
graphene_rect_scale (&clip, scale_x, scale_y, &clip);
|
|
gsk_vulkan_clip_init_empty (&state.clip, &clip);
|
|
|
|
state.modelview = NULL;
|
|
graphene_matrix_init_ortho (&state.projection,
|
|
0, self->viewport.size.width,
|
|
0, self->viewport.size.height,
|
|
2 * ORTHO_NEAR_PLANE - ORTHO_FAR_PLANE,
|
|
ORTHO_FAR_PLANE);
|
|
graphene_vec2_init_from_vec2 (&state.scale, &self->scale);
|
|
state.offset = GRAPHENE_POINT_INIT (-self->viewport.origin.x * scale_x,
|
|
-self->viewport.origin.y * scale_y);
|
|
|
|
gsk_vulkan_render_pass_append_scissor (self, node, &state);
|
|
gsk_vulkan_render_pass_append_push_constants (self, node, &state);
|
|
|
|
gsk_vulkan_render_pass_add_node (self, render, &state, node);
|
|
}
|
|
|
|
static GskVulkanOp *
|
|
gsk_vulkan_render_pass_get_first_op (GskVulkanRenderPass *self)
|
|
{
|
|
if (gsk_vulkan_render_ops_get_size (&self->render_ops) == 0)
|
|
return NULL;
|
|
|
|
return (GskVulkanOp *) gsk_vulkan_render_ops_index (&self->render_ops, 0);
|
|
}
|
|
|
|
void
|
|
gsk_vulkan_render_pass_upload (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
GskVulkanUploader *uploader)
|
|
{
|
|
GskVulkanOp *op;
|
|
|
|
for (op = gsk_vulkan_render_pass_get_first_op (self); op; op = op->next)
|
|
{
|
|
gsk_vulkan_op_upload (op, self, render, uploader);
|
|
}
|
|
}
|
|
|
|
gsize
|
|
gsk_vulkan_render_pass_count_vertex_data (GskVulkanRenderPass *self,
|
|
gsize n_bytes)
|
|
{
|
|
GskVulkanOp *op;
|
|
|
|
for (op = gsk_vulkan_render_pass_get_first_op (self); op; op = op->next)
|
|
{
|
|
n_bytes = gsk_vulkan_op_count_vertex_data (op, n_bytes);
|
|
}
|
|
|
|
return n_bytes;
|
|
}
|
|
|
|
void
|
|
gsk_vulkan_render_pass_collect_vertex_data (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
guchar *data)
|
|
{
|
|
GskVulkanOp *op;
|
|
|
|
for (op = gsk_vulkan_render_pass_get_first_op (self); op; op = op->next)
|
|
{
|
|
gsk_vulkan_op_collect_vertex_data (op, self, render, data);
|
|
}
|
|
}
|
|
|
|
void
|
|
gsk_vulkan_render_pass_reserve_descriptor_sets (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render)
|
|
{
|
|
GskVulkanOp *op;
|
|
|
|
for (op = gsk_vulkan_render_pass_get_first_op (self); op; op = op->next)
|
|
{
|
|
gsk_vulkan_op_reserve_descriptor_sets (op, render);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gsk_vulkan_render_pass_draw_rect (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
VkPipelineLayout pipeline_layout,
|
|
VkCommandBuffer command_buffer)
|
|
{
|
|
VkPipeline current_pipeline = VK_NULL_HANDLE;
|
|
const GskVulkanOpClass *current_pipeline_class = NULL;
|
|
const char *current_pipeline_clip_type = NULL;
|
|
GskVulkanOp *op;
|
|
|
|
for (op = gsk_vulkan_render_pass_get_first_op (self); op; op = op->next)
|
|
{
|
|
if (op->op_class->shader_name &&
|
|
(op->op_class != current_pipeline_class ||
|
|
current_pipeline_clip_type != op->clip_type))
|
|
{
|
|
current_pipeline = gsk_vulkan_render_get_pipeline (render,
|
|
op->op_class,
|
|
op->clip_type,
|
|
gsk_vulkan_image_get_vk_format (self->target),
|
|
self->render_pass);
|
|
vkCmdBindPipeline (command_buffer,
|
|
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
current_pipeline);
|
|
current_pipeline_class = op->op_class;
|
|
current_pipeline_clip_type = op->clip_type;
|
|
}
|
|
|
|
gsk_vulkan_op_command (op, render, pipeline_layout, command_buffer);
|
|
}
|
|
}
|
|
|
|
void
|
|
gsk_vulkan_render_pass_draw (GskVulkanRenderPass *self,
|
|
GskVulkanRender *render,
|
|
VkPipelineLayout pipeline_layout,
|
|
VkCommandBuffer command_buffer)
|
|
{
|
|
cairo_rectangle_int_t rect;
|
|
|
|
vkCmdSetViewport (command_buffer,
|
|
0,
|
|
1,
|
|
&(VkViewport) {
|
|
.x = 0,
|
|
.y = 0,
|
|
.width = self->viewport.size.width,
|
|
.height = self->viewport.size.height,
|
|
.minDepth = 0,
|
|
.maxDepth = 1
|
|
});
|
|
|
|
cairo_region_get_extents (self->clip, &rect);
|
|
|
|
vkCmdBeginRenderPass (command_buffer,
|
|
&(VkRenderPassBeginInfo) {
|
|
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
|
.renderPass = self->render_pass,
|
|
.framebuffer = gsk_vulkan_image_get_framebuffer (self->target,
|
|
self->render_pass),
|
|
.renderArea = {
|
|
{ rect.x, rect.y },
|
|
{ rect.width, rect.height }
|
|
},
|
|
.clearValueCount = 1,
|
|
.pClearValues = (VkClearValue [1]) {
|
|
{ .color = { .float32 = { 0.f, 0.f, 0.f, 0.f } } }
|
|
}
|
|
},
|
|
VK_SUBPASS_CONTENTS_INLINE);
|
|
|
|
gsk_vulkan_render_bind_descriptor_sets (render, command_buffer);
|
|
|
|
gsk_vulkan_render_pass_draw_rect (self, render, pipeline_layout, command_buffer);
|
|
|
|
vkCmdEndRenderPass (command_buffer);
|
|
}
|
|
|