mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-26 13:41:07 +00:00
70c9521cae
Renderpasses get recreated every frame, but we keep render objects around. So if we keep the vertex buffer in the render object, we can also keep it around and just reuse it. Also, we only need one buffer for all the render passes, which is another bonus. The initial buffer size is chosen at 128kB. Maximized Nautilus, gnome-text-editor with an open file and widget-factory take ~100kB when doing a full redraw. Other apps are between 30-50kB usually. So I chose a value that is not too big, but catches ~90% of cases.
1530 lines
59 KiB
C
1530 lines
59 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;
|
|
VkFramebuffer framebuffer;
|
|
};
|
|
|
|
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;
|
|
VkImageLayout final_layout;
|
|
|
|
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 dependent pass
|
|
final_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
else
|
|
final_layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
|
GSK_VK_CHECK (vkCreateRenderPass, gdk_vulkan_context_get_device (self->vulkan),
|
|
&(VkRenderPassCreateInfo) {
|
|
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
|
.attachmentCount = 1,
|
|
.pAttachments = (VkAttachmentDescription[]) {
|
|
{
|
|
.format = gsk_vulkan_image_get_vk_format (target),
|
|
.samples = VK_SAMPLE_COUNT_1_BIT,
|
|
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
|
|
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
|
|
.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
|
|
.finalLayout = final_layout
|
|
}
|
|
},
|
|
.subpassCount = 1,
|
|
.pSubpasses = (VkSubpassDescription []) {
|
|
{
|
|
.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
.inputAttachmentCount = 0,
|
|
.colorAttachmentCount = 1,
|
|
.pColorAttachments = (VkAttachmentReference []) {
|
|
{
|
|
.attachment = 0,
|
|
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
|
|
}
|
|
},
|
|
.pResolveAttachments = (VkAttachmentReference []) {
|
|
{
|
|
.attachment = VK_ATTACHMENT_UNUSED,
|
|
.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
|
|
}
|
|
},
|
|
.pDepthStencilAttachment = NULL,
|
|
}
|
|
},
|
|
.dependencyCount = 0
|
|
},
|
|
NULL,
|
|
&self->render_pass);
|
|
|
|
GSK_VK_CHECK (vkCreateFramebuffer, gdk_vulkan_context_get_device (self->vulkan),
|
|
&(VkFramebufferCreateInfo) {
|
|
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
|
.renderPass = self->render_pass,
|
|
.attachmentCount = 1,
|
|
.pAttachments = (VkImageView[1]) {
|
|
gsk_vulkan_image_get_image_view (target)
|
|
},
|
|
.width = gsk_vulkan_image_get_width (target),
|
|
.height = gsk_vulkan_image_get_height (target),
|
|
.layers = 1
|
|
},
|
|
NULL,
|
|
&self->framebuffer);
|
|
|
|
#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)
|
|
{
|
|
VkDevice device = gdk_vulkan_context_get_device (self->vulkan);
|
|
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);
|
|
vkDestroyFramebuffer (device, self->framebuffer, NULL);
|
|
vkDestroyRenderPass (device, self->render_pass, NULL);
|
|
|
|
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_create_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 = self->framebuffer,
|
|
.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);
|
|
}
|
|
|