[FFmpeg-devel] [PATCH 1/2] libavutil/libavfilter: opencl wrapper based on comments on 20130326

Wei Gao highgod0401 at gmail.com
Thu Mar 28 03:08:14 CET 2013


Hi,

Stefano Sabatini, thanks for your reviewing, questions and explanations as
follows, thianks.

Best regards

2013/3/28 Stefano Sabatini <stefasab at gmail.com>

> On date Wednesday 2013-03-27 15:11:46 +0800, Wei Gao encoded:
> > Hi,
> >
> > Stefano Sabatini, thanks for your reply. some questions and explanations
> as
> > follows
> >
> > Thanks
> > Best regards
> >
> > 2013/3/27 Stefano Sabatini <stefasab at gmail.com>
> >
> > > On date Tuesday 2013-03-26 18:55:05 +0800, Wei Gao encoded:
> > > >
> > >
> > > > From f91df6a8315a1b7bdc7b69517831fc745fcbd4fd Mon Sep 17 00:00:00
> 2001
> > > > From: highgod0401 <highgod0401 at gmail.com>
> > > > Date: Tue, 26 Mar 2013 18:43:00 +0800
> > > > Subject: [PATCH 1/2] opencl wrapper based on comments on 20130326
> [...]
> > > This is my understanding of how this OpenCL API works.
> > >
> > > You register some code with a name (which are currently stored
> > > in the global environment), with av_opencl_register_kernel().
> > >
> > > Then the previously registered framments of code are compiled by
> > > OpenCL, when doing: av_opencl_init() -> compile_kernel_file()
> > >
> > > With av_opencl_init() you also specify some parameters (build_options)
> > > which are used when compiling the code of the specified functions.
> > >
> > > av_opencl_init() also creates the OpenCL program, which is stored in
> > > the global environment. The program is unique for all the kernels
> > > registered so far.
> > >
> > > At this point you need to create an entry point for each kernel, to
> > > run a specific *function* defined within it. This is done by creating
> > > a kernel, with av_opencl_create_kernel()
> > >
> > > av_opencl_create_kernel() is used to create a kernel (a sort of
> > > handler to communicate with the *compiled* kernel). The kernel is
> > > created specifying the name of the function to run *in the kernel
> > > code*. The kernel is set in the passed AVOpenCLEnv environment.
> > >
> > > In order to run a function specified in a kernel, you also need to
> > > provide some parameters/data to it.
> > >
> > > This is done through av_opencl_register_kernel_function(), which is
> > > used to register a function which is associated to one of the
> > > previously registered kernel in the global environment.
> > >
> > > so we have: kernel(global env) -> function(global env)
> > >
> > > To run the code of a kernel, av_opencl_run_kernel() must be called,
> > > with the name of the registered kernel on which the function is to be
> > > called.
> > >
> > > This function lookups the functions registered in the global
> > > environment, and executes the registered function with provided user
> > > data/parameters, which in particular must contain the opencl
> > > environment. The environment should contain the kernel handler created
> > > with av_opencl_create_kerne(), and is used to set the arguments for
> > > the function defined in the kernel code, and eventually run the code
> > > for it (see the deshake patch for an example of such usage).
> > >
> > > ...
> > >
> > > So basically this is the workflow:
> > >
> > > kernel code registration (done in the global environment)            ->
> > > av_opencl_register_kernel()
> > > kernel code compilation/init (always done in the global environment) ->
> > > av_opencl_init()
> > >
> > > kernel function registration (can be eventually done *before* init)  ->
> > > av_opencl_register_kernel_function()
> > > kernel object creation, which is required to run the code            ->
> > > av_opencl_create_kernel
> > > kernel code execution with user data parameters                      ->
> > > av_opencl_run_kernel()
> > >
> > > Cleanup:
> > > kernel object (stored in an environment)                             ->
> > > av_opencl_release_kernel()
> > > global environment                                                   ->
> > > av_opencl_uninit()
> > >
> > > ...
> >
> > > Can you confirm that this is an accurate description of the
> > > design/workflow?
> > >
> > yes,you are right
> >
> > >
> > > The main problem with this design is that different threads and
> > > components can messup with the global environment.
> > >
> > > For example you may want to init a filter, this creates a global
> > > environment, then you create another filter/component which requires
> > > to build a different kernel etc., which can't be done since you're
> > > supposed to init the global environment just once.
> > >
>
> > all the opencl code an use the same, such as reuse context, command
> queue,
> > device, platform....... just init once is enough.
>
> Yes, but if opencl was already inited it is not possible to add a new
> kernel, withouth destroying the global environment. And in general you
> may want to add a new kernel on the fly, after the library/opencl have
> been already inited.

the global environment should not be destroied becaust all the kernels will
share the environment..the create kernel functions is like this

 cl_int status;
    if (strlen(kernel_entry_point) > sizeof(env->kernel_entry_point)) {
        av_log(&openclutils, AV_LOG_ERROR, "Created kernel name %s is too
long\n", kernel_entry_point);
        return AVERROR(EINVAL);
    }
    if (!env->kernel) {
        env->kernel = clCreateKernel(gpu_env.program, kernel_entry_point,
&status);
        if (status != CL_SUCCESS) {
            av_log(&openclutils, AV_LOG_ERROR, "Could not create OpenCL
kernel: %s\n", opencl_errstr(status));
            return AVERROR_EXTERNAL;
        }
        env->context       = gpu_env.context;
        env->command_queue = gpu_env.command_queue;
        env->program       = gpu_env.program;
        av_strlcpy(env->kernel_entry_point, kernel_entry_point,
sizeof(env->kernel_entry_point));
    }
    return 0;
so it just call the api to create a kernel and pass the global enviroment,
you can create kernel every where, but the kernel code must  registerd
before I think it is a operation like that you should register a av_filter
then use it, is is right?

> >
> > >
> > > Ideally we should have one OpenCL context per component, so we don't
> > > need to know everything (kernel code and functions) when we init the
> > > OpenCL system, and by using a global environment you are prevented
> > > from doing that.
> > >
> > > all kernels can reuse the opencl environment, no need to create for
> > each.also the init function accept the externel OpenCL environment
> > "AVOpenCLExternalInfo" from application, if the environment is created by
> > application, the library will not release the environment.
>
> The problem again is that there is no way to register and compile new
> code on the fly for a new component, after the system have been
> already inited, unless you release the old opencl environment.
>
> So you can have this sequence:
>
> register kernels
> av_opencl_init()
> register kernels
> av_opencl_init() -> no op: the new kernels are never compiled
>
all the kernels have registered in function
"av_opencl_register_kernel_function", and when init, it will compiled all
the kernel, so the secode calling don't need to compile the functions
again.

>
> > > In a similar way, when you uninit the OpenCL system you don't know if
> > > other components are actually using it, so the only safe way is to
> > > uninit() it when you close the *application*, which is not ideal for a
> > > library.
> > >
> > I set a counter to count whether all kernels are released
> > "gpu_env.runtime_kernel_count--; ", if all the kernel is released ,then
> > release the OpenCL environment(it should wait for all the kernels have
> been
> > released.).the unint function just release the environment created by the
> > library itself,"gpu_env.is_user_created" can indicate that whether the
> > OpenCL enviroment is created by application or the opencl library itself,
> > it only set to 1 if the OpenCL enviroment is created by application in
> > av_opencl_init.There are two paths, one is application create the opencl
> > library itself,then it release it. the other is created by application,
> > then application release it.
>
> gpu_env.runtime_kernel_count is increased when a new function is
> registered.
> Also note that you increase the counter every time, so you can have:
>
> gpu_env.runtime_kernel_count = 0;
>
> av_opencl_register_kernel_function("foobar", fn)
> gpu_env.runtime_kernel_count -> 1
>
> av_opencl_register_kernel_function("foobar", fn)
> // registered again, the new function overwrites the old one
> gpu_env.runtime_kernel_count -> 2
>
> av_opencl_uninit()
> gpu_env.runtime_kernel_count--;
> gpu_env.runtime_kernel_count -> 1
> -> not released
>
why call  av_opencl_register_kernel_function twice and uninit once? at the
start we design that when call uninit ,it will release all kernels and
environment. but it should placed in the applications uninit code. Do you
think so? and second is like this, when you register a kernel function, you
should call the uninit, the should exist in pairs

>
> Also in case you never register a function you have:
> gpu_env.runtime_kernel_count = 0;
> av_opencl_uninit()
> gpu_env.runtime_kernel_count--;
> gpu_env.runtime_kernel_count -> -INT_MAX
> -> not released
>
> [...]
>
> I'll try to suggest an alternative design when commenting on the new
> patch.
> --
> FFmpeg = Fast and Faithful Mean Practical Exploitable Guru
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>


More information about the ffmpeg-devel mailing list