Imported Upstream version 6.1
[platform/upstream/ffmpeg.git] / libavfilter / vsrc_testsrc_vulkan.c
1 /*
2  * Copyright (c) Lynne
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * FFmpeg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FFmpeg; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20
21 #include "libavutil/random_seed.h"
22 #include "libavutil/csp.h"
23 #include "libavutil/opt.h"
24 #include "vulkan_filter.h"
25 #include "vulkan_spirv.h"
26 #include "internal.h"
27 #include "filters.h"
28 #include "colorspace.h"
29 #include "video.h"
30
31 enum TestSrcVulkanMode {
32     TESTSRC_COLOR,
33 };
34
35 typedef struct TestSrcVulkanPushData {
36     float color_comp[4];
37 } TestSrcVulkanPushData;
38
39 typedef struct TestSrcVulkanContext {
40     FFVulkanContext vkctx;
41
42     int initialized;
43     FFVulkanPipeline pl;
44     FFVkExecPool e;
45     FFVkQueueFamilyCtx qf;
46     FFVkSPIRVShader shd;
47
48     /* Only used by color_vulkan */
49     uint8_t color_rgba[4];
50
51     TestSrcVulkanPushData opts;
52
53     int w, h;
54     int pw, ph;
55     char *out_format_string;
56     enum AVColorRange out_range;
57     unsigned int nb_frame;
58     AVRational time_base, frame_rate;
59     int64_t pts;
60     int64_t duration;           ///< duration expressed in microseconds
61     AVRational sar;             ///< sample aspect ratio
62     int draw_once;              ///< draw only the first frame, always put out the same picture
63     int draw_once_reset;        ///< draw only the first frame or in case of reset
64     AVFrame *picref;            ///< cached reference containing the painted picture
65 } TestSrcVulkanContext;
66
67 static av_cold int init_filter(AVFilterContext *ctx, enum TestSrcVulkanMode mode)
68 {
69     int err;
70     uint8_t *spv_data;
71     size_t spv_len;
72     void *spv_opaque = NULL;
73     TestSrcVulkanContext *s = ctx->priv;
74     FFVulkanContext *vkctx = &s->vkctx;
75     const int planes = av_pix_fmt_count_planes(s->vkctx.output_format);
76     FFVkSPIRVShader *shd = &s->shd;
77     FFVkSPIRVCompiler *spv;
78     FFVulkanDescriptorSetBinding *desc_set;
79     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(s->vkctx.output_format);
80
81     spv = ff_vk_spirv_init();
82     if (!spv) {
83         av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n");
84         return AVERROR_EXTERNAL;
85     }
86
87     ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT);
88     RET(ff_vk_exec_pool_init(vkctx, &s->qf, &s->e, s->qf.nb_queues*4, 0, 0, 0, NULL));
89     RET(ff_vk_shader_init(&s->pl, &s->shd, "testsrc_compute",
90                           VK_SHADER_STAGE_COMPUTE_BIT, 0));
91
92     ff_vk_shader_set_compute_sizes(&s->shd, 32, 32, 1);
93
94     GLSLC(0, layout(push_constant, std430) uniform pushConstants {        );
95     GLSLC(1,    vec4 color_comp;                                          );
96     GLSLC(0, };                                                           );
97     GLSLC(0,                                                              );
98
99     ff_vk_add_push_constant(&s->pl, 0, sizeof(s->opts),
100                             VK_SHADER_STAGE_COMPUTE_BIT);
101
102     desc_set = (FFVulkanDescriptorSetBinding []) {
103         {
104             .name       = "output_img",
105             .type       = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
106             .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format),
107             .mem_quali  = "writeonly",
108             .dimensions = 2,
109             .elems      = planes,
110             .stages     = VK_SHADER_STAGE_COMPUTE_BIT,
111         },
112     };
113
114     RET(ff_vk_pipeline_descriptor_set_add(vkctx, &s->pl, shd, desc_set, 1, 0, 0));
115
116     GLSLC(0, void main()                                                  );
117     GLSLC(0, {                                                            );
118     GLSLC(1,     ivec2 pos = ivec2(gl_GlobalInvocationID.xy);             );
119     if (mode == TESTSRC_COLOR) {
120         double rgb2yuv[3][3];
121         double rgbad[4];
122         double yuvad[4];
123
124         enum AVColorSpace csp;
125         const AVLumaCoefficients *luma = NULL;
126
127         s->draw_once = 1;
128
129         if (desc->flags & AV_PIX_FMT_FLAG_RGB)
130             csp = AVCOL_SPC_RGB;
131         else
132             csp = AVCOL_SPC_SMPTE170M;
133
134         if (!(desc->flags & AV_PIX_FMT_FLAG_RGB) && !(luma = av_csp_luma_coeffs_from_avcsp(csp)))
135             return AVERROR(EINVAL);
136         else if (!(desc->flags & AV_PIX_FMT_FLAG_RGB))
137             ff_fill_rgb2yuv_table(luma, rgb2yuv);
138
139         for (int i = 0; i < 4; i++)
140             rgbad[i] = s->color_rgba[i] / 255.0;
141
142         if (!(desc->flags & AV_PIX_FMT_FLAG_RGB))
143             ff_matrix_mul_3x3_vec(yuvad, rgbad, rgb2yuv);
144         else
145             memcpy(yuvad, rgbad, sizeof(rgbad));
146
147         yuvad[3] = rgbad[3];
148
149         if (!(desc->flags & AV_PIX_FMT_FLAG_RGB)) {
150             for (int i = 0; i < 3; i++) {
151                 int chroma = (!(desc->flags & AV_PIX_FMT_FLAG_RGB) && i > 0);
152                 if (s->out_range == AVCOL_RANGE_MPEG) {
153                     yuvad[i] *= (chroma ? 224.0 : 219.0) / 255.0;
154                     yuvad[i] += (chroma ? 128.0 :  16.0) / 255.0;
155                 } else if (chroma) {
156                     yuvad[i] += 0.5;
157                 }
158             }
159         }
160
161         /* Ensure we place the alpha appropriately for gray formats */
162         if (desc->nb_components <= 2)
163             yuvad[1] = yuvad[3];
164
165         for (int i = 0; i < 4; i++)
166             s->opts.color_comp[i] = yuvad[i];
167
168         GLSLC(1,     vec4 r;                                                  );
169         GLSLC(0,                                                              );
170         for (int i = 0, c_off = 0; i < planes; i++) {
171             for (int c = 0; c < desc->nb_components; c++) {
172                 if (desc->comp[c].plane == i) {
173                     int off = desc->comp[c].offset / (FFALIGN(desc->comp[c].depth, 8)/8);
174                     GLSLF(1, r[%i] = color_comp[%i];             ,off, c_off++);
175                 }
176             }
177             GLSLF(1, imageStore(output_img[%i], pos, r);                    ,i);
178             GLSLC(0,                                                          );
179         }
180     }
181     GLSLC(0, }                                                            );
182
183     RET(spv->compile_shader(spv, ctx, shd, &spv_data, &spv_len, "main",
184                             &spv_opaque));
185     RET(ff_vk_shader_create(vkctx, shd, spv_data, spv_len, "main"));
186
187     RET(ff_vk_init_compute_pipeline(vkctx, &s->pl, shd));
188     RET(ff_vk_exec_pipeline_register(vkctx, &s->e, &s->pl));
189
190     s->initialized = 1;
191
192 fail:
193     if (spv_opaque)
194         spv->free_shader(spv, &spv_opaque);
195     if (spv)
196         spv->uninit(&spv);
197
198     return err;
199 }
200
201 static int testsrc_vulkan_activate(AVFilterContext *ctx)
202 {
203     int err;
204     AVFilterLink *outlink = ctx->outputs[0];
205     TestSrcVulkanContext *s = ctx->priv;
206     AVFrame *frame;
207
208     if (!s->initialized) {
209         enum TestSrcVulkanMode mode = TESTSRC_COLOR;
210         err = init_filter(ctx, mode);
211         if (err < 0)
212             return err;
213     }
214
215     if (!ff_outlink_frame_wanted(outlink))
216         return FFERROR_NOT_READY;
217     if (s->duration >= 0 &&
218         av_rescale_q(s->pts, s->time_base, AV_TIME_BASE_Q) >= s->duration) {
219         ff_outlink_set_status(outlink, AVERROR_EOF, s->pts);
220         return 0;
221     }
222
223     if (s->draw_once) {
224         if (s->draw_once_reset) {
225             av_frame_free(&s->picref);
226             s->draw_once_reset = 0;
227         }
228         if (!s->picref) {
229             s->picref = ff_get_video_buffer(outlink, s->w, s->h);
230             if (!s->picref)
231                 return AVERROR(ENOMEM);
232
233             err = ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->pl, s->picref, NULL,
234                                               NULL, &s->opts, sizeof(s->opts));
235             if (err < 0)
236                 return err;
237         }
238         frame = av_frame_clone(s->picref);
239     } else {
240         frame = ff_get_video_buffer(outlink, s->w, s->h);
241     }
242
243     if (!frame)
244         return AVERROR(ENOMEM);
245
246     frame->pts                 = s->pts;
247     frame->duration            = 1;
248     frame->flags               = AV_FRAME_FLAG_KEY;
249     frame->pict_type           = AV_PICTURE_TYPE_I;
250     frame->sample_aspect_ratio = s->sar;
251     if (!s->draw_once) {
252         err = ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->pl, frame, NULL,
253                                           NULL, &s->opts, sizeof(s->opts));
254         if (err < 0) {
255             av_frame_free(&frame);
256             return err;
257         }
258     }
259
260     s->pts++;
261     s->nb_frame++;
262
263     return ff_filter_frame(outlink, frame);
264 }
265
266 static int testsrc_vulkan_config_props(AVFilterLink *outlink)
267 {
268     int err;
269     TestSrcVulkanContext *s = outlink->src->priv;
270     FFVulkanContext *vkctx = &s->vkctx;
271
272     if (!s->out_format_string) {
273         vkctx->output_format = AV_PIX_FMT_YUV444P;
274     } else {
275         vkctx->output_format = av_get_pix_fmt(s->out_format_string);
276         if (vkctx->output_format == AV_PIX_FMT_NONE) {
277             av_log(vkctx, AV_LOG_ERROR, "Invalid output format.\n");
278             return AVERROR(EINVAL);
279         }
280     }
281
282     err = ff_vk_filter_init_context(outlink->src, vkctx, NULL,
283                                     s->w, s->h, vkctx->output_format);
284     if (err < 0)
285         return err;
286
287     outlink->hw_frames_ctx = av_buffer_ref(vkctx->frames_ref);
288     if (!outlink->hw_frames_ctx)
289         return AVERROR(ENOMEM);
290
291     s->time_base = av_inv_q(s->frame_rate);
292     s->nb_frame = 0;
293     s->pts = 0;
294
295     s->vkctx.output_width = s->w;
296     s->vkctx.output_height = s->h;
297     outlink->w = s->w;
298     outlink->h = s->h;
299     outlink->sample_aspect_ratio = s->sar;
300     outlink->frame_rate = s->frame_rate;
301     outlink->time_base  = s->time_base;
302
303     return 0;
304 }
305
306 static void testsrc_vulkan_uninit(AVFilterContext *avctx)
307 {
308     TestSrcVulkanContext *s = avctx->priv;
309     FFVulkanContext *vkctx = &s->vkctx;
310
311     av_frame_free(&s->picref);
312
313     ff_vk_exec_pool_free(vkctx, &s->e);
314     ff_vk_pipeline_free(vkctx, &s->pl);
315     ff_vk_shader_free(vkctx, &s->shd);
316
317     ff_vk_uninit(&s->vkctx);
318
319     s->initialized = 0;
320 }
321
322 #define OFFSET(x) offsetof(TestSrcVulkanContext, x)
323 #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
324
325 #define COMMON_OPTS                                                                                                                           \
326     { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, { .str = "1920x1080" }, 0, 0, FLAGS },                                     \
327     { "s",    "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, { .str = "1920x1080" }, 0, 0, FLAGS },                                     \
328                                                                                                                                               \
329     { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, { .str = "60" }, 0, INT_MAX, FLAGS },                             \
330     { "r",    "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, { .str = "60" }, 0, INT_MAX, FLAGS },                             \
331                                                                                                                                               \
332     { "duration", "set video duration", OFFSET(duration), AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT64_MAX, FLAGS },                        \
333     { "d",        "set video duration", OFFSET(duration), AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT64_MAX, FLAGS },                        \
334                                                                                                                                               \
335     { "sar", "set video sample aspect ratio", OFFSET(sar), AV_OPT_TYPE_RATIONAL, { .dbl = 1 },  0, INT_MAX, FLAGS },                          \
336                                                                                                                                               \
337     { "format", "Output video format (software format of hardware frames)", OFFSET(out_format_string), AV_OPT_TYPE_STRING, .flags = FLAGS },
338
339 static const AVOption color_vulkan_options[] = {
340     { "color", "set color", OFFSET(color_rgba), AV_OPT_TYPE_COLOR, {.str = "black"}, 0, 0, FLAGS },
341     { "c",     "set color", OFFSET(color_rgba), AV_OPT_TYPE_COLOR, {.str = "black"}, 0, 0, FLAGS },
342     COMMON_OPTS
343     { "out_range", "Output colour range (from 0 to 2) (default 0)", OFFSET(out_range), AV_OPT_TYPE_INT, {.i64 = AVCOL_RANGE_UNSPECIFIED}, AVCOL_RANGE_UNSPECIFIED, AVCOL_RANGE_JPEG, .flags = FLAGS, "range" },
344         { "full", "Full range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, "range" },
345         { "limited", "Limited range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, "range" },
346         { "jpeg", "Full range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, "range" },
347         { "mpeg", "Limited range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, "range" },
348         { "tv", "Limited range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, "range" },
349         { "pc", "Full range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, "range" },
350     { NULL },
351 };
352
353 AVFILTER_DEFINE_CLASS(color_vulkan);
354
355 static const AVFilterPad testsrc_vulkan_outputs[] = {
356     {
357         .name = "default",
358         .type = AVMEDIA_TYPE_VIDEO,
359         .config_props = testsrc_vulkan_config_props,
360     },
361 };
362
363 const AVFilter ff_vsrc_color_vulkan = {
364     .name           = "color_vulkan",
365     .description    = NULL_IF_CONFIG_SMALL("Generate a constant color (Vulkan)"),
366     .priv_size      = sizeof(TestSrcVulkanContext),
367     .init           = &ff_vk_filter_init,
368     .uninit         = &testsrc_vulkan_uninit,
369     .inputs         = NULL,
370     .flags          = AVFILTER_FLAG_HWDEVICE,
371     .activate       = testsrc_vulkan_activate,
372     FILTER_OUTPUTS(testsrc_vulkan_outputs),
373     FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN),
374     .priv_class     = &color_vulkan_class,
375     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
376 };