diff options
author | Matthias Clasen <mclasen@redhat.com> | 2017-09-23 01:59:50 -0400 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2017-09-23 01:59:50 -0400 |
commit | d61f6ff39306efff512b4e4b80c642ca2acf7dce (patch) | |
tree | a1df63c2c19a228157b72bcb0c74b9059bbc15e4 /gsk | |
parent | b192120f39206a546de7190901e79a62133b56aa (diff) | |
download | gtk+-d61f6ff39306efff512b4e4b80c642ca2acf7dce.tar.gz |
vulkan: Implement blend modes
This is another example for a 2-texture shader.
So far, only separable blend modes are implemented.
The implementation is not optimized, with an
if-else cascade in the shader.
Diffstat (limited to 'gsk')
-rw-r--r-- | gsk/gskvulkanblendmodepipeline.c | 146 | ||||
-rw-r--r-- | gsk/gskvulkanblendmodepipelineprivate.h | 37 | ||||
-rw-r--r-- | gsk/gskvulkanrender.c | 4 | ||||
-rw-r--r-- | gsk/gskvulkanrenderpass.c | 96 | ||||
-rw-r--r-- | gsk/gskvulkanrenderprivate.h | 3 | ||||
-rw-r--r-- | gsk/meson.build | 1 | ||||
-rw-r--r-- | gsk/resources/vulkan/blendmode-clip-rounded.frag.spv | bin | 0 -> 22744 bytes | |||
-rw-r--r-- | gsk/resources/vulkan/blendmode-clip-rounded.vert.spv | bin | 0 -> 5856 bytes | |||
-rw-r--r-- | gsk/resources/vulkan/blendmode-clip.frag.spv | bin | 0 -> 15972 bytes | |||
-rw-r--r-- | gsk/resources/vulkan/blendmode-clip.vert.spv | bin | 0 -> 5856 bytes | |||
-rw-r--r-- | gsk/resources/vulkan/blendmode-rect-rounded.frag.spv | bin | 0 -> 22744 bytes | |||
-rw-r--r-- | gsk/resources/vulkan/blendmode-rect.frag.spv | bin | 0 -> 15972 bytes | |||
-rw-r--r-- | gsk/resources/vulkan/blendmode.frag | 183 | ||||
-rw-r--r-- | gsk/resources/vulkan/blendmode.frag.spv | bin | 0 -> 15972 bytes | |||
-rw-r--r-- | gsk/resources/vulkan/blendmode.vert | 44 | ||||
-rw-r--r-- | gsk/resources/vulkan/blendmode.vert.spv | bin | 0 -> 4124 bytes | |||
-rw-r--r-- | gsk/resources/vulkan/meson.build | 2 |
17 files changed, 515 insertions, 1 deletions
diff --git a/gsk/gskvulkanblendmodepipeline.c b/gsk/gskvulkanblendmodepipeline.c new file mode 100644 index 0000000000..062921e0b2 --- /dev/null +++ b/gsk/gskvulkanblendmodepipeline.c @@ -0,0 +1,146 @@ +#include "config.h" + +#include "gskvulkanblendmodepipelineprivate.h" + +struct _GskVulkanBlendModePipeline +{ + GObject parent_instance; +}; + +typedef struct _GskVulkanBlendModeInstance GskVulkanBlendModeInstance; + +struct _GskVulkanBlendModeInstance +{ + float rect[4]; + float start_tex_rect[4]; + float end_tex_rect[4]; + guint32 blend_mode; +}; + +G_DEFINE_TYPE (GskVulkanBlendModePipeline, gsk_vulkan_blend_mode_pipeline, GSK_TYPE_VULKAN_PIPELINE) + +static const VkPipelineVertexInputStateCreateInfo * +gsk_vulkan_blend_mode_pipeline_get_input_state_create_info (GskVulkanPipeline *self) +{ + static const VkVertexInputBindingDescription vertexBindingDescriptions[] = { + { + .binding = 0, + .stride = sizeof (GskVulkanBlendModeInstance), + .inputRate = VK_VERTEX_INPUT_RATE_INSTANCE + } + }; + static const VkVertexInputAttributeDescription vertexInputAttributeDescription[] = { + { + .location = 0, + .binding = 0, + .format = VK_FORMAT_R32G32B32A32_SFLOAT, + .offset = 0, + }, + { + .location = 1, + .binding = 0, + .format = VK_FORMAT_R32G32B32A32_SFLOAT, + .offset = G_STRUCT_OFFSET (GskVulkanBlendModeInstance, start_tex_rect), + }, + { + .location = 2, + .binding = 0, + .format = VK_FORMAT_R32G32B32A32_SFLOAT, + .offset = G_STRUCT_OFFSET (GskVulkanBlendModeInstance, end_tex_rect), + }, + { + .location = 3, + .binding = 0, + .format = VK_FORMAT_R32_UINT, + .offset = G_STRUCT_OFFSET (GskVulkanBlendModeInstance, blend_mode), + } + }; + static const VkPipelineVertexInputStateCreateInfo info = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + .vertexBindingDescriptionCount = G_N_ELEMENTS (vertexBindingDescriptions), + .pVertexBindingDescriptions = vertexBindingDescriptions, + .vertexAttributeDescriptionCount = G_N_ELEMENTS (vertexInputAttributeDescription), + .pVertexAttributeDescriptions = vertexInputAttributeDescription + }; + + return &info; +} + +static void +gsk_vulkan_blend_mode_pipeline_finalize (GObject *gobject) +{ + //GskVulkanBlendModePipeline *self = GSK_VULKAN_BLUR_PIPELINE (gobject); + + G_OBJECT_CLASS (gsk_vulkan_blend_mode_pipeline_parent_class)->finalize (gobject); +} + +static void +gsk_vulkan_blend_mode_pipeline_class_init (GskVulkanBlendModePipelineClass *klass) +{ + GskVulkanPipelineClass *pipeline_class = GSK_VULKAN_PIPELINE_CLASS (klass); + + G_OBJECT_CLASS (klass)->finalize = gsk_vulkan_blend_mode_pipeline_finalize; + + pipeline_class->get_input_state_create_info = gsk_vulkan_blend_mode_pipeline_get_input_state_create_info; +} + +static void +gsk_vulkan_blend_mode_pipeline_init (GskVulkanBlendModePipeline *self) +{ +} + +GskVulkanPipeline * +gsk_vulkan_blend_mode_pipeline_new (GdkVulkanContext *context, + VkPipelineLayout layout, + const char *shader_name, + VkRenderPass render_pass) +{ + return gsk_vulkan_pipeline_new (GSK_TYPE_VULKAN_BLEND_MODE_PIPELINE, context, layout, shader_name, render_pass); +} + +gsize +gsk_vulkan_blend_mode_pipeline_count_vertex_data (GskVulkanBlendModePipeline *pipeline) +{ + return sizeof (GskVulkanBlendModeInstance); +} + +void +gsk_vulkan_blend_mode_pipeline_collect_vertex_data (GskVulkanBlendModePipeline *pipeline, + guchar *data, + const graphene_rect_t *bounds, + const graphene_rect_t *start_bounds, + const graphene_rect_t *end_bounds, + GskBlendMode blend_mode) +{ + GskVulkanBlendModeInstance *instance = (GskVulkanBlendModeInstance *) data; + + instance->rect[0] = bounds->origin.x; + instance->rect[1] = bounds->origin.y; + instance->rect[2] = bounds->size.width; + instance->rect[3] = bounds->size.height; + + instance->start_tex_rect[0] = (bounds->origin.x - start_bounds->origin.x)/start_bounds->size.width; + instance->start_tex_rect[1] = (bounds->origin.y - start_bounds->origin.y)/start_bounds->size.height; + instance->start_tex_rect[2] = (bounds->size.width + bounds->origin.x - start_bounds->origin.x)/start_bounds->size.width; + instance->start_tex_rect[3] = (bounds->size.height + bounds->origin.y - start_bounds->origin.y)/start_bounds->size.height; + + instance->end_tex_rect[0] = (bounds->origin.x - end_bounds->origin.x)/end_bounds->size.width; + instance->end_tex_rect[1] = (bounds->origin.y - end_bounds->origin.y)/end_bounds->size.height; + instance->end_tex_rect[2] = (bounds->size.width + bounds->origin.x - end_bounds->origin.x)/end_bounds->size.width; + instance->end_tex_rect[3] = (bounds->size.height + bounds->origin.y - end_bounds->origin.y)/end_bounds->size.height; + + instance->blend_mode = blend_mode; +} + +gsize +gsk_vulkan_blend_mode_pipeline_draw (GskVulkanBlendModePipeline *pipeline, + VkCommandBuffer command_buffer, + gsize offset, + gsize n_commands) +{ + vkCmdDraw (command_buffer, + 6, n_commands, + 0, offset); + + return n_commands; +} diff --git a/gsk/gskvulkanblendmodepipelineprivate.h b/gsk/gskvulkanblendmodepipelineprivate.h new file mode 100644 index 0000000000..67822e4f9b --- /dev/null +++ b/gsk/gskvulkanblendmodepipelineprivate.h @@ -0,0 +1,37 @@ +#ifndef __GSK_VULKAN_BLEND_MODE_PIPELINE_PRIVATE_H__ +#define __GSK_VULKAN_BLEND_MODE_PIPELINE_PRIVATE_H__ + +#include <graphene.h> + +#include "gskvulkanpipelineprivate.h" +#include "gskenums.h" + +G_BEGIN_DECLS + +typedef struct _GskVulkanBlendModePipelineLayout GskVulkanBlendModePipelineLayout; + +#define GSK_TYPE_VULKAN_BLEND_MODE_PIPELINE (gsk_vulkan_blend_mode_pipeline_get_type ()) + +G_DECLARE_FINAL_TYPE (GskVulkanBlendModePipeline, gsk_vulkan_blend_mode_pipeline, GSK, VULKAN_BLEND_MODE_PIPELINE, GskVulkanPipeline) + +GskVulkanPipeline * gsk_vulkan_blend_mode_pipeline_new (GdkVulkanContext *context, + VkPipelineLayout layout, + const char *shader_name, + VkRenderPass render_pass); + +gsize gsk_vulkan_blend_mode_pipeline_count_vertex_data (GskVulkanBlendModePipeline *pipeline); +void gsk_vulkan_blend_mode_pipeline_collect_vertex_data (GskVulkanBlendModePipeline *pipeline, + guchar *data, + const graphene_rect_t *bounds, + const graphene_rect_t *start_bounds, + const graphene_rect_t *end_bounds, + GskBlendMode blend_mode); +gsize gsk_vulkan_blend_mode_pipeline_draw (GskVulkanBlendModePipeline *pipeline, + VkCommandBuffer command_buffer, + gsize offset, + gsize n_commands); + + +G_END_DECLS + +#endif /* __GSK_VULKAN_BLEND_MODE_PIPELINE_PRIVATE_H__ */ diff --git a/gsk/gskvulkanrender.c b/gsk/gskvulkanrender.c index 06b622db59..007f3d4ac2 100644 --- a/gsk/gskvulkanrender.c +++ b/gsk/gskvulkanrender.c @@ -10,6 +10,7 @@ #include "gskvulkanpipelineprivate.h" #include "gskvulkanrenderpassprivate.h" +#include "gskvulkanblendmodepipelineprivate.h" #include "gskvulkanblendpipelineprivate.h" #include "gskvulkanblurpipelineprivate.h" #include "gskvulkanborderpipelineprivate.h" @@ -392,6 +393,9 @@ gsk_vulkan_render_get_pipeline (GskVulkanRender *self, { "crossfade", 2, gsk_vulkan_cross_fade_pipeline_new }, { "crossfade-clip", 2, gsk_vulkan_cross_fade_pipeline_new }, { "crossfade-clip-rounded", 2, gsk_vulkan_cross_fade_pipeline_new }, + { "blendmode", 2, gsk_vulkan_blend_mode_pipeline_new }, + { "blendmode-clip", 2, gsk_vulkan_blend_mode_pipeline_new }, + { "blendmode-clip-rounded", 2, gsk_vulkan_blend_mode_pipeline_new }, }; g_return_val_if_fail (type < GSK_VULKAN_N_PIPELINES, NULL); diff --git a/gsk/gskvulkanrenderpass.c b/gsk/gskvulkanrenderpass.c index 866a4eaa73..3f9c7aa0c6 100644 --- a/gsk/gskvulkanrenderpass.c +++ b/gsk/gskvulkanrenderpass.c @@ -6,6 +6,7 @@ #include "gskrendernodeprivate.h" #include "gskrenderer.h" #include "gskroundedrectprivate.h" +#include "gskvulkanblendmodepipelineprivate.h" #include "gskvulkanblendpipelineprivate.h" #include "gskvulkanblurpipelineprivate.h" #include "gskvulkanborderpipelineprivate.h" @@ -50,6 +51,7 @@ typedef enum { GSK_VULKAN_OP_COLOR_TEXT, /* GskVulkanOpCrossFade */ GSK_VULKAN_OP_CROSS_FADE, + GSK_VULKAN_OP_BLEND_MODE, /* GskVulkanOpPushConstants */ GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS, } GskVulkanOpType; @@ -180,10 +182,28 @@ gsk_vulkan_render_pass_add_node (GskVulkanRenderPass *self, return; case GSK_REPEAT_NODE: case GSK_SHADOW_NODE: - case GSK_BLEND_NODE: default: FALLBACK ("Unsupported node '%s'\n", node->node_class->type_name); + case GSK_BLEND_NODE: + if (gsk_blend_node_get_blend_mode (node) == GSK_BLEND_MODE_COLOR || + gsk_blend_node_get_blend_mode (node) == GSK_BLEND_MODE_HUE || + gsk_blend_node_get_blend_mode (node) == GSK_BLEND_MODE_SATURATION || + gsk_blend_node_get_blend_mode (node) == GSK_BLEND_MODE_LUMINOSITY) + FALLBACK ("Nonseparable blend modes not implemented\n"); + if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) + pipeline_type = GSK_VULKAN_PIPELINE_BLEND_MODE; + else if (constants->clip.type == GSK_VULKAN_CLIP_RECT) + pipeline_type = GSK_VULKAN_PIPELINE_BLEND_MODE_CLIP; + else if (constants->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\n", constants->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; + case GSK_CROSS_FADE_NODE: if (gsk_vulkan_clip_contains_rect (&constants->clip, &node->bounds)) pipeline_type = GSK_VULKAN_PIPELINE_CROSS_FADE; @@ -758,6 +778,24 @@ gsk_vulkan_render_pass_upload (GskVulkanRenderPass *self, } break; + case GSK_VULKAN_OP_BLEND_MODE: + { + GskRenderNode *top = gsk_blend_node_get_top_child (op->crossfade.node); + GskRenderNode *bottom = gsk_blend_node_get_bottom_child (op->crossfade.node); + + op->crossfade.start = gsk_vulkan_render_pass_get_node_as_texture (self, + render, + uploader, + top, + &top->bounds); + op->crossfade.end = gsk_vulkan_render_pass_get_node_as_texture (self, + render, + uploader, + bottom, + &bottom->bounds); + } + break; + default: g_assert_not_reached (); case GSK_VULKAN_OP_COLOR: @@ -843,6 +881,11 @@ gsk_vulkan_render_pass_count_vertex_data (GskVulkanRenderPass *self) n_bytes += op->crossfade.vertex_count; break; + case GSK_VULKAN_OP_BLEND_MODE: + op->crossfade.vertex_count = gsk_vulkan_blend_mode_pipeline_count_vertex_data (GSK_VULKAN_BLEND_MODE_PIPELINE (op->crossfade.pipeline)); + n_bytes += op->crossfade.vertex_count; + break; + default: g_assert_not_reached (); case GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS: @@ -1049,6 +1092,22 @@ gsk_vulkan_render_pass_collect_vertex_data (GskVulkanRenderPass *self, } break; + case GSK_VULKAN_OP_BLEND_MODE: + { + GskRenderNode *top = gsk_blend_node_get_top_child (op->crossfade.node); + GskRenderNode *bottom = gsk_blend_node_get_bottom_child (op->crossfade.node); + + op->crossfade.vertex_offset = offset + n_bytes; + gsk_vulkan_blend_mode_pipeline_collect_vertex_data (GSK_VULKAN_BLEND_MODE_PIPELINE (op->crossfade.pipeline), + data + n_bytes + offset, + &op->crossfade.node->bounds, + &top->bounds, + &bottom->bounds, + gsk_blend_node_get_blend_mode (op->crossfade.node)); + n_bytes += op->crossfade.vertex_count; + } + break; + default: g_assert_not_reached (); case GSK_VULKAN_OP_PUSH_VERTEX_CONSTANTS: @@ -1091,6 +1150,7 @@ gsk_vulkan_render_pass_reserve_descriptor_sets (GskVulkanRenderPass *self, break; case GSK_VULKAN_OP_CROSS_FADE: + case GSK_VULKAN_OP_BLEND_MODE: op->crossfade.descriptor_set_start = gsk_vulkan_render_reserve_descriptor_set (render, op->crossfade.start); op->crossfade.descriptor_set_end = gsk_vulkan_render_reserve_descriptor_set (render, op->crossfade.end); break; @@ -1432,6 +1492,40 @@ gsk_vulkan_render_pass_draw (GskVulkanRenderPass *self, current_draw_index, 1); break; + case GSK_VULKAN_OP_BLEND_MODE: + if (current_pipeline != op->crossfade.pipeline) + { + current_pipeline = op->crossfade.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->crossfade.vertex_offset }); + current_draw_index = 0; + } + + vkCmdBindDescriptorSets (command_buffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, + gsk_vulkan_pipeline_get_pipeline_layout (current_pipeline), + 0, + 2, + (VkDescriptorSet[2]) { + gsk_vulkan_render_get_descriptor_set (render, op->crossfade.descriptor_set_start), + gsk_vulkan_render_get_descriptor_set (render, op->crossfade.descriptor_set_end) + }, + 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; diff --git a/gsk/gskvulkanrenderprivate.h b/gsk/gskvulkanrenderprivate.h index 6739bb5941..ed35019c3f 100644 --- a/gsk/gskvulkanrenderprivate.h +++ b/gsk/gskvulkanrenderprivate.h @@ -43,6 +43,9 @@ typedef enum { GSK_VULKAN_PIPELINE_CROSS_FADE, GSK_VULKAN_PIPELINE_CROSS_FADE_CLIP, GSK_VULKAN_PIPELINE_CROSS_FADE_CLIP_ROUNDED, + GSK_VULKAN_PIPELINE_BLEND_MODE, + GSK_VULKAN_PIPELINE_BLEND_MODE_CLIP, + GSK_VULKAN_PIPELINE_BLEND_MODE_CLIP_ROUNDED, /* add more */ GSK_VULKAN_N_PIPELINES } GskVulkanPipelineType; diff --git a/gsk/meson.build b/gsk/meson.build index 072c140da3..e91985af8e 100644 --- a/gsk/meson.build +++ b/gsk/meson.build @@ -54,6 +54,7 @@ gsk_private_vulkan_compiled_shaders_deps = [] if have_vulkan gsk_private_sources += files([ + 'gskvulkanblendmodepipeline.c', 'gskvulkanblendpipeline.c', 'gskvulkanblurpipeline.c', 'gskvulkanborderpipeline.c', diff --git a/gsk/resources/vulkan/blendmode-clip-rounded.frag.spv b/gsk/resources/vulkan/blendmode-clip-rounded.frag.spv Binary files differnew file mode 100644 index 0000000000..4ede20eb07 --- /dev/null +++ b/gsk/resources/vulkan/blendmode-clip-rounded.frag.spv diff --git a/gsk/resources/vulkan/blendmode-clip-rounded.vert.spv b/gsk/resources/vulkan/blendmode-clip-rounded.vert.spv Binary files differnew file mode 100644 index 0000000000..499a74abe1 --- /dev/null +++ b/gsk/resources/vulkan/blendmode-clip-rounded.vert.spv diff --git a/gsk/resources/vulkan/blendmode-clip.frag.spv b/gsk/resources/vulkan/blendmode-clip.frag.spv Binary files differnew file mode 100644 index 0000000000..21484c8e18 --- /dev/null +++ b/gsk/resources/vulkan/blendmode-clip.frag.spv diff --git a/gsk/resources/vulkan/blendmode-clip.vert.spv b/gsk/resources/vulkan/blendmode-clip.vert.spv Binary files differnew file mode 100644 index 0000000000..499a74abe1 --- /dev/null +++ b/gsk/resources/vulkan/blendmode-clip.vert.spv diff --git a/gsk/resources/vulkan/blendmode-rect-rounded.frag.spv b/gsk/resources/vulkan/blendmode-rect-rounded.frag.spv Binary files differnew file mode 100644 index 0000000000..55ce955e83 --- /dev/null +++ b/gsk/resources/vulkan/blendmode-rect-rounded.frag.spv diff --git a/gsk/resources/vulkan/blendmode-rect.frag.spv b/gsk/resources/vulkan/blendmode-rect.frag.spv Binary files differnew file mode 100644 index 0000000000..7d0fdd1fa7 --- /dev/null +++ b/gsk/resources/vulkan/blendmode-rect.frag.spv diff --git a/gsk/resources/vulkan/blendmode.frag b/gsk/resources/vulkan/blendmode.frag new file mode 100644 index 0000000000..803a538d1d --- /dev/null +++ b/gsk/resources/vulkan/blendmode.frag @@ -0,0 +1,183 @@ +#version 420 core + +#include "clip.frag.glsl" + +layout(location = 0) in vec2 inPos; +layout(location = 1) in vec2 inStartTexCoord; +layout(location = 2) in vec2 inEndTexCoord; +layout(location = 3) flat in uint inBlendMode; + +layout(set = 0, binding = 0) uniform sampler2D startTexture; +layout(set = 1, binding = 0) uniform sampler2D endTexture; + +layout(location = 0) out vec4 color; + +vec3 +multiply (vec3 source, vec3 backdrop, float opacity) +{ + vec3 result = source * backdrop; + return mix (source, result, opacity); +} + +vec3 +difference (vec3 source, vec3 backdrop, float opacity) +{ + vec3 result = abs (source - backdrop); + return mix (source, result, opacity); +} + +vec3 +screen (vec3 source, vec3 backdrop, float opacity) +{ + vec3 result = source + backdrop - source * backdrop; + return mix (source, result, opacity); +} + +float +hard_light (float source, float backdrop) +{ + if (source <= 0.5) + return 2 * backdrop * source; + else + return 2 * (backdrop + source - backdrop * source) - 1; +} + +vec3 +hard_light (vec3 source, vec3 backdrop, float opacity) +{ + vec3 result = vec3 (hard_light (source.r, backdrop.r), + hard_light (source.g, backdrop.g), + hard_light (source.b, backdrop.b)); + return mix (source, result, opacity); +} + +float +soft_light (float source, float backdrop) +{ + float db; + + if (backdrop <= 0.25) + db = ((16 * backdrop - 12) * backdrop + 4) * backdrop; + else + db = sqrt (backdrop); + + if (source <= 0.5) + return backdrop - (1 - 2 * source) * backdrop * (1 - backdrop); + else + return backdrop + (2 * source - 1) * (db - backdrop); +} + +vec3 +soft_light (vec3 source, vec3 backdrop, float opacity) +{ + vec3 result = vec3 (soft_light (source.r, backdrop.r), + soft_light (source.g, backdrop.g), + soft_light (source.b, backdrop.b)); + return mix (source, result, opacity); +} + +vec3 +overlay (vec3 source, vec3 backdrop, float opacity) +{ + return hard_light (backdrop, source, opacity); +} + +vec3 +darken (vec3 source, vec3 backdrop, float opacity) +{ + vec3 result = min (source, backdrop); + return mix (source, result, opacity); +} + +vec3 +lighten (vec3 source, vec3 backdrop, float opacity) +{ + vec3 result = max (source, backdrop); + return mix (source, result, opacity); +} + +float +color_dodge (float source, float backdrop) +{ + return (source == 1.0) ? source : min (backdrop / (1.0 - source), 1.0); +} + +vec3 +color_dodge (vec3 source, vec3 backdrop, float opacity) +{ + vec3 result = vec3 (color_dodge (source.r, backdrop.r), + color_dodge (source.g, backdrop.g), + color_dodge (source.b, backdrop.b)); + return mix (source, result, opacity); +} + + +float +color_burn (float source, float backdrop) +{ + return (source == 0.0) ? source : max ((1.0 - ((1.0 - backdrop) / source)), 0.0); +} + +vec3 +color_burn (vec3 source, vec3 backdrop, float opacity) +{ + vec3 result = vec3 (color_burn (source.r, backdrop.r), + color_burn (source.g, backdrop.g), + color_burn (source.b, backdrop.b)); + return mix (source, result, opacity); +} + +vec3 +exclusion (vec3 source, vec3 backdrop, float opacity) +{ + vec3 result = backdrop + source - 2.0 * backdrop * source; + return mix (source, result, opacity); +} + +float +combine (float source, float backdrop) +{ + return source + backdrop * (1 - source); +} + +void main() +{ + vec4 source = texture (startTexture, inStartTexCoord); + vec4 backdrop = texture (endTexture, inEndTexCoord); + vec3 rgb = vec3(1,0,0); + float a = 1; + + a = combine (source.a, backdrop.a); + + if (inBlendMode == 0) // default + { + rgb = source.rgb; + a = source.a; + } + else if (inBlendMode == 1) + rgb = multiply (source.rgb, backdrop.rgb, backdrop.a); + else if (inBlendMode == 2) + rgb = screen (source.rgb, backdrop.rgb, backdrop.a); + else if (inBlendMode == 3) + rgb = overlay (source.rgb, backdrop.rgb, backdrop.a); + else if (inBlendMode == 4) + rgb = darken (source.rgb, backdrop.rgb, backdrop.a); + else if (inBlendMode == 5) + rgb = lighten (source.rgb, backdrop.rgb, backdrop.a); + else if (inBlendMode == 6) + rgb = color_dodge (source.rgb, backdrop.rgb, backdrop.a); + else if (inBlendMode == 7) + rgb = color_burn (source.rgb, backdrop.rgb, backdrop.a); + else if (inBlendMode == 8) + rgb = hard_light (source.rgb, backdrop.rgb, backdrop.a); + else if (inBlendMode == 9) + rgb = soft_light (source.rgb, backdrop.rgb, backdrop.a); + else if (inBlendMode == 10) + rgb = difference (source.rgb, backdrop.rgb, backdrop.a); + else if (inBlendMode == 11) + rgb = exclusion (source.rgb, backdrop.rgb, backdrop.a); + else + discard; + + color = clip (inPos, vec4 (rgb, a)); +} diff --git a/gsk/resources/vulkan/blendmode.frag.spv b/gsk/resources/vulkan/blendmode.frag.spv Binary files differnew file mode 100644 index 0000000000..21484c8e18 --- /dev/null +++ b/gsk/resources/vulkan/blendmode.frag.spv diff --git a/gsk/resources/vulkan/blendmode.vert b/gsk/resources/vulkan/blendmode.vert new file mode 100644 index 0000000000..35b8356c90 --- /dev/null +++ b/gsk/resources/vulkan/blendmode.vert @@ -0,0 +1,44 @@ +#version 420 core + +#include "clip.vert.glsl" + +layout(location = 0) in vec4 inRect; +layout(location = 1) in vec4 inStartTexRect; +layout(location = 2) in vec4 inEndTexRect; +layout(location = 3) in uint inBlendMode; + +layout(location = 0) out vec2 outPos; +layout(location = 1) out vec2 outStartTexCoord; +layout(location = 2) out vec2 outEndTexCoord; +layout(location = 3) flat out uint outBlendMode; + +out gl_PerVertex { + vec4 gl_Position; +}; + +vec2 offsets[6] = { vec2(0.0, 0.0), + vec2(1.0, 0.0), + vec2(0.0, 1.0), + vec2(0.0, 1.0), + vec2(1.0, 0.0), + vec2(1.0, 1.0) }; + +void main() { + vec4 rect = clip (inRect); + vec2 pos = rect.xy + rect.zw * offsets[gl_VertexIndex]; + gl_Position = push.mvp * vec4 (pos, 0.0, 1.0); + + outPos = pos; + + vec4 texrect = vec4((rect.xy - inRect.xy) / inRect.zw, + rect.zw / inRect.zw); + vec4 starttexrect = vec4(inStartTexRect.xy + inStartTexRect.zw * texrect.xy, + inStartTexRect.zw * texrect.zw); + vec4 endtexrect = vec4(inEndTexRect.xy + inEndTexRect.zw * texrect.xy, + inEndTexRect.zw * texrect.zw); + + outStartTexCoord = starttexrect.xy + starttexrect.zw * offsets[gl_VertexIndex]; + outEndTexCoord = endtexrect.xy + endtexrect.zw * offsets[gl_VertexIndex]; + + outBlendMode = inBlendMode; +} diff --git a/gsk/resources/vulkan/blendmode.vert.spv b/gsk/resources/vulkan/blendmode.vert.spv Binary files differnew file mode 100644 index 0000000000..614f7cfc50 --- /dev/null +++ b/gsk/resources/vulkan/blendmode.vert.spv diff --git a/gsk/resources/vulkan/meson.build b/gsk/resources/vulkan/meson.build index 6e61885e65..160bc0d6e4 100644 --- a/gsk/resources/vulkan/meson.build +++ b/gsk/resources/vulkan/meson.build @@ -8,6 +8,7 @@ gsk_private_vulkan_fragment_shaders = [ 'blend.frag', + 'blendmode.frag', 'blur.frag', 'border.frag', 'color.frag', @@ -21,6 +22,7 @@ gsk_private_vulkan_fragment_shaders = [ gsk_private_vulkan_vertex_shaders = [ 'blend.vert', + 'blendmode.vert', 'blur.vert', 'border.vert', 'color.vert', |