gtk/gsk/vulkan/gskvulkanrenderpass.c
Benjamin Otte d98991a0ad vulkan: Set offsets when counting
We can set the vertex offset while counting the data, this gets rid of
the need of passing all the counting machinery into the actual data
collection code.
2023-06-04 19:42:01 +02:00

2392 lines
104 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 "gskvulkanblendmodepipelineprivate.h"
#include "gskvulkanblurpipelineprivate.h"
#include "gskvulkanborderpipelineprivate.h"
#include "gskvulkanboxshadowpipelineprivate.h"
#include "gskvulkanclipprivate.h"
#include "gskvulkancolorpipelineprivate.h"
#include "gskvulkancolortextpipelineprivate.h"
#include "gskvulkancrossfadepipelineprivate.h"
#include "gskvulkaneffectpipelineprivate.h"
#include "gskvulkanlineargradientpipelineprivate.h"
#include "gskvulkantextpipelineprivate.h"
#include "gskvulkantexturepipelineprivate.h"
#include "gskvulkanimageprivate.h"
#include "gskvulkanpushconstantsprivate.h"
#include "gskvulkanrendererprivate.h"
#include "gskprivate.h"
#define ORTHO_NEAR_PLANE -10000
#define ORTHO_FAR_PLANE 10000
typedef struct _GskVulkanParseState GskVulkanParseState;
typedef union _GskVulkanOp GskVulkanOp;
typedef struct _GskVulkanOpRender GskVulkanOpRender;
typedef struct _GskVulkanOpText GskVulkanOpText;
typedef struct _GskVulkanOpPushConstants GskVulkanOpPushConstants;
typedef enum {
/* GskVulkanOpRender */
GSK_VULKAN_OP_FALLBACK,
GSK_VULKAN_OP_FALLBACK_CLIP,
GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP,
GSK_VULKAN_OP_TEXTURE,
GSK_VULKAN_OP_COLOR,
GSK_VULKAN_OP_LINEAR_GRADIENT,
GSK_VULKAN_OP_OPACITY,
GSK_VULKAN_OP_BLUR,
GSK_VULKAN_OP_COLOR_MATRIX,
GSK_VULKAN_OP_BORDER,
GSK_VULKAN_OP_INSET_SHADOW,
GSK_VULKAN_OP_OUTSET_SHADOW,
GSK_VULKAN_OP_REPEAT,
GSK_VULKAN_OP_CROSS_FADE,
GSK_VULKAN_OP_BLEND_MODE,
/* GskVulkanOpText */
GSK_VULKAN_OP_TEXT,
GSK_VULKAN_OP_COLOR_TEXT,
/* GskVulkanOpPushConstants */
GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS,
} GskVulkanOpType;
/* render ops with 0, 1 or 2 sources */
struct _GskVulkanOpRender
{
GskVulkanOpType type;
GskRenderNode *node; /* node that's the source of this op */
graphene_point_t offset; /* offset of the node */
GskVulkanPipeline *pipeline; /* pipeline to use */
GskRoundedRect clip; /* clip rect (or random memory if not relevant) */
GskVulkanImage *source; /* source image to render */
GskVulkanImage *source2; /* second source image to render (if relevant) */
gsize vertex_offset; /* offset into vertex buffer */
gsize vertex_count; /* number of vertices */
gsize descriptor_set_index; /* index into descriptor sets array for the right descriptor set to bind */
gsize descriptor_set_index2; /* descriptor index for the second source (if relevant) */
graphene_rect_t source_rect; /* area that source maps to */
graphene_rect_t source2_rect; /* area that source2 maps to */
};
struct _GskVulkanOpText
{
GskVulkanOpType type;
GskRenderNode *node; /* node that's the source of this op */
graphene_point_t offset; /* offset of the node */
GskVulkanPipeline *pipeline; /* pipeline to use */
GskRoundedRect clip; /* clip rect (or random memory if not relevant) */
GskVulkanImage *source; /* source image to render */
gsize vertex_offset; /* offset into vertex buffer */
gsize vertex_count; /* number of vertices */
gsize descriptor_set_index; /* index into descriptor sets array for the right descriptor set to bind */
guint texture_index; /* index of the texture in the glyph cache */
guint start_glyph; /* the first glyph in nodes glyphstring that we render */
guint num_glyphs; /* number of *non-empty* glyphs (== instances) we render */
float scale;
};
struct _GskVulkanOpPushConstants
{
GskVulkanOpType type;
GskRenderNode *node; /* node that's the source of this op */
graphene_vec2_t scale;
graphene_matrix_t mvp;
GskRoundedRect clip;
};
union _GskVulkanOp
{
GskVulkanOpType type;
GskVulkanOpRender render;
GskVulkanOpText text;
GskVulkanOpPushConstants constants;
};
struct _GskVulkanRenderPass
{
GdkVulkanContext *vulkan;
GArray *render_ops;
GskVulkanImage *target;
graphene_rect_t viewport;
cairo_region_t *clip;
graphene_vec2_t scale;
VkRenderPass render_pass;
VkSemaphore signal_semaphore;
GArray *wait_semaphores;
GskVulkanBuffer *vertex_data;
};
struct _GskVulkanParseState
{
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
GskVulkanRenderPass *
gsk_vulkan_render_pass_new (GdkVulkanContext *context,
GskVulkanImage *target,
const graphene_vec2_t *scale,
const graphene_rect_t *viewport,
cairo_region_t *clip,
VkSemaphore signal_semaphore)
{
GskVulkanRenderPass *self;
VkImageLayout final_layout;
self = g_new0 (GskVulkanRenderPass, 1);
self->vulkan = g_object_ref (context);
self->render_ops = g_array_new (FALSE, FALSE, sizeof (GskVulkanOp));
self->target = g_object_ref (target);
self->clip = cairo_region_copy (clip);
self->viewport = *viewport;
graphene_vec2_init_from_vec2 (&self->scale, scale);
if (signal_semaphore != VK_NULL_HANDLE) // 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 = gdk_vulkan_context_get_image_format (self->vulkan),
.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);
self->signal_semaphore = signal_semaphore;
self->wait_semaphores = g_array_new (FALSE, FALSE, sizeof (VkSemaphore));
self->vertex_data = NULL;
#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
return self;
}
void
gsk_vulkan_render_pass_free (GskVulkanRenderPass *self)
{
g_array_unref (self->render_ops);
g_object_unref (self->vulkan);
g_object_unref (self->target);
cairo_region_destroy (self->clip);
vkDestroyRenderPass (gdk_vulkan_context_get_device (self->vulkan),
self->render_pass,
NULL);
if (self->vertex_data)
gsk_vulkan_buffer_free (self->vertex_data);
if (self->signal_semaphore != VK_NULL_HANDLE)
vkDestroySemaphore (gdk_vulkan_context_get_device (self->vulkan),
self->signal_semaphore,
NULL);
g_array_unref (self->wait_semaphores);
g_free (self);
}
static void
gsk_vulkan_render_pass_append_push_constants (GskVulkanRenderPass *self,
GskRenderNode *node,
const GskVulkanParseState *state)
{
GskVulkanOp op = {
.constants.type = GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS,
.constants.node = node,
.constants.scale = state->scale,
.constants.clip = state->clip.rect,
};
if (state->modelview)
{
gsk_transform_to_matrix (state->modelview, &op.constants.mvp);
graphene_matrix_multiply (&op.constants.mvp, &state->projection, &op.constants.mvp);
}
else
graphene_matrix_init_from_matrix (&op.constants.mvp, &state->projection);
g_array_append_val (self->render_ops, op);
}
#define FALLBACK(...) G_STMT_START { \
GSK_RENDERER_DEBUG (gsk_vulkan_render_get_renderer (render), FALLBACK, __VA_ARGS__); \
return FALSE; \
}G_STMT_END
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)
{
GskVulkanOp op = {
.render.node = node,
.render.offset = state->offset,
};
switch (state->clip.type)
{
case GSK_VULKAN_CLIP_NONE:
op.type = GSK_VULKAN_OP_FALLBACK;
break;
case GSK_VULKAN_CLIP_RECT:
op.type = GSK_VULKAN_OP_FALLBACK_CLIP;
gsk_rounded_rect_init_copy (&op.render.clip, &state->clip.rect);
break;
case GSK_VULKAN_CLIP_ROUNDED_CIRCULAR:
case GSK_VULKAN_CLIP_ROUNDED:
op.type = GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP;
gsk_rounded_rect_init_copy (&op.render.clip, &state->clip.rect);
break;
case GSK_VULKAN_CLIP_ALL_CLIPPED:
default:
g_assert_not_reached ();
return FALSE;
}
op.render.pipeline = gsk_vulkan_render_get_pipeline (render, GSK_VULKAN_PIPELINE_TEXTURE);
g_array_append_val (self->render_ops, op);
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)
{
if (!gsk_vulkan_clip_intersects_rect (&state->clip, &state->offset, &node->bounds))
return TRUE;
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)
{
GskVulkanOp op = {
.render.node = node,
.render.offset = state->offset,
};
GskVulkanPipelineType pipeline_type;
if (gsk_vulkan_clip_contains_rect (&state->clip, &state->offset, &node->bounds))
pipeline_type = GSK_VULKAN_PIPELINE_COLOR;
else if (state->clip.type == GSK_VULKAN_CLIP_RECT)
pipeline_type = GSK_VULKAN_PIPELINE_COLOR_CLIP;
else if (state->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
pipeline_type = GSK_VULKAN_PIPELINE_COLOR_CLIP_ROUNDED;
else
FALLBACK ("Color nodes can't deal with clip type %u", state->clip.type);
op.type = GSK_VULKAN_OP_COLOR;
op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
g_array_append_val (self->render_ops, op);
return TRUE;
}
static inline gboolean
gsk_vulkan_render_pass_add_repeating_linear_gradient_node (GskVulkanRenderPass *self,
GskVulkanRender *render,
const GskVulkanParseState *state,
GskRenderNode *node)
{
GskVulkanPipelineType pipeline_type;
GskVulkanOp op = {
.render.node = node,
.render.offset = state->offset,
};
if (gsk_linear_gradient_node_get_n_color_stops (node) > GSK_VULKAN_LINEAR_GRADIENT_PIPELINE_MAX_COLOR_STOPS)
FALLBACK ("Linear gradient with %zu color stops, hardcoded limit is %u",
gsk_linear_gradient_node_get_n_color_stops (node),
GSK_VULKAN_LINEAR_GRADIENT_PIPELINE_MAX_COLOR_STOPS);
if (gsk_vulkan_clip_contains_rect (&state->clip, &state->offset, &node->bounds))
pipeline_type = GSK_VULKAN_PIPELINE_LINEAR_GRADIENT;
else if (state->clip.type == GSK_VULKAN_CLIP_RECT)
pipeline_type = GSK_VULKAN_PIPELINE_LINEAR_GRADIENT_CLIP;
else if (state->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
pipeline_type = GSK_VULKAN_PIPELINE_LINEAR_GRADIENT_CLIP_ROUNDED;
else
FALLBACK ("Linear gradient nodes can't deal with clip type %u", state->clip.type);
op.type = GSK_VULKAN_OP_LINEAR_GRADIENT;
op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
g_array_append_val (self->render_ops, op);
return TRUE;
}
static inline gboolean
gsk_vulkan_render_pass_add_border_node (GskVulkanRenderPass *self,
GskVulkanRender *render,
const GskVulkanParseState *state,
GskRenderNode *node)
{
GskVulkanPipelineType pipeline_type;
GskVulkanOp op = {
.render.node = node,
.render.offset = state->offset,
};
if (gsk_vulkan_clip_contains_rect (&state->clip, &state->offset, &node->bounds))
pipeline_type = GSK_VULKAN_PIPELINE_BORDER;
else if (state->clip.type == GSK_VULKAN_CLIP_RECT)
pipeline_type = GSK_VULKAN_PIPELINE_BORDER_CLIP;
else if (state->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
pipeline_type = GSK_VULKAN_PIPELINE_BORDER_CLIP_ROUNDED;
else
FALLBACK ("Border nodes can't deal with clip type %u", state->clip.type);
op.type = GSK_VULKAN_OP_BORDER;
op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
g_array_append_val (self->render_ops, op);
return TRUE;
}
static inline gboolean
gsk_vulkan_render_pass_add_texture_node (GskVulkanRenderPass *self,
GskVulkanRender *render,
const GskVulkanParseState *state,
GskRenderNode *node)
{
GskVulkanPipelineType pipeline_type;
GskVulkanOp op = {
.render.node = node,
.render.offset = state->offset,
};
if (gsk_vulkan_clip_contains_rect (&state->clip, &state->offset, &node->bounds))
pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE;
else if (state->clip.type == GSK_VULKAN_CLIP_RECT)
pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE_CLIP;
else if (state->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE_CLIP_ROUNDED;
else
FALLBACK ("Texture nodes can't deal with clip type %u", state->clip.type);
op.type = GSK_VULKAN_OP_TEXTURE;
op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
g_array_append_val (self->render_ops, op);
return TRUE;
}
static inline gboolean
gsk_vulkan_render_pass_add_inset_shadow_node (GskVulkanRenderPass *self,
GskVulkanRender *render,
const GskVulkanParseState *state,
GskRenderNode *node)
{
GskVulkanPipelineType pipeline_type;
GskVulkanOp op = {
.render.node = node,
.render.offset = state->offset,
};
if (gsk_inset_shadow_node_get_blur_radius (node) > 0)
FALLBACK ("Blur support not implemented for inset shadows");
else if (gsk_vulkan_clip_contains_rect (&state->clip, &state->offset, &node->bounds))
pipeline_type = GSK_VULKAN_PIPELINE_INSET_SHADOW;
else if (state->clip.type == GSK_VULKAN_CLIP_RECT)
pipeline_type = GSK_VULKAN_PIPELINE_INSET_SHADOW_CLIP;
else if (state->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
pipeline_type = GSK_VULKAN_PIPELINE_INSET_SHADOW_CLIP_ROUNDED;
else
FALLBACK ("Inset shadow nodes can't deal with clip type %u", state->clip.type);
op.type = GSK_VULKAN_OP_INSET_SHADOW;
op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
g_array_append_val (self->render_ops, op);
return TRUE;
}
static inline gboolean
gsk_vulkan_render_pass_add_outset_shadow_node (GskVulkanRenderPass *self,
GskVulkanRender *render,
const GskVulkanParseState *state,
GskRenderNode *node)
{
GskVulkanPipelineType pipeline_type;
GskVulkanOp op = {
.render.node = node,
.render.offset = state->offset,
};
if (gsk_outset_shadow_node_get_blur_radius (node) > 0)
FALLBACK ("Blur support not implemented for outset shadows");
else if (gsk_vulkan_clip_contains_rect (&state->clip, &state->offset, &node->bounds))
pipeline_type = GSK_VULKAN_PIPELINE_OUTSET_SHADOW;
else if (state->clip.type == GSK_VULKAN_CLIP_RECT)
pipeline_type = GSK_VULKAN_PIPELINE_OUTSET_SHADOW_CLIP;
else if (state->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
pipeline_type = GSK_VULKAN_PIPELINE_OUTSET_SHADOW_CLIP_ROUNDED;
else
FALLBACK ("Outset shadow nodes can't deal with clip type %u", state->clip.type);
op.type = GSK_VULKAN_OP_OUTSET_SHADOW;
op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
g_array_append_val (self->render_ops, op);
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;
}
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 inline gboolean
gsk_vulkan_render_pass_add_opacity_node (GskVulkanRenderPass *self,
GskVulkanRender *render,
const GskVulkanParseState *state,
GskRenderNode *node)
{
GskVulkanPipelineType pipeline_type;
GskVulkanOp op = {
.render.node = node,
.render.offset = state->offset,
};
if (gsk_vulkan_clip_contains_rect (&state->clip, &state->offset, &node->bounds))
pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX;
else if (state->clip.type == GSK_VULKAN_CLIP_RECT)
pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX_CLIP;
else if (state->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX_CLIP_ROUNDED;
else
FALLBACK ("Opacity nodes can't deal with clip type %u", state->clip.type);
op.type = GSK_VULKAN_OP_OPACITY;
op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
g_array_append_val (self->render_ops, op);
return TRUE;
}
static inline gboolean
gsk_vulkan_render_pass_add_color_matrix_node (GskVulkanRenderPass *self,
GskVulkanRender *render,
const GskVulkanParseState *state,
GskRenderNode *node)
{
GskVulkanPipelineType pipeline_type;
GskVulkanOp op = {
.render.node = node,
.render.offset = state->offset,
};
if (gsk_vulkan_clip_contains_rect (&state->clip, &state->offset, &node->bounds))
pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX;
else if (state->clip.type == GSK_VULKAN_CLIP_RECT)
pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX_CLIP;
else if (state->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
pipeline_type = GSK_VULKAN_PIPELINE_COLOR_MATRIX_CLIP_ROUNDED;
else
FALLBACK ("Color matrix nodes can't deal with clip type %u", state->clip.type);
op.type = GSK_VULKAN_OP_COLOR_MATRIX;
op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
g_array_append_val (self->render_ops, op);
return TRUE;
}
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;
graphene_rect_offset_r (gsk_clip_node_get_clip (node),
state->offset.x, state->offset.y,
&clip);
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);
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);
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));
gsk_vulkan_render_pass_append_push_constants (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.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)
{
GskVulkanPipelineType pipeline_type;
GskVulkanOp op = {
.render.node = node,
.render.offset = state->offset,
};
if (graphene_rect_get_area (gsk_repeat_node_get_child_bounds (node)) == 0)
return TRUE;
if (gsk_vulkan_clip_contains_rect (&state->clip, &state->offset, &node->bounds))
pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE;
else if (state->clip.type == GSK_VULKAN_CLIP_RECT)
pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE_CLIP;
else if (state->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
pipeline_type = GSK_VULKAN_PIPELINE_TEXTURE_CLIP_ROUNDED;
else
FALLBACK ("Repeat nodes can't deal with clip type %u", state->clip.type);
op.type = GSK_VULKAN_OP_REPEAT;
op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
g_array_append_val (self->render_ops, op);
return TRUE;
}
static inline gboolean
gsk_vulkan_render_pass_add_blend_node (GskVulkanRenderPass *self,
GskVulkanRender *render,
const GskVulkanParseState *state,
GskRenderNode *node)
{
GskVulkanPipelineType pipeline_type;
GskVulkanOp op = {
.render.node = node,
.render.offset = state->offset,
};
if (gsk_vulkan_clip_contains_rect (&state->clip, &state->offset, &node->bounds))
pipeline_type = GSK_VULKAN_PIPELINE_BLEND_MODE;
else if (state->clip.type == GSK_VULKAN_CLIP_RECT)
pipeline_type = GSK_VULKAN_PIPELINE_BLEND_MODE_CLIP;
else if (state->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
pipeline_type = GSK_VULKAN_PIPELINE_BLEND_MODE_CLIP_ROUNDED;
else
FALLBACK ("Blend nodes can't deal with clip type %u", state->clip.type);
op.type = GSK_VULKAN_OP_BLEND_MODE;
op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
g_array_append_val (self->render_ops, op);
return TRUE;
}
static inline gboolean
gsk_vulkan_render_pass_add_cross_fade_node (GskVulkanRenderPass *self,
GskVulkanRender *render,
const GskVulkanParseState *state,
GskRenderNode *node)
{
GskVulkanOp op = {
.render.node = node,
.render.offset = state->offset,
};
GskVulkanPipelineType pipeline_type;
if (gsk_vulkan_clip_contains_rect (&state->clip, &state->offset, &node->bounds))
pipeline_type = GSK_VULKAN_PIPELINE_CROSS_FADE;
else if (state->clip.type == GSK_VULKAN_CLIP_RECT)
pipeline_type = GSK_VULKAN_PIPELINE_CROSS_FADE_CLIP;
else if (state->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
pipeline_type = GSK_VULKAN_PIPELINE_CROSS_FADE_CLIP_ROUNDED;
else
FALLBACK ("Cross fade nodes can't deal with clip type %u", state->clip.type);
op.type = GSK_VULKAN_OP_CROSS_FADE;
op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
g_array_append_val (self->render_ops, op);
return TRUE;
}
static inline gboolean
gsk_vulkan_render_pass_add_text_node (GskVulkanRenderPass *self,
GskVulkanRender *render,
const GskVulkanParseState *state,
GskRenderNode *node)
{
GskVulkanOp op = {
.render.node = node,
.render.offset = state->offset,
};
GskVulkanPipelineType pipeline_type;
const PangoGlyphInfo *glyphs;
GskVulkanRenderer *renderer;
const PangoFont *font;
guint texture_index;
guint num_glyphs;
guint count;
int x_position;
int i;
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);
if (gsk_text_node_has_color_glyphs (node))
{
if (gsk_vulkan_clip_contains_rect (&state->clip, &state->offset, &node->bounds))
pipeline_type = GSK_VULKAN_PIPELINE_COLOR_TEXT;
else if (state->clip.type == GSK_VULKAN_CLIP_RECT)
pipeline_type = GSK_VULKAN_PIPELINE_COLOR_TEXT_CLIP;
else if (state->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
pipeline_type = GSK_VULKAN_PIPELINE_COLOR_TEXT_CLIP_ROUNDED;
else
FALLBACK ("Text nodes can't deal with clip type %u", state->clip.type);
op.type = GSK_VULKAN_OP_COLOR_TEXT;
}
else
{
if (gsk_vulkan_clip_contains_rect (&state->clip, &state->offset, &node->bounds))
pipeline_type = GSK_VULKAN_PIPELINE_TEXT;
else if (state->clip.type == GSK_VULKAN_CLIP_RECT)
pipeline_type = GSK_VULKAN_PIPELINE_TEXT_CLIP;
else if (state->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
pipeline_type = GSK_VULKAN_PIPELINE_TEXT_CLIP_ROUNDED;
else
FALLBACK ("Text nodes can't deal with clip type %u", state->clip.type);
op.type = GSK_VULKAN_OP_TEXT;
}
op.text.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
op.text.start_glyph = 0;
op.text.texture_index = G_MAXUINT;
op.text.scale = MAX (graphene_vec2_get_x (&state->scale), graphene_vec2_get_y (&state->scale));
x_position = 0;
for (i = 0, count = 0; i < num_glyphs; i++)
{
const PangoGlyphInfo *gi = &glyphs[i];
texture_index = gsk_vulkan_renderer_cache_glyph (renderer,
(PangoFont *)font,
gi->glyph,
x_position + gi->geometry.x_offset,
gi->geometry.y_offset,
op.text.scale);
if (op.text.texture_index == G_MAXUINT)
op.text.texture_index = texture_index;
if (texture_index != op.text.texture_index)
{
op.text.num_glyphs = count;
g_array_append_val (self->render_ops, op);
count = 1;
op.text.start_glyph = i;
op.text.texture_index = texture_index;
}
else
count++;
x_position += gi->geometry.width;
}
if (op.text.texture_index != G_MAXUINT && count != 0)
{
op.text.num_glyphs = count;
g_array_append_val (self->render_ops, op);
}
return TRUE;
}
static inline gboolean
gsk_vulkan_render_pass_add_blur_node (GskVulkanRenderPass *self,
GskVulkanRender *render,
const GskVulkanParseState *state,
GskRenderNode *node)
{
GskVulkanPipelineType pipeline_type;
GskVulkanOp op = {
.render.node = node,
.render.offset = state->offset,
};
if (gsk_vulkan_clip_contains_rect (&state->clip, &state->offset, &node->bounds))
pipeline_type = GSK_VULKAN_PIPELINE_BLUR;
else if (state->clip.type == GSK_VULKAN_CLIP_RECT)
pipeline_type = GSK_VULKAN_PIPELINE_BLUR_CLIP;
else if (state->clip.type == GSK_VULKAN_CLIP_ROUNDED_CIRCULAR)
pipeline_type = GSK_VULKAN_PIPELINE_BLUR_CLIP_ROUNDED;
else
FALLBACK ("Blur nodes can't deal with clip type %u", state->clip.type);
op.type = GSK_VULKAN_OP_BLUR;
op.render.pipeline = gsk_vulkan_render_get_pipeline (render, pipeline_type);
g_array_append_val (self->render_ops, op);
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);
#define N_RENDER_NODES (GSK_MASK_NODE + 1)
/* TODO: implement remaining nodes */
static const GskVulkanRenderPassNodeFunc nodes_vtable[N_RENDER_NODES] = {
[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_repeating_linear_gradient_node,
[GSK_REPEATING_LINEAR_GRADIENT_NODE] = gsk_vulkan_render_pass_add_repeating_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] = NULL,
[GSK_MASK_NODE] = NULL,
};
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;
node_type = gsk_render_node_get_node_type (node);
node_func = nodes_vtable[node_type];
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);
}
void
gsk_vulkan_render_pass_add (GskVulkanRenderPass *self,
GskVulkanRender *render,
GskRenderNode *node)
{
GskVulkanParseState state;
cairo_rectangle_int_t rect;
graphene_rect_t clip;
cairo_region_get_extents (self->clip, &rect);
clip = GRAPHENE_RECT_INIT(rect.x + self->viewport.origin.x,
rect.y + self->viewport.origin.y,
rect.width, rect.height);
if (graphene_rect_equal (&clip, &self->viewport))
{
graphene_rect_scale (&clip, 1 / graphene_vec2_get_x (&self->scale), 1 / graphene_vec2_get_y (&self->scale), &clip);
gsk_vulkan_clip_init_empty (&state.clip, &self->viewport);
}
else
{
graphene_rect_scale (&clip, 1 / graphene_vec2_get_x (&self->scale), 1 / graphene_vec2_get_y (&self->scale), &clip);
gsk_vulkan_clip_init_rect (&state.clip, &clip);
}
state.modelview = NULL;
graphene_matrix_init_ortho (&state.projection,
self->viewport.origin.x, self->viewport.origin.x + self->viewport.size.width,
self->viewport.origin.y, self->viewport.origin.y + 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_zero ();
gsk_vulkan_render_pass_append_push_constants (self, node, &state);
gsk_vulkan_render_pass_add_node (self, render, &state, node);
}
static GskVulkanImage *
gsk_vulkan_render_pass_render_offscreen (GdkVulkanContext *vulkan,
GskVulkanRender *render,
GskVulkanUploader *uploader,
VkSemaphore semaphore,
GskRenderNode *node,
const graphene_vec2_t *scale,
const graphene_rect_t *viewport)
{
graphene_rect_t view;
cairo_region_t *clip;
GskVulkanRenderPass *pass;
GskVulkanImage *result;
float scale_x, scale_y;
scale_x = graphene_vec2_get_x (scale);
scale_y = graphene_vec2_get_y (scale);
view = GRAPHENE_RECT_INIT (scale_x * viewport->origin.x,
scale_y * viewport->origin.y,
ceil (scale_x * viewport->size.width),
ceil (scale_y * viewport->size.height));
result = gsk_vulkan_image_new_for_offscreen (vulkan,
view.size.width, view.size.height);
#ifdef G_ENABLE_DEBUG
{
GskProfiler *profiler = gsk_renderer_get_profiler (gsk_vulkan_render_get_renderer (render));
gsk_profiler_counter_add (profiler,
texture_pixels_quark,
view.size.width * view.size.height);
}
#endif
clip = cairo_region_create_rectangle (&(cairo_rectangle_int_t) {
0, 0,
gsk_vulkan_image_get_width (result),
gsk_vulkan_image_get_height (result)
});
pass = gsk_vulkan_render_pass_new (vulkan,
result,
scale,
&view,
clip,
semaphore);
cairo_region_destroy (clip);
gsk_vulkan_render_add_render_pass (render, pass);
gsk_vulkan_render_pass_add (pass, render, node);
gsk_vulkan_render_add_cleanup_image (render, result);
return result;
}
static GskVulkanImage *
gsk_vulkan_render_pass_get_node_as_texture (GskVulkanRenderPass *self,
GskVulkanRender *render,
GskVulkanUploader *uploader,
GskRenderNode *node,
const graphene_vec2_t *scale,
const graphene_rect_t *clip_bounds,
const graphene_point_t *clip_offset,
graphene_rect_t *tex_bounds)
{
VkSemaphore semaphore;
GskVulkanImage *result;
cairo_surface_t *surface;
cairo_t *cr;
switch ((guint) gsk_render_node_get_node_type (node))
{
case GSK_TEXTURE_NODE:
result = gsk_vulkan_renderer_ref_texture_image (GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render)),
gsk_texture_node_get_texture (node),
uploader);
gsk_vulkan_render_add_cleanup_image (render, result);
*tex_bounds = node->bounds;
return result;
case GSK_CAIRO_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.
*/
break;
default:
{
graphene_rect_t clipped;
graphene_rect_offset_r (clip_bounds, - clip_offset->x, - clip_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;
vkCreateSemaphore (gdk_vulkan_context_get_device (self->vulkan),
&(VkSemaphoreCreateInfo) {
VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
NULL,
0
},
NULL,
&semaphore);
g_array_append_val (self->wait_semaphores, semaphore);
return gsk_vulkan_render_pass_render_offscreen (self->vulkan,
render,
uploader,
semaphore,
node,
scale,
&clipped);
}
}
GSK_RENDERER_DEBUG (gsk_vulkan_render_get_renderer (render), FALLBACK, "Node as texture not implemented for this case. Using %gx%g fallback surface",
ceil (node->bounds.size.width),
ceil (node->bounds.size.height));
#ifdef G_ENABLE_DEBUG
{
GskProfiler *profiler = gsk_renderer_get_profiler (gsk_vulkan_render_get_renderer (render));
gsk_profiler_counter_add (profiler,
fallback_pixels_quark,
ceil (node->bounds.size.width) * ceil (node->bounds.size.height));
}
#endif
/* XXX: We could intersect bounds with clip bounds here */
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
ceil (node->bounds.size.width * graphene_vec2_get_x (scale)),
ceil (node->bounds.size.height * graphene_vec2_get_y (scale)));
cairo_surface_set_device_scale (surface, graphene_vec2_get_x (scale), graphene_vec2_get_y (scale));
cr = cairo_create (surface);
cairo_translate (cr, -node->bounds.origin.x, -node->bounds.origin.y);
gsk_render_node_draw (node, cr);
cairo_destroy (cr);
result = gsk_vulkan_image_new_from_data (uploader,
cairo_image_surface_get_data (surface),
cairo_image_surface_get_width (surface),
cairo_image_surface_get_height (surface),
cairo_image_surface_get_stride (surface));
cairo_surface_destroy (surface);
gsk_vulkan_render_add_cleanup_image (render, result);
*tex_bounds = node->bounds;
return result;
}
static void
gsk_vulkan_render_pass_upload_fallback (GskVulkanRenderPass *self,
GskVulkanOpRender *op,
GskVulkanRender *render,
GskVulkanUploader *uploader)
{
GskRenderNode *node;
cairo_surface_t *surface;
cairo_t *cr;
float scale_x, scale_y;
node = op->node;
GSK_RENDERER_DEBUG (gsk_vulkan_render_get_renderer (render), FALLBACK,
"Upload op=%s, node %s[%p], bounds %gx%g",
op->type == GSK_VULKAN_OP_FALLBACK_CLIP ? "fallback-clip" :
(op->type == GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP ? "fallback-rounded-clip" : "fallback"),
g_type_name_from_instance ((GTypeInstance *) node), node,
ceil (node->bounds.size.width),
ceil (node->bounds.size.height));
#ifdef G_ENABLE_DEBUG
{
GskProfiler *profiler = gsk_renderer_get_profiler (gsk_vulkan_render_get_renderer (render));
gsk_profiler_counter_add (profiler,
fallback_pixels_quark,
ceil (node->bounds.size.width) * ceil (node->bounds.size.height));
}
#endif
scale_x = graphene_vec2_get_x (&self->scale);
scale_y = graphene_vec2_get_y (&self->scale);
/* XXX: We could intersect bounds with clip bounds here */
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
ceil (node->bounds.size.width * scale_x),
ceil (node->bounds.size.height * scale_y));
cairo_surface_set_device_scale (surface, scale_x, scale_y);
cr = cairo_create (surface);
cairo_translate (cr, -node->bounds.origin.x, -node->bounds.origin.y);
if (op->type == GSK_VULKAN_OP_FALLBACK_CLIP)
{
cairo_rectangle (cr,
op->clip.bounds.origin.x - op->offset.x,
op->clip.bounds.origin.y - op->offset.y,
op->clip.bounds.size.width,
op->clip.bounds.size.height);
cairo_clip (cr);
}
else if (op->type == GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP)
{
cairo_translate (cr, - op->offset.x, - op->offset.y);
gsk_rounded_rect_path (&op->clip, cr);
cairo_translate (cr, op->offset.x, op->offset.y);
cairo_clip (cr);
}
else
{
g_assert (op->type == GSK_VULKAN_OP_FALLBACK);
}
gsk_render_node_draw (node, cr);
#ifdef G_ENABLE_DEBUG
if (GSK_RENDERER_DEBUG_CHECK (gsk_vulkan_render_get_renderer (render), FALLBACK))
{
cairo_rectangle (cr,
op->clip.bounds.origin.x - op->offset.x,
op->clip.bounds.origin.y - op->offset.y,
op->clip.bounds.size.width,
op->clip.bounds.size.height);
if (gsk_render_node_get_node_type (node) == GSK_CAIRO_NODE)
cairo_set_source_rgba (cr, 0.3, 0, 1, 0.25);
else
cairo_set_source_rgba (cr, 1, 0, 0, 0.25);
cairo_fill_preserve (cr);
if (gsk_render_node_get_node_type (node) == GSK_CAIRO_NODE)
cairo_set_source_rgba (cr, 0.3, 0, 1, 1);
else
cairo_set_source_rgba (cr, 1, 0, 0, 1);
cairo_stroke (cr);
}
#endif
cairo_destroy (cr);
op->source = gsk_vulkan_image_new_from_data (uploader,
cairo_image_surface_get_data (surface),
cairo_image_surface_get_width (surface),
cairo_image_surface_get_height (surface),
cairo_image_surface_get_stride (surface));
op->source_rect = GRAPHENE_RECT_INIT(0, 0, 1, 1);
cairo_surface_destroy (surface);
gsk_vulkan_render_add_cleanup_image (render, op->source);
}
static void
get_tex_rect (graphene_rect_t *tex_coords,
const graphene_rect_t *rect,
const graphene_rect_t *tex)
{
graphene_rect_init (tex_coords,
(rect->origin.x - tex->origin.x) / tex->size.width,
(rect->origin.y - tex->origin.y) / tex->size.height,
rect->size.width / tex->size.width,
rect->size.height / tex->size.height);
}
void
gsk_vulkan_render_pass_upload (GskVulkanRenderPass *self,
GskVulkanRender *render,
GskVulkanUploader *uploader)
{
GskVulkanOp *op;
guint i;
const graphene_rect_t *clip = NULL;
const graphene_vec2_t *scale = NULL;
for (i = 0; i < self->render_ops->len; i++)
{
op = &g_array_index (self->render_ops, GskVulkanOp, i);
switch (op->type)
{
case GSK_VULKAN_OP_FALLBACK:
case GSK_VULKAN_OP_FALLBACK_CLIP:
case GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP:
gsk_vulkan_render_pass_upload_fallback (self, &op->render, render, uploader);
break;
case GSK_VULKAN_OP_TEXT:
case GSK_VULKAN_OP_COLOR_TEXT:
{
op->text.source = gsk_vulkan_renderer_ref_glyph_image (GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render)),
uploader,
op->text.texture_index);
gsk_vulkan_render_add_cleanup_image (render, op->text.source);
}
break;
case GSK_VULKAN_OP_TEXTURE:
{
op->render.source = gsk_vulkan_renderer_ref_texture_image (GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render)),
gsk_texture_node_get_texture (op->render.node),
uploader);
op->render.source_rect = GRAPHENE_RECT_INIT(0, 0, 1, 1);
gsk_vulkan_render_add_cleanup_image (render, op->render.source);
}
break;
case GSK_VULKAN_OP_OPACITY:
{
GskRenderNode *child = gsk_opacity_node_get_child (op->render.node);
graphene_rect_t tex_bounds;
op->render.source = gsk_vulkan_render_pass_get_node_as_texture (self,
render,
uploader,
child,
scale,
clip,
&op->render.offset,
&tex_bounds);
get_tex_rect (&op->render.source_rect, &op->render.node->bounds, &tex_bounds);
}
break;
case GSK_VULKAN_OP_REPEAT:
{
GskRenderNode *child = gsk_repeat_node_get_child (op->render.node);
const graphene_rect_t *child_bounds = gsk_repeat_node_get_child_bounds (op->render.node);
graphene_rect_t tex_bounds;
if (!graphene_rect_equal (child_bounds, &child->bounds))
{
VkSemaphore semaphore;
/* We need to create a texture in the right size so that we can repeat it
* properly, so even for texture nodes this step is necessary.
* We also can't use the clip because of that. */
vkCreateSemaphore (gdk_vulkan_context_get_device (self->vulkan),
&(VkSemaphoreCreateInfo) {
VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
NULL,
0
},
NULL,
&semaphore);
g_array_append_val (self->wait_semaphores, semaphore);
op->render.source = gsk_vulkan_render_pass_render_offscreen (self->vulkan,
render,
uploader,
semaphore,
child,
scale,
child_bounds);
get_tex_rect (&op->render.source_rect, &op->render.node->bounds, child_bounds);
}
else
{
op->render.source = gsk_vulkan_render_pass_get_node_as_texture (self,
render,
uploader,
child,
scale,
&child->bounds,
&GRAPHENE_POINT_INIT (0, 0),
&tex_bounds);
get_tex_rect (&op->render.source_rect, &op->render.node->bounds, &tex_bounds);
}
}
break;
case GSK_VULKAN_OP_BLUR:
{
GskRenderNode *child = gsk_blur_node_get_child (op->render.node);
graphene_rect_t tex_bounds;
op->render.source = gsk_vulkan_render_pass_get_node_as_texture (self,
render,
uploader,
child,
scale,
clip,
&op->render.offset,
&tex_bounds);
get_tex_rect (&op->render.source_rect, &op->render.node->bounds, &tex_bounds);
}
break;
case GSK_VULKAN_OP_COLOR_MATRIX:
{
GskRenderNode *child = gsk_color_matrix_node_get_child (op->render.node);
graphene_rect_t tex_bounds;
op->render.source = gsk_vulkan_render_pass_get_node_as_texture (self,
render,
uploader,
child,
scale,
clip,
&op->render.offset,
&tex_bounds);
get_tex_rect (&op->render.source_rect, &op->render.node->bounds, &tex_bounds);
}
break;
case GSK_VULKAN_OP_CROSS_FADE:
{
GskRenderNode *start = gsk_cross_fade_node_get_start_child (op->render.node);
GskRenderNode *end = gsk_cross_fade_node_get_end_child (op->render.node);
graphene_rect_t tex_bounds;
op->render.source = gsk_vulkan_render_pass_get_node_as_texture (self,
render,
uploader,
start,
scale,
clip,
&op->render.offset,
&tex_bounds);
get_tex_rect (&op->render.source_rect, &op->render.node->bounds, &tex_bounds);
op->render.source2 = gsk_vulkan_render_pass_get_node_as_texture (self,
render,
uploader,
end,
scale,
clip,
&op->render.offset,
&tex_bounds);
get_tex_rect (&op->render.source2_rect, &op->render.node->bounds, &tex_bounds);
}
break;
case GSK_VULKAN_OP_BLEND_MODE:
{
GskRenderNode *top = gsk_blend_node_get_top_child (op->render.node);
GskRenderNode *bottom = gsk_blend_node_get_bottom_child (op->render.node);
graphene_rect_t tex_bounds;
op->render.source = gsk_vulkan_render_pass_get_node_as_texture (self,
render,
uploader,
top,
scale,
clip,
&op->render.offset,
&tex_bounds);
get_tex_rect (&op->render.source_rect, &op->render.node->bounds, &tex_bounds);
op->render.source2 = gsk_vulkan_render_pass_get_node_as_texture (self,
render,
uploader,
bottom,
scale,
clip,
&op->render.offset,
&tex_bounds);
get_tex_rect (&op->render.source2_rect, &op->render.node->bounds, &tex_bounds);
}
break;
case GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS:
clip = &op->constants.clip.bounds;
scale = &op->constants.scale;
break;
default:
g_assert_not_reached ();
case GSK_VULKAN_OP_COLOR:
case GSK_VULKAN_OP_LINEAR_GRADIENT:
case GSK_VULKAN_OP_BORDER:
case GSK_VULKAN_OP_INSET_SHADOW:
case GSK_VULKAN_OP_OUTSET_SHADOW:
break;
}
}
}
static gsize
gsk_vulkan_render_pass_count_vertex_data (GskVulkanRenderPass *self)
{
GskVulkanOp *op;
gsize n_bytes, vertex_stride;
guint i;
n_bytes = 0;
for (i = 0; i < self->render_ops->len; i++)
{
op = &g_array_index (self->render_ops, GskVulkanOp, i);
switch (op->type)
{
case GSK_VULKAN_OP_FALLBACK:
case GSK_VULKAN_OP_FALLBACK_CLIP:
case GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP:
case GSK_VULKAN_OP_TEXTURE:
case GSK_VULKAN_OP_REPEAT:
case GSK_VULKAN_OP_COLOR:
case GSK_VULKAN_OP_LINEAR_GRADIENT:
case GSK_VULKAN_OP_OPACITY:
case GSK_VULKAN_OP_COLOR_MATRIX:
case GSK_VULKAN_OP_BLUR:
case GSK_VULKAN_OP_BORDER:
case GSK_VULKAN_OP_INSET_SHADOW:
case GSK_VULKAN_OP_OUTSET_SHADOW:
case GSK_VULKAN_OP_CROSS_FADE:
case GSK_VULKAN_OP_BLEND_MODE:
vertex_stride = gsk_vulkan_pipeline_get_vertex_stride (op->render.pipeline);
op->render.vertex_offset = n_bytes;
op->render.vertex_count = vertex_stride;
n_bytes += op->render.vertex_count;
break;
case GSK_VULKAN_OP_TEXT:
case GSK_VULKAN_OP_COLOR_TEXT:
vertex_stride = gsk_vulkan_pipeline_get_vertex_stride (op->render.pipeline);
op->text.vertex_offset = n_bytes;
op->text.vertex_count = vertex_stride * op->text.num_glyphs;
n_bytes += op->text.vertex_count;
break;
default:
g_assert_not_reached ();
case GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS:
continue;
}
}
return n_bytes;
}
static void
gsk_vulkan_render_pass_collect_vertex_data (GskVulkanRenderPass *self,
GskVulkanRender *render,
guchar *data)
{
GskVulkanOp *op;
guint i;
for (i = 0; i < self->render_ops->len; i++)
{
op = &g_array_index (self->render_ops, GskVulkanOp, i);
switch (op->type)
{
case GSK_VULKAN_OP_FALLBACK:
case GSK_VULKAN_OP_FALLBACK_CLIP:
case GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP:
case GSK_VULKAN_OP_TEXTURE:
gsk_vulkan_texture_pipeline_collect_vertex_data (GSK_VULKAN_TEXTURE_PIPELINE (op->render.pipeline),
data + op->render.vertex_offset,
&op->render.offset,
&op->render.node->bounds,
&op->render.source_rect);
break;
case GSK_VULKAN_OP_REPEAT:
gsk_vulkan_texture_pipeline_collect_vertex_data (GSK_VULKAN_TEXTURE_PIPELINE (op->render.pipeline),
data + op->render.vertex_offset,
&op->render.offset,
&op->render.node->bounds,
&op->render.source_rect);
break;
case GSK_VULKAN_OP_TEXT:
gsk_vulkan_text_pipeline_collect_vertex_data (GSK_VULKAN_TEXT_PIPELINE (op->text.pipeline),
data + op->text.vertex_offset,
GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render)),
&op->text.node->bounds,
(PangoFont *)gsk_text_node_get_font (op->text.node),
gsk_text_node_get_num_glyphs (op->text.node),
gsk_text_node_get_glyphs (op->text.node, NULL),
gsk_text_node_get_color (op->text.node),
&GRAPHENE_POINT_INIT (
gsk_text_node_get_offset (op->text.node)->x + op->render.offset.x,
gsk_text_node_get_offset (op->text.node)->y + op->render.offset.y
),
op->text.start_glyph,
op->text.num_glyphs,
op->text.scale);
break;
case GSK_VULKAN_OP_COLOR_TEXT:
gsk_vulkan_color_text_pipeline_collect_vertex_data (GSK_VULKAN_COLOR_TEXT_PIPELINE (op->text.pipeline),
data + op->text.vertex_offset,
GSK_VULKAN_RENDERER (gsk_vulkan_render_get_renderer (render)),
&op->text.node->bounds,
(PangoFont *)gsk_text_node_get_font (op->text.node),
gsk_text_node_get_num_glyphs (op->text.node),
gsk_text_node_get_glyphs (op->text.node, NULL),
&GRAPHENE_POINT_INIT (
gsk_text_node_get_offset (op->text.node)->x + op->render.offset.x,
gsk_text_node_get_offset (op->text.node)->y + op->render.offset.y
),
op->text.start_glyph,
op->text.num_glyphs,
op->text.scale);
break;
case GSK_VULKAN_OP_COLOR:
gsk_vulkan_color_pipeline_collect_vertex_data (GSK_VULKAN_COLOR_PIPELINE (op->render.pipeline),
data + op->render.vertex_offset,
&op->render.offset,
&op->render.node->bounds,
gsk_color_node_get_color (op->render.node));
break;
case GSK_VULKAN_OP_LINEAR_GRADIENT:
gsk_vulkan_linear_gradient_pipeline_collect_vertex_data (GSK_VULKAN_LINEAR_GRADIENT_PIPELINE (op->render.pipeline),
data + op->render.vertex_offset,
&op->render.offset,
&op->render.node->bounds,
gsk_linear_gradient_node_get_start (op->render.node),
gsk_linear_gradient_node_get_end (op->render.node),
gsk_render_node_get_node_type (op->render.node) == GSK_REPEATING_LINEAR_GRADIENT_NODE,
gsk_linear_gradient_node_get_n_color_stops (op->render.node),
gsk_linear_gradient_node_get_color_stops (op->render.node, NULL));
break;
case GSK_VULKAN_OP_OPACITY:
{
graphene_matrix_t color_matrix;
graphene_vec4_t color_offset;
graphene_matrix_init_from_float (&color_matrix,
(float[16]) {
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, gsk_opacity_node_get_opacity (op->render.node)
});
graphene_vec4_init (&color_offset, 0.0, 0.0, 0.0, 0.0);
gsk_vulkan_effect_pipeline_collect_vertex_data (GSK_VULKAN_EFFECT_PIPELINE (op->render.pipeline),
data + op->render.vertex_offset,
&op->render.offset,
&op->render.node->bounds,
&op->render.source_rect,
&color_matrix,
&color_offset);
}
break;
case GSK_VULKAN_OP_BLUR:
gsk_vulkan_blur_pipeline_collect_vertex_data (GSK_VULKAN_BLUR_PIPELINE (op->render.pipeline),
data + op->render.vertex_offset,
&op->render.offset,
&op->render.node->bounds,
&op->render.source_rect,
gsk_blur_node_get_radius (op->render.node));
break;
case GSK_VULKAN_OP_COLOR_MATRIX:
gsk_vulkan_effect_pipeline_collect_vertex_data (GSK_VULKAN_EFFECT_PIPELINE (op->render.pipeline),
data + op->render.vertex_offset,
&op->render.offset,
&op->render.node->bounds,
&op->render.source_rect,
gsk_color_matrix_node_get_color_matrix (op->render.node),
gsk_color_matrix_node_get_color_offset (op->render.node));
break;
case GSK_VULKAN_OP_BORDER:
gsk_vulkan_border_pipeline_collect_vertex_data (GSK_VULKAN_BORDER_PIPELINE (op->render.pipeline),
data + op->render.vertex_offset,
&op->render.offset,
gsk_border_node_get_outline (op->render.node),
gsk_border_node_get_widths (op->render.node),
gsk_border_node_get_colors (op->render.node));
break;
case GSK_VULKAN_OP_INSET_SHADOW:
gsk_vulkan_box_shadow_pipeline_collect_vertex_data (GSK_VULKAN_BOX_SHADOW_PIPELINE (op->render.pipeline),
data + op->render.vertex_offset,
&op->render.offset,
gsk_inset_shadow_node_get_outline (op->render.node),
gsk_inset_shadow_node_get_color (op->render.node),
gsk_inset_shadow_node_get_dx (op->render.node),
gsk_inset_shadow_node_get_dy (op->render.node),
gsk_inset_shadow_node_get_spread (op->render.node),
gsk_inset_shadow_node_get_blur_radius (op->render.node));
break;
case GSK_VULKAN_OP_OUTSET_SHADOW:
gsk_vulkan_box_shadow_pipeline_collect_vertex_data (GSK_VULKAN_BOX_SHADOW_PIPELINE (op->render.pipeline),
data + op->render.vertex_offset,
&op->render.offset,
gsk_outset_shadow_node_get_outline (op->render.node),
gsk_outset_shadow_node_get_color (op->render.node),
gsk_outset_shadow_node_get_dx (op->render.node),
gsk_outset_shadow_node_get_dy (op->render.node),
gsk_outset_shadow_node_get_spread (op->render.node),
gsk_outset_shadow_node_get_blur_radius (op->render.node));
break;
case GSK_VULKAN_OP_CROSS_FADE:
gsk_vulkan_cross_fade_pipeline_collect_vertex_data (GSK_VULKAN_CROSS_FADE_PIPELINE (op->render.pipeline),
data + op->render.vertex_offset,
&op->render.offset,
&op->render.node->bounds,
&op->render.source_rect,
&op->render.source2_rect,
gsk_cross_fade_node_get_progress (op->render.node));
break;
case GSK_VULKAN_OP_BLEND_MODE:
gsk_vulkan_blend_mode_pipeline_collect_vertex_data (GSK_VULKAN_BLEND_MODE_PIPELINE (op->render.pipeline),
data + op->render.vertex_offset,
&op->render.offset,
&op->render.node->bounds,
&op->render.source_rect,
&op->render.source2_rect,
gsk_blend_node_get_blend_mode (op->render.node));
break;
default:
g_assert_not_reached ();
case GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS:
continue;
}
}
}
static GskVulkanBuffer *
gsk_vulkan_render_pass_get_vertex_data (GskVulkanRenderPass *self,
GskVulkanRender *render)
{
if (self->vertex_data == NULL)
{
gsize n_bytes;
guchar *data;
n_bytes = gsk_vulkan_render_pass_count_vertex_data (self);
if (n_bytes == 0)
return NULL;
self->vertex_data = gsk_vulkan_buffer_new (self->vulkan, n_bytes);
data = gsk_vulkan_buffer_map (self->vertex_data);
gsk_vulkan_render_pass_collect_vertex_data (self, render, data);
gsk_vulkan_buffer_unmap (self->vertex_data);
}
return self->vertex_data;
}
gsize
gsk_vulkan_render_pass_get_wait_semaphores (GskVulkanRenderPass *self,
VkSemaphore **semaphores)
{
*semaphores = (VkSemaphore *)self->wait_semaphores->data;
return self->wait_semaphores->len;
}
gsize
gsk_vulkan_render_pass_get_signal_semaphores (GskVulkanRenderPass *self,
VkSemaphore **semaphores)
{
*semaphores = (VkSemaphore *)&self->signal_semaphore;
return self->signal_semaphore != VK_NULL_HANDLE ? 1 : 0;
}
void
gsk_vulkan_render_pass_reserve_descriptor_sets (GskVulkanRenderPass *self,
GskVulkanRender *render)
{
GskVulkanOp *op;
guint i;
for (i = 0; i < self->render_ops->len; i++)
{
op = &g_array_index (self->render_ops, GskVulkanOp, i);
switch (op->type)
{
case GSK_VULKAN_OP_FALLBACK:
case GSK_VULKAN_OP_FALLBACK_CLIP:
case GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP:
case GSK_VULKAN_OP_TEXTURE:
case GSK_VULKAN_OP_OPACITY:
case GSK_VULKAN_OP_BLUR:
case GSK_VULKAN_OP_COLOR_MATRIX:
if (op->render.source)
op->render.descriptor_set_index = gsk_vulkan_render_reserve_descriptor_set (render, op->render.source, FALSE);
break;
case GSK_VULKAN_OP_REPEAT:
if (op->render.source)
op->render.descriptor_set_index = gsk_vulkan_render_reserve_descriptor_set (render, op->render.source, TRUE);
break;
case GSK_VULKAN_OP_TEXT:
case GSK_VULKAN_OP_COLOR_TEXT:
op->text.descriptor_set_index = gsk_vulkan_render_reserve_descriptor_set (render, op->text.source, FALSE);
break;
case GSK_VULKAN_OP_CROSS_FADE:
case GSK_VULKAN_OP_BLEND_MODE:
if (op->render.source && op->render.source2)
{
op->render.descriptor_set_index = gsk_vulkan_render_reserve_descriptor_set (render, op->render.source, FALSE);
op->render.descriptor_set_index2 = gsk_vulkan_render_reserve_descriptor_set (render, op->render.source2, FALSE);
}
break;
default:
g_assert_not_reached ();
case GSK_VULKAN_OP_COLOR:
case GSK_VULKAN_OP_LINEAR_GRADIENT:
case GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS:
case GSK_VULKAN_OP_BORDER:
case GSK_VULKAN_OP_INSET_SHADOW:
case GSK_VULKAN_OP_OUTSET_SHADOW:
break;
}
}
}
static void
gsk_vulkan_render_pass_draw_rect (GskVulkanRenderPass *self,
GskVulkanRender *render,
VkPipelineLayout pipeline_layout,
VkCommandBuffer command_buffer)
{
GskVulkanPipeline *current_pipeline = NULL;
gsize current_draw_index = 0;
GskVulkanOp *op;
guint i, step;
GskVulkanBuffer *vertex_buffer;
vertex_buffer = gsk_vulkan_render_pass_get_vertex_data (self, render);
for (i = 0; i < self->render_ops->len; i += step)
{
op = &g_array_index (self->render_ops, GskVulkanOp, i);
step = 1;
switch (op->type)
{
case GSK_VULKAN_OP_FALLBACK:
case GSK_VULKAN_OP_FALLBACK_CLIP:
case GSK_VULKAN_OP_FALLBACK_ROUNDED_CLIP:
case GSK_VULKAN_OP_TEXTURE:
case GSK_VULKAN_OP_REPEAT:
if (!op->render.source)
continue;
if (current_pipeline != op->render.pipeline)
{
current_pipeline = op->render.pipeline;
vkCmdBindPipeline (command_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
gsk_vulkan_pipeline_get_pipeline (current_pipeline));
vkCmdBindVertexBuffers (command_buffer,
0,
1,
(VkBuffer[1]) {
gsk_vulkan_buffer_get_buffer (vertex_buffer)
},
(VkDeviceSize[1]) { op->render.vertex_offset });
current_draw_index = 0;
}
vkCmdBindDescriptorSets (command_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline_layout,
0,
1,
(VkDescriptorSet[1]) {
gsk_vulkan_render_get_descriptor_set (render, op->render.descriptor_set_index)
},
0,
NULL);
current_draw_index += gsk_vulkan_texture_pipeline_draw (GSK_VULKAN_TEXTURE_PIPELINE (current_pipeline),
command_buffer,
current_draw_index, 1);
break;
case GSK_VULKAN_OP_TEXT:
if (current_pipeline != op->text.pipeline)
{
current_pipeline = op->text.pipeline;
vkCmdBindPipeline (command_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
gsk_vulkan_pipeline_get_pipeline (current_pipeline));
vkCmdBindVertexBuffers (command_buffer,
0,
1,
(VkBuffer[1]) {
gsk_vulkan_buffer_get_buffer (vertex_buffer)
},
(VkDeviceSize[1]) { op->text.vertex_offset });
current_draw_index = 0;
}
vkCmdBindDescriptorSets (command_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline_layout,
0,
1,
(VkDescriptorSet[1]) {
gsk_vulkan_render_get_descriptor_set (render, op->text.descriptor_set_index)
},
0,
NULL);
current_draw_index += gsk_vulkan_text_pipeline_draw (GSK_VULKAN_TEXT_PIPELINE (current_pipeline),
command_buffer,
current_draw_index, op->text.num_glyphs);
break;
case GSK_VULKAN_OP_COLOR_TEXT:
if (current_pipeline != op->text.pipeline)
{
current_pipeline = op->text.pipeline;
vkCmdBindPipeline (command_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
gsk_vulkan_pipeline_get_pipeline (current_pipeline));
vkCmdBindVertexBuffers (command_buffer,
0,
1,
(VkBuffer[1]) {
gsk_vulkan_buffer_get_buffer (vertex_buffer)
},
(VkDeviceSize[1]) { op->text.vertex_offset });
current_draw_index = 0;
}
vkCmdBindDescriptorSets (command_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline_layout,
0,
1,
(VkDescriptorSet[1]) {
gsk_vulkan_render_get_descriptor_set (render, op->text.descriptor_set_index)
},
0,
NULL);
current_draw_index += gsk_vulkan_color_text_pipeline_draw (GSK_VULKAN_COLOR_TEXT_PIPELINE (current_pipeline),
command_buffer,
current_draw_index, op->text.num_glyphs);
break;
case GSK_VULKAN_OP_OPACITY:
case GSK_VULKAN_OP_COLOR_MATRIX:
if (!op->render.source)
continue;
if (current_pipeline != op->render.pipeline)
{
current_pipeline = op->render.pipeline;
vkCmdBindPipeline (command_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
gsk_vulkan_pipeline_get_pipeline (current_pipeline));
vkCmdBindVertexBuffers (command_buffer,
0,
1,
(VkBuffer[1]) {
gsk_vulkan_buffer_get_buffer (vertex_buffer)
},
(VkDeviceSize[1]) { op->render.vertex_offset });
current_draw_index = 0;
}
vkCmdBindDescriptorSets (command_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline_layout,
0,
1,
(VkDescriptorSet[1]) {
gsk_vulkan_render_get_descriptor_set (render, op->render.descriptor_set_index)
},
0,
NULL);
current_draw_index += gsk_vulkan_effect_pipeline_draw (GSK_VULKAN_EFFECT_PIPELINE (current_pipeline),
command_buffer,
current_draw_index, 1);
break;
case GSK_VULKAN_OP_BLUR:
if (!op->render.source)
continue;
if (current_pipeline != op->render.pipeline)
{
current_pipeline = op->render.pipeline;
vkCmdBindPipeline (command_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
gsk_vulkan_pipeline_get_pipeline (current_pipeline));
vkCmdBindVertexBuffers (command_buffer,
0,
1,
(VkBuffer[1]) {
gsk_vulkan_buffer_get_buffer (vertex_buffer)
},
(VkDeviceSize[1]) { op->render.vertex_offset });
current_draw_index = 0;
}
vkCmdBindDescriptorSets (command_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline_layout,
0,
1,
(VkDescriptorSet[1]) {
gsk_vulkan_render_get_descriptor_set (render, op->render.descriptor_set_index)
},
0,
NULL);
current_draw_index += gsk_vulkan_blur_pipeline_draw (GSK_VULKAN_BLUR_PIPELINE (current_pipeline),
command_buffer,
current_draw_index, 1);
break;
case GSK_VULKAN_OP_COLOR:
if (current_pipeline != op->render.pipeline)
{
current_pipeline = op->render.pipeline;
vkCmdBindPipeline (command_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
gsk_vulkan_pipeline_get_pipeline (current_pipeline));
vkCmdBindVertexBuffers (command_buffer,
0,
1,
(VkBuffer[1]) {
gsk_vulkan_buffer_get_buffer (vertex_buffer)
},
(VkDeviceSize[1]) { op->render.vertex_offset });
current_draw_index = 0;
}
for (step = 1; step + i < self->render_ops->len; step++)
{
GskVulkanOp *cmp = &g_array_index (self->render_ops, GskVulkanOp, i + step);
if (cmp->type != GSK_VULKAN_OP_COLOR ||
cmp->render.pipeline != current_pipeline)
break;
}
current_draw_index += gsk_vulkan_color_pipeline_draw (GSK_VULKAN_COLOR_PIPELINE (current_pipeline),
command_buffer,
current_draw_index, step);
break;
case GSK_VULKAN_OP_LINEAR_GRADIENT:
if (current_pipeline != op->render.pipeline)
{
current_pipeline = op->render.pipeline;
vkCmdBindPipeline (command_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
gsk_vulkan_pipeline_get_pipeline (current_pipeline));
vkCmdBindVertexBuffers (command_buffer,
0,
1,
(VkBuffer[1]) {
gsk_vulkan_buffer_get_buffer (vertex_buffer)
},
(VkDeviceSize[1]) { op->render.vertex_offset });
current_draw_index = 0;
}
current_draw_index += gsk_vulkan_linear_gradient_pipeline_draw (GSK_VULKAN_LINEAR_GRADIENT_PIPELINE (current_pipeline),
command_buffer,
current_draw_index, 1);
break;
case GSK_VULKAN_OP_BORDER:
if (current_pipeline != op->render.pipeline)
{
current_pipeline = op->render.pipeline;
vkCmdBindPipeline (command_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
gsk_vulkan_pipeline_get_pipeline (current_pipeline));
vkCmdBindVertexBuffers (command_buffer,
0,
1,
(VkBuffer[1]) {
gsk_vulkan_buffer_get_buffer (vertex_buffer)
},
(VkDeviceSize[1]) { op->render.vertex_offset });
current_draw_index = 0;
}
current_draw_index += gsk_vulkan_border_pipeline_draw (GSK_VULKAN_BORDER_PIPELINE (current_pipeline),
command_buffer,
current_draw_index, 1);
break;
case GSK_VULKAN_OP_INSET_SHADOW:
case GSK_VULKAN_OP_OUTSET_SHADOW:
if (current_pipeline != op->render.pipeline)
{
current_pipeline = op->render.pipeline;
vkCmdBindPipeline (command_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
gsk_vulkan_pipeline_get_pipeline (current_pipeline));
vkCmdBindVertexBuffers (command_buffer,
0,
1,
(VkBuffer[1]) {
gsk_vulkan_buffer_get_buffer (vertex_buffer)
},
(VkDeviceSize[1]) { op->render.vertex_offset });
current_draw_index = 0;
}
current_draw_index += gsk_vulkan_box_shadow_pipeline_draw (GSK_VULKAN_BOX_SHADOW_PIPELINE (current_pipeline),
command_buffer,
current_draw_index, 1);
break;
case GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS:
gsk_vulkan_push_constants_push (command_buffer,
pipeline_layout,
&op->constants.scale,
&op->constants.mvp,
&op->constants.clip);
break;
case GSK_VULKAN_OP_CROSS_FADE:
if (!op->render.source || !op->render.source2)
continue;
if (current_pipeline != op->render.pipeline)
{
current_pipeline = op->render.pipeline;
vkCmdBindPipeline (command_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
gsk_vulkan_pipeline_get_pipeline (current_pipeline));
vkCmdBindVertexBuffers (command_buffer,
0,
1,
(VkBuffer[1]) {
gsk_vulkan_buffer_get_buffer (vertex_buffer)
},
(VkDeviceSize[1]) { op->render.vertex_offset });
current_draw_index = 0;
}
vkCmdBindDescriptorSets (command_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline_layout,
0,
2,
(VkDescriptorSet[2]) {
gsk_vulkan_render_get_descriptor_set (render, op->render.descriptor_set_index),
gsk_vulkan_render_get_descriptor_set (render, op->render.descriptor_set_index2)
},
0,
NULL);
current_draw_index += gsk_vulkan_cross_fade_pipeline_draw (GSK_VULKAN_CROSS_FADE_PIPELINE (current_pipeline),
command_buffer,
current_draw_index, 1);
break;
case GSK_VULKAN_OP_BLEND_MODE:
if (!op->render.source || !op->render.source2)
continue;
if (current_pipeline != op->render.pipeline)
{
current_pipeline = op->render.pipeline;
vkCmdBindPipeline (command_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
gsk_vulkan_pipeline_get_pipeline (current_pipeline));
vkCmdBindVertexBuffers (command_buffer,
0,
1,
(VkBuffer[1]) {
gsk_vulkan_buffer_get_buffer (vertex_buffer)
},
(VkDeviceSize[1]) { op->render.vertex_offset });
current_draw_index = 0;
}
vkCmdBindDescriptorSets (command_buffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline_layout,
0,
2,
(VkDescriptorSet[2]) {
gsk_vulkan_render_get_descriptor_set (render, op->render.descriptor_set_index),
gsk_vulkan_render_get_descriptor_set (render, op->render.descriptor_set_index2)
},
0,
NULL);
current_draw_index += gsk_vulkan_blend_mode_pipeline_draw (GSK_VULKAN_BLEND_MODE_PIPELINE (current_pipeline),
command_buffer,
current_draw_index, 1);
break;
default:
g_assert_not_reached ();
break;
}
}
}
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);
vkCmdSetScissor (command_buffer,
0,
1,
&(VkRect2D) {
{ rect.x, rect.y },
{ rect.width, rect.height }
});
vkCmdBeginRenderPass (command_buffer,
&(VkRenderPassBeginInfo) {
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
.renderPass = self->render_pass,
.framebuffer = gsk_vulkan_render_get_framebuffer (render, self->target),
.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_pass_draw_rect (self, render, pipeline_layout, command_buffer);
vkCmdEndRenderPass (command_buffer);
}