3 * Copyright (C) 2018 Philip Langdale <philipl@overt.org>
4 * Copyright (C) 2016 Thomas Mundt <loudmax@yahoo.de>
6 * This file is part of FFmpeg.
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 #include "libavutil/random_seed.h"
24 #include "libavutil/opt.h"
25 #include "libavutil/vulkan_spirv.h"
26 #include "vulkan_filter.h"
30 typedef struct BWDIFVulkanContext
{
32 FFVulkanContext vkctx
;
36 AVVulkanDeviceQueueFamily
*qf
;
41 typedef struct BWDIFParameters
{
47 extern const char *ff_source_bwdif_comp
;
49 static av_cold
int init_filter(AVFilterContext
*ctx
)
54 void *spv_opaque
= NULL
;
55 BWDIFVulkanContext
*s
= ctx
->priv
;
56 FFVulkanContext
*vkctx
= &s
->vkctx
;
57 const int planes
= av_pix_fmt_count_planes(s
->vkctx
.output_format
);
59 FFVkSPIRVCompiler
*spv
;
60 FFVulkanDescriptorSetBinding
*desc
;
62 spv
= ff_vk_spirv_init();
64 av_log(ctx
, AV_LOG_ERROR
, "Unable to initialize SPIR-V compiler!\n");
65 return AVERROR_EXTERNAL
;
68 s
->qf
= ff_vk_qf_find(vkctx
, VK_QUEUE_COMPUTE_BIT
, 0);
70 av_log(ctx
, AV_LOG_ERROR
, "Device has no compute queues\n");
71 err
= AVERROR(ENOTSUP
);
75 RET(ff_vk_exec_pool_init(vkctx
, s
->qf
, &s
->e
, s
->qf
->num
*4, 0, 0, 0, NULL
));
76 RET(ff_vk_init_sampler(vkctx
, &s
->sampler
, 1, VK_FILTER_NEAREST
));
78 RET(ff_vk_shader_init(vkctx
, &s
->shd
, "bwdif",
79 VK_SHADER_STAGE_COMPUTE_BIT
,
85 desc
= (FFVulkanDescriptorSetBinding
[]) {
88 .type
= VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
,
91 .stages
= VK_SHADER_STAGE_COMPUTE_BIT
,
92 .samplers
= DUP_SAMPLER(s
->sampler
),
96 .type
= VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
,
99 .stages
= VK_SHADER_STAGE_COMPUTE_BIT
,
100 .samplers
= DUP_SAMPLER(s
->sampler
),
104 .type
= VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
,
107 .stages
= VK_SHADER_STAGE_COMPUTE_BIT
,
108 .samplers
= DUP_SAMPLER(s
->sampler
),
112 .type
= VK_DESCRIPTOR_TYPE_STORAGE_IMAGE
,
113 .mem_layout
= ff_vk_shader_rep_fmt(s
->vkctx
.output_format
, FF_VK_REP_FLOAT
),
114 .mem_quali
= "writeonly",
117 .stages
= VK_SHADER_STAGE_COMPUTE_BIT
,
121 RET(ff_vk_shader_add_descriptor_set(vkctx
, &s
->shd
, desc
, 4, 0, 0));
123 GLSLC(0, layout(push_constant
, std430
) uniform pushConstants
{ );
124 GLSLC(1, int parity
; );
126 GLSLC(1, int current_field
; );
129 ff_vk_shader_add_push_const(&s
->shd
, 0, sizeof(BWDIFParameters
),
130 VK_SHADER_STAGE_COMPUTE_BIT
);
132 GLSLD(ff_source_bwdif_comp
);
133 GLSLC(0, void main() );
135 GLSLC(1, ivec2 size
; );
136 GLSLC(1, const ivec2 pos
= ivec2(gl_GlobalInvocationID
.xy
); );
137 GLSLC(1, bool filter_field
= ((pos
.y
^ parity
) & 1) == 1; );
138 GLSLF(1, bool is_intra
= filter_field
&& (current_field
== %i
); ,YADIF_FIELD_END
);
139 GLSLC(1, bool field_parity
= (parity
^ tff
) != 0; );
141 GLSLC(1, size
= imageSize(dst
[0]); );
142 GLSLC(1, if (!IS_WITHIN(pos
, size
)) { );
144 GLSLC(1, } else if (is_intra
) { );
145 for (int i
= 0; i
< planes
; i
++) {
147 GLSLF(2, size
= imageSize(dst
[%i
]); ,i
);
148 GLSLC(2, if (!IS_WITHIN(pos
, size
)) );
151 GLSLF(2, process_plane_intra(%i
, pos
); ,i
);
153 GLSLC(1, } else if (filter_field
) { );
154 for (int i
= 0; i
< planes
; i
++) {
156 GLSLF(2, size
= imageSize(dst
[%i
]); ,i
);
157 GLSLC(2, if (!IS_WITHIN(pos
, size
)) );
160 GLSLF(2, process_plane(%i
, pos
, filter_field
, is_intra
, field_parity
); ,i
);
163 for (int i
= 0; i
< planes
; i
++) {
165 GLSLF(2, size
= imageSize(dst
[%i
]); ,i
);
166 GLSLC(2, if (!IS_WITHIN(pos
, size
)) );
169 GLSLF(2, imageStore(dst
[%i
], pos
, texture(cur
[%i
], pos
)); ,i
, i
);
174 RET(spv
->compile_shader(vkctx
, spv
, &s
->shd
, &spv_data
, &spv_len
, "main",
176 RET(ff_vk_shader_link(vkctx
, &s
->shd
, spv_data
, spv_len
, "main"));
178 RET(ff_vk_shader_register_exec(vkctx
, &s
->e
, &s
->shd
));
184 spv
->free_shader(spv
, &spv_opaque
);
191 static void bwdif_vulkan_filter_frame(AVFilterContext
*ctx
, AVFrame
*dst
,
194 BWDIFVulkanContext
*s
= ctx
->priv
;
195 YADIFContext
*y
= &s
->yadif
;
196 BWDIFParameters params
= {
199 .current_field
= y
->current_field
,
202 ff_vk_filter_process_Nin(&s
->vkctx
, &s
->e
, &s
->shd
, dst
,
203 (AVFrame
*[]){ y
->prev
, y
->cur
, y
->next
}, 3,
204 s
->sampler
, ¶ms
, sizeof(params
));
206 if (y
->current_field
== YADIF_FIELD_END
)
207 y
->current_field
= YADIF_FIELD_NORMAL
;
210 static void bwdif_vulkan_uninit(AVFilterContext
*avctx
)
212 BWDIFVulkanContext
*s
= avctx
->priv
;
213 FFVulkanContext
*vkctx
= &s
->vkctx
;
214 FFVulkanFunctions
*vk
= &vkctx
->vkfn
;
216 ff_vk_exec_pool_free(vkctx
, &s
->e
);
217 ff_vk_shader_free(vkctx
, &s
->shd
);
220 vk
->DestroySampler(vkctx
->hwctx
->act_dev
, s
->sampler
,
221 vkctx
->hwctx
->alloc
);
223 ff_vk_uninit(&s
->vkctx
);
225 ff_yadif_uninit(avctx
);
230 static int bwdif_vulkan_config_input(AVFilterLink
*inlink
)
232 FilterLink
*l
= ff_filter_link(inlink
);
233 AVHWFramesContext
*input_frames
;
234 AVFilterContext
*avctx
= inlink
->dst
;
235 BWDIFVulkanContext
*s
= avctx
->priv
;
236 FFVulkanContext
*vkctx
= &s
->vkctx
;
238 if (!l
->hw_frames_ctx
) {
239 av_log(inlink
->dst
, AV_LOG_ERROR
, "Vulkan filtering requires a "
240 "hardware frames context on the input.\n");
241 return AVERROR(EINVAL
);
244 input_frames
= (AVHWFramesContext
*)l
->hw_frames_ctx
->data
;
245 if (input_frames
->format
!= AV_PIX_FMT_VULKAN
)
246 return AVERROR(EINVAL
);
248 /* Extract the device and default output format from the first input. */
249 if (avctx
->inputs
[0] != inlink
)
252 /* Save the ref, without reffing it */
253 vkctx
->input_frames_ref
= l
->hw_frames_ctx
;
256 vkctx
->output_format
= input_frames
->sw_format
;
257 vkctx
->output_width
= inlink
->w
;
258 vkctx
->output_height
= inlink
->h
;
263 static int bwdif_vulkan_config_output(AVFilterLink
*outlink
)
265 FilterLink
*l
= ff_filter_link(outlink
);
267 AVFilterContext
*avctx
= outlink
->src
;
268 BWDIFVulkanContext
*s
= avctx
->priv
;
269 YADIFContext
*y
= &s
->yadif
;
270 FFVulkanContext
*vkctx
= &s
->vkctx
;
272 av_buffer_unref(&l
->hw_frames_ctx
);
274 err
= ff_vk_filter_init_context(avctx
, vkctx
, vkctx
->input_frames_ref
,
275 vkctx
->output_width
, vkctx
->output_height
,
276 vkctx
->output_format
);
281 vkctx
->class = y
->class;
283 l
->hw_frames_ctx
= av_buffer_ref(vkctx
->frames_ref
);
284 if (!l
->hw_frames_ctx
)
285 return AVERROR(ENOMEM
);
287 err
= ff_yadif_config_output_common(outlink
);
291 y
->csp
= av_pix_fmt_desc_get(vkctx
->frames
->sw_format
);
292 y
->filter
= bwdif_vulkan_filter_frame
;
294 if (AV_CEIL_RSHIFT(outlink
->w
, y
->csp
->log2_chroma_w
) < 4 || AV_CEIL_RSHIFT(outlink
->h
, y
->csp
->log2_chroma_h
) < 4) {
295 av_log(avctx
, AV_LOG_ERROR
, "Video with planes less than 4 columns or lines is not supported\n");
296 return AVERROR(EINVAL
);
299 return init_filter(avctx
);
302 static const AVClass bwdif_vulkan_class
= {
303 .class_name
= "bwdif_vulkan",
304 .item_name
= av_default_item_name
,
305 .option
= ff_yadif_options
,
306 .version
= LIBAVUTIL_VERSION_INT
,
307 .category
= AV_CLASS_CATEGORY_FILTER
,
310 static const AVFilterPad bwdif_vulkan_inputs
[] = {
313 .type
= AVMEDIA_TYPE_VIDEO
,
314 .filter_frame
= ff_yadif_filter_frame
,
315 .config_props
= &bwdif_vulkan_config_input
,
319 static const AVFilterPad bwdif_vulkan_outputs
[] = {
322 .type
= AVMEDIA_TYPE_VIDEO
,
323 .request_frame
= ff_yadif_request_frame
,
324 .config_props
= &bwdif_vulkan_config_output
,
328 const FFFilter ff_vf_bwdif_vulkan
= {
329 .p
.name
= "bwdif_vulkan",
330 .p
.description
= NULL_IF_CONFIG_SMALL("Deinterlace Vulkan frames via bwdif"),
331 .p
.priv_class
= &bwdif_vulkan_class
,
332 .p
.flags
= AVFILTER_FLAG_HWDEVICE
|
333 AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL
,
334 .priv_size
= sizeof(BWDIFVulkanContext
),
335 .init
= &ff_vk_filter_init
,
336 .uninit
= &bwdif_vulkan_uninit
,
337 FILTER_INPUTS(bwdif_vulkan_inputs
),
338 FILTER_OUTPUTS(bwdif_vulkan_outputs
),
339 FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN
),
340 .flags_internal
= FF_FILTER_FLAG_HWFRAME_AWARE
,