• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * cl_pyramid_blender.cpp - CL multi-band blender
3  *
4  *  Copyright (c) 2016 Intel Corporation
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * Author: Wind Yuan <feng.yuan@intel.com>
19  */
20 
21 #include "cl_pyramid_blender.h"
22 #include <algorithm>
23 #include "xcam_obj_debug.h"
24 #include "cl_device.h"
25 #include "cl_utils.h"
26 
27 #if CL_PYRAMID_ENABLE_DUMP
28 #define BLENDER_PROFILING_START(name)  XCAM_STATIC_PROFILING_START(name)
29 #define BLENDER_PROFILING_END(name, times_of_print)  XCAM_STATIC_PROFILING_END(name, times_of_print)
30 #else
31 #define BLENDER_PROFILING_START(name)
32 #define BLENDER_PROFILING_END(name, times_of_print)
33 #endif
34 
35 //#define SAMPLER_POSITION_OFFSET -0.25f
36 #define SAMPLER_POSITION_OFFSET 0.0f
37 
38 #define SEAM_POS_TYPE int16_t
39 #define SEAM_SUM_TYPE float
40 #define SEAM_MASK_TYPE uint8_t
41 
42 namespace XCam {
43 
44 enum {
45     KernelPyramidTransform   = 0,
46     KernelPyramidReconstruct,
47     KernelPyramidBlender,
48     KernelPyramidScale,
49     KernelPyramidCopy,
50     KernelPyramidLap,
51     KernelImageDiff,
52     KernelSeamDP,
53     KernelSeamMaskScale,
54     KernelSeamMaskScaleSLM,
55     KernelSeamBlender
56 };
57 
58 static const XCamKernelInfo kernels_info [] = {
59     {
60         "kernel_gauss_scale_transform",
61 #include "kernel_gauss_lap_pyramid.clx"
62         , 0,
63     },
64     {
65         "kernel_gauss_lap_reconstruct",
66 #include "kernel_gauss_lap_pyramid.clx"
67         , 0,
68     },
69     {
70         "kernel_pyramid_blend",
71 #include "kernel_gauss_lap_pyramid.clx"
72         , 0,
73     },
74     {
75         "kernel_pyramid_scale",
76 #include "kernel_gauss_lap_pyramid.clx"
77         , 0,
78     },
79     {
80         "kernel_pyramid_copy",
81 #include "kernel_gauss_lap_pyramid.clx"
82         , 0,
83     },
84     {
85         "kernel_lap_transform",
86 #include "kernel_gauss_lap_pyramid.clx"
87         , 0,
88     },
89     {
90         "kernel_image_diff",
91 #include "kernel_gauss_lap_pyramid.clx"
92         , 0,
93     },
94     {
95         "kernel_seam_dp",
96 #include "kernel_gauss_lap_pyramid.clx"
97         , 0,
98     },
99     {
100         "kernel_mask_gauss_scale",
101 #include "kernel_gauss_lap_pyramid.clx"
102         , 0,
103     },
104     {
105         "kernel_mask_gauss_scale_slm",
106 #include "kernel_gauss_lap_pyramid.clx"
107         , 0,
108     },
109     {
110         "kernel_seam_mask_blend",
111 #include "kernel_gauss_lap_pyramid.clx"
112         , 0,
113     }
114 };
115 
116 static uint32_t
clamp(int32_t i,int32_t min,int32_t max)117 clamp(int32_t i, int32_t min, int32_t max)
118 {
119     if (i < min)
120         return min;
121     if (i > max - 1)
122         return max - 1;
123     return i;
124 }
125 
126 static float*
get_gauss_coeffs(int radius,float sigma)127 get_gauss_coeffs (int radius, float sigma)
128 {
129     static int g_radius = 0;
130     static float g_sigma = 0;
131     static float g_table[512] = {0.0f};
132 
133     int i;
134     int scale = radius * 2 + 1;
135     float dis = 0.0f, sum = 0.0f;
136 
137     if (g_radius == radius && g_sigma == sigma)
138         return g_table;
139 
140     XCAM_ASSERT (scale < 512);
141 
142     for (i = 0; i < scale; i++)  {
143         dis = ((float)i - radius) * ((float)i - radius);
144         g_table[i] = exp(-dis / (2.0f * sigma * sigma));
145         sum += g_table[i];
146     }
147 
148     for(i = 0; i < scale; i++)
149         g_table[i] = g_table[i] / sum;
150 
151     g_radius = radius;
152     g_sigma = sigma;
153 
154     return g_table;
155 }
156 
157 static bool
gauss_blur_buffer(SmartPtr<CLBuffer> & buf,int buf_len,int g_radius,float g_sigma)158 gauss_blur_buffer (SmartPtr<CLBuffer> &buf, int buf_len, int g_radius, float g_sigma)
159 {
160     float *buf_ptr = NULL;
161     float *coeff = NULL;
162     XCamReturn ret = XCAM_RETURN_NO_ERROR;
163     float *tmp_ptr = NULL;
164 
165     coeff = get_gauss_coeffs (g_radius, g_sigma);
166     XCAM_ASSERT (coeff);
167 
168     ret = buf->enqueue_map((void*&)buf_ptr, 0, buf_len * sizeof (float));
169     XCAM_FAIL_RETURN (ERROR, ret == XCAM_RETURN_NO_ERROR, false, "gauss_blur_buffer failed on enqueue_map");
170 
171     tmp_ptr = (float *)xcam_malloc (buf_len * sizeof (float));
172     XCAM_ASSERT (tmp_ptr);
173     for (int i = 0; i < buf_len; ++i) {
174         tmp_ptr[i] = 0.0f;
175         for (int j = -g_radius; j <= (int)g_radius; ++j) {
176             tmp_ptr[i] += buf_ptr[clamp(i + j, 0, buf_len)] * coeff[g_radius + j];
177         }
178     }
179 
180     for (int i = 0; i < buf_len; ++i) {
181         buf_ptr[i] = tmp_ptr[i];
182     }
183     xcam_free (tmp_ptr);
184     buf->enqueue_unmap((void*)buf_ptr);
185     return true;
186 }
187 
PyramidLayer()188 PyramidLayer::PyramidLayer ()
189     : blend_width (0)
190     , blend_height (0)
191 {
192     for (int plane = 0; plane < CLBlenderPlaneMax; ++plane) {
193         for (int i = 0; i < XCAM_BLENDER_IMAGE_NUM; ++i) {
194             gauss_offset_x[plane][i] = 0;
195             lap_offset_x[plane][i] = 0;
196         }
197         mask_width [plane] = 0;
198     }
199 }
200 
CLPyramidBlender(const SmartPtr<CLContext> & context,const char * name,int layers,bool need_uv,bool need_seam,CLBlenderScaleMode scale_mode)201 CLPyramidBlender::CLPyramidBlender (
202     const SmartPtr<CLContext> &context, const char *name,
203     int layers, bool need_uv, bool need_seam, CLBlenderScaleMode scale_mode)
204     : CLBlender (context, name, need_uv, scale_mode)
205     , _layers (0)
206     , _need_seam (need_seam)
207     , _seam_pos_stride (0)
208     , _seam_width (0)
209     , _seam_height (0)
210     , _seam_pos_offset_x (0)
211     , _seam_pos_valid_width (0)
212     , _seam_mask_done (false)
213 {
214     if (layers <= 1)
215         _layers = 1;
216     else if (layers > XCAM_CL_PYRAMID_MAX_LEVEL)
217         _layers = XCAM_CL_PYRAMID_MAX_LEVEL;
218     else
219         _layers = (uint32_t)layers;
220 }
221 
~CLPyramidBlender()222 CLPyramidBlender::~CLPyramidBlender ()
223 {
224 }
225 
226 SmartPtr<CLImage>
get_gauss_image(uint32_t layer,uint32_t buf_index,bool is_uv)227 CLPyramidBlender::get_gauss_image (uint32_t layer, uint32_t buf_index, bool is_uv)
228 {
229     XCAM_ASSERT (layer < _layers);
230     XCAM_ASSERT (buf_index < XCAM_BLENDER_IMAGE_NUM);
231     uint32_t plane = (is_uv ? 1 : 0);
232     return _pyramid_layers[layer].gauss_image[plane][buf_index];
233 }
234 
235 SmartPtr<CLImage>
get_lap_image(uint32_t layer,uint32_t buf_index,bool is_uv)236 CLPyramidBlender::get_lap_image (uint32_t layer, uint32_t buf_index, bool is_uv)
237 {
238     XCAM_ASSERT (layer < _layers);
239     XCAM_ASSERT (buf_index < XCAM_BLENDER_IMAGE_NUM);
240     uint32_t plane = (is_uv ? 1 : 0);
241 
242     return _pyramid_layers[layer].lap_image[plane][buf_index];
243 }
244 
245 SmartPtr<CLImage>
get_blend_image(uint32_t layer,bool is_uv)246 CLPyramidBlender::get_blend_image (uint32_t layer, bool is_uv)
247 {
248     XCAM_ASSERT (layer < _layers);
249     uint32_t plane = (is_uv ? 1 : 0);
250 
251     return _pyramid_layers[layer].blend_image[plane][BlendImageIndex];
252 }
253 
254 SmartPtr<CLImage>
get_reconstruct_image(uint32_t layer,bool is_uv)255 CLPyramidBlender::get_reconstruct_image (uint32_t layer, bool is_uv)
256 {
257     XCAM_ASSERT (layer < _layers);
258     uint32_t plane = (is_uv ? 1 : 0);
259     return _pyramid_layers[layer].blend_image[plane][ReconstructImageIndex];
260 }
261 
262 SmartPtr<CLImage>
get_scale_image(bool is_uv)263 CLPyramidBlender::get_scale_image (bool is_uv)
264 {
265     uint32_t plane = (is_uv ? 1 : 0);
266     return _pyramid_layers[0].scale_image[plane];
267 }
268 
269 SmartPtr<CLBuffer>
get_blend_mask(uint32_t layer,bool is_uv)270 CLPyramidBlender::get_blend_mask (uint32_t layer, bool is_uv)
271 {
272     XCAM_ASSERT (layer < _layers);
273     uint32_t plane = (is_uv ? 1 : 0);
274     return _pyramid_layers[layer].blend_mask[plane];
275 }
276 
277 SmartPtr<CLImage>
get_seam_mask(uint32_t layer)278 CLPyramidBlender::get_seam_mask (uint32_t layer)
279 {
280     XCAM_ASSERT (layer < _layers);
281     return _pyramid_layers[layer].seam_mask[CLSeamMaskCoeff];
282 }
283 
284 const PyramidLayer &
get_pyramid_layer(uint32_t layer) const285 CLPyramidBlender::get_pyramid_layer (uint32_t layer) const
286 {
287     return _pyramid_layers[layer];
288 }
289 
290 const SmartPtr<CLImage> &
get_image_diff() const291 CLPyramidBlender::get_image_diff () const
292 {
293     return _image_diff;
294 }
295 
296 void
get_seam_info(uint32_t & width,uint32_t & height,uint32_t & stride) const297 CLPyramidBlender::get_seam_info (uint32_t &width, uint32_t &height, uint32_t &stride) const
298 {
299     width = _seam_width;
300     height = _seam_height;
301     stride = _seam_pos_stride;
302 }
303 
304 void
get_seam_pos_info(uint32_t & offset_x,uint32_t & valid_width) const305 CLPyramidBlender::get_seam_pos_info (uint32_t &offset_x, uint32_t &valid_width) const
306 {
307     offset_x = _seam_pos_offset_x;
308     valid_width = _seam_pos_valid_width;
309 }
310 
311 void
bind_buf_to_layer0(SmartPtr<CLContext> context,SmartPtr<VideoBuffer> & input0,SmartPtr<VideoBuffer> & input1,SmartPtr<VideoBuffer> & output,const Rect & merge0_rect,const Rect & merge1_rect,bool need_uv,CLBlenderScaleMode scale_mode)312 PyramidLayer::bind_buf_to_layer0 (
313     SmartPtr<CLContext> context,
314     SmartPtr<VideoBuffer> &input0, SmartPtr<VideoBuffer> &input1, SmartPtr<VideoBuffer> &output,
315     const Rect &merge0_rect, const Rect &merge1_rect, bool need_uv, CLBlenderScaleMode scale_mode)
316 {
317     const VideoBufferInfo &in0_info = input0->get_video_info ();
318     const VideoBufferInfo &in1_info = input1->get_video_info ();
319     const VideoBufferInfo &out_info = output->get_video_info ();
320     int max_plane = (need_uv ? 2 : 1);
321     uint32_t divider_vert[2] = {1, 2};
322 
323     XCAM_ASSERT (in0_info.height == in1_info.height);
324     XCAM_ASSERT (merge0_rect.width == merge1_rect.width);
325 
326     this->blend_width = XCAM_ALIGN_UP (merge0_rect.width, XCAM_CL_BLENDER_ALIGNMENT_X);
327     this->blend_height = merge0_rect.height;
328 
329     CLImageDesc cl_desc;
330     cl_desc.format.image_channel_data_type = CL_UNSIGNED_INT16;
331     cl_desc.format.image_channel_order = CL_RGBA;
332 
333     for (int i_plane = 0; i_plane < max_plane; ++i_plane) {
334         cl_desc.width = in0_info.width / 8;
335         cl_desc.height = in0_info.height / divider_vert[i_plane];
336         cl_desc.row_pitch = in0_info.strides[i_plane];
337         this->gauss_image[i_plane][0] = convert_to_climage (context, input0, cl_desc, in0_info.offsets[i_plane]);
338         this->gauss_offset_x[i_plane][0] = merge0_rect.pos_x; // input0 offset
339 
340         cl_desc.width = in1_info.width / 8;
341         cl_desc.height = in1_info.height / divider_vert[i_plane];
342         cl_desc.row_pitch = in1_info.strides[i_plane];
343         this->gauss_image[i_plane][1] = convert_to_climage (context, input1, cl_desc, in1_info.offsets[i_plane]);
344         this->gauss_offset_x[i_plane][1] = merge1_rect.pos_x; // input1 offset
345 
346         cl_desc.width = out_info.width / 8;
347         cl_desc.height = out_info.height / divider_vert[i_plane];
348         cl_desc.row_pitch = out_info.strides[i_plane];
349 
350         if (scale_mode == CLBlenderScaleLocal) {
351             this->scale_image[i_plane] = convert_to_climage (context, output, cl_desc, out_info.offsets[i_plane]);
352 
353             cl_desc.width = XCAM_ALIGN_UP (this->blend_width, XCAM_CL_BLENDER_ALIGNMENT_X) / 8;
354             cl_desc.height = XCAM_ALIGN_UP (this->blend_height, divider_vert[i_plane]) / divider_vert[i_plane];
355             uint32_t row_pitch = CLImage::calculate_pixel_bytes (cl_desc.format) *
356                                  XCAM_ALIGN_UP (cl_desc.width, XCAM_CL_IMAGE_ALIGNMENT_X);
357             uint32_t size = row_pitch * cl_desc.height;
358             SmartPtr<CLBuffer> cl_buf = new CLBuffer (context, size);
359             XCAM_ASSERT (cl_buf.ptr () && cl_buf->is_valid ());
360             cl_desc.row_pitch = row_pitch;
361             this->blend_image[i_plane][ReconstructImageIndex] = new CLImage2D (context, cl_desc, 0, cl_buf);
362         } else {
363             this->blend_image[i_plane][ReconstructImageIndex] =
364                 convert_to_climage (context, output, cl_desc, out_info.offsets[i_plane]);
365         }
366         XCAM_ASSERT (this->blend_image[i_plane][ReconstructImageIndex].ptr ());
367     }
368 
369 }
370 
371 void
init_layer0(SmartPtr<CLContext> context,bool last_layer,bool need_uv,int mask_radius,float mask_sigma)372 PyramidLayer::init_layer0 (SmartPtr<CLContext> context, bool last_layer, bool need_uv, int mask_radius, float mask_sigma)
373 {
374     XCAM_ASSERT (this->blend_width && this->blend_height);
375 
376     //init mask
377     this->mask_width[0] = this->blend_width;
378     uint32_t mask_size = this->mask_width[0] * sizeof (float);
379     this->blend_mask[0] = new CLBuffer(context, mask_size);
380     float *blend_ptr = NULL;
381     XCamReturn ret = this->blend_mask[0]->enqueue_map((void*&)blend_ptr, 0, mask_size);
382     if (!xcam_ret_is_ok (ret)) {
383         XCAM_LOG_ERROR ("PyramidLayer init layer0 failed in blend_mask mem_map");
384         return;
385     }
386 
387     for (uint32_t i_ptr = 0; i_ptr < this->mask_width[0]; ++i_ptr) {
388         if (i_ptr <= this->mask_width[0] / 2)
389             blend_ptr[i_ptr] = 1.0f;
390         else
391             blend_ptr[i_ptr] = 0.0f;
392     }
393     this->blend_mask[0]->enqueue_unmap ((void*)blend_ptr);
394     gauss_blur_buffer (this->blend_mask[0], this->mask_width[0], mask_radius, mask_sigma);
395 
396     if (need_uv)
397         copy_mask_from_y_to_uv (context);
398 
399     if (last_layer)
400         return;
401 
402     int max_plane = (need_uv ? 2 : 1);
403     uint32_t divider_vert[2] = {1, 2};
404     CLImageDesc cl_desc;
405     cl_desc.format.image_channel_data_type = CL_UNSIGNED_INT16;
406     cl_desc.format.image_channel_order = CL_RGBA;
407     for (int i_plane = 0; i_plane < max_plane; ++i_plane) {
408         cl_desc.width = this->blend_width / 8;
409         cl_desc.height = XCAM_ALIGN_UP (this->blend_height, divider_vert[i_plane]) / divider_vert[i_plane];
410 
411         this->blend_image[i_plane][BlendImageIndex] = new CLImage2D (context, cl_desc);
412         this->lap_image[i_plane][0] = new CLImage2D (context, cl_desc);
413         this->lap_image[i_plane][1] = new CLImage2D (context, cl_desc);
414         this->lap_offset_x[i_plane][0] = this->lap_offset_x[i_plane][1] = 0;
415 
416 #if CL_PYRAMID_ENABLE_DUMP
417         this->dump_gauss_resize[i_plane] = new CLImage2D (context, cl_desc);
418         this->dump_original[i_plane][0] = new CLImage2D (context, cl_desc);
419         this->dump_original[i_plane][1] = new CLImage2D (context, cl_desc);
420         this->dump_final[i_plane] = new CLImage2D (context, cl_desc);
421 #endif
422     }
423 }
424 
425 void
build_cl_images(SmartPtr<CLContext> context,bool last_layer,bool need_uv)426 PyramidLayer::build_cl_images (SmartPtr<CLContext> context, bool last_layer, bool need_uv)
427 {
428     uint32_t size = 0, row_pitch = 0;
429     CLImageDesc cl_desc_set;
430     SmartPtr<CLBuffer> cl_buf;
431     uint32_t divider_vert[2] = {1, 2};
432     uint32_t max_plane = (need_uv ? 2 : 1);
433 
434     cl_desc_set.format.image_channel_data_type = CL_UNSIGNED_INT16;
435     cl_desc_set.format.image_channel_order = CL_RGBA;
436 
437     for (uint32_t plane = 0; plane < max_plane; ++plane) {
438         for (int i_image = 0; i_image < XCAM_BLENDER_IMAGE_NUM; ++i_image) {
439             cl_desc_set.row_pitch = 0;
440             cl_desc_set.width = XCAM_ALIGN_UP (this->blend_width, XCAM_CL_BLENDER_ALIGNMENT_X) / 8;
441             cl_desc_set.height = XCAM_ALIGN_UP (this->blend_height, divider_vert[plane]) / divider_vert[plane];
442 
443             //gauss y image created by cl buffer
444             row_pitch = CLImage::calculate_pixel_bytes (cl_desc_set.format) *
445                         XCAM_ALIGN_UP (cl_desc_set.width, XCAM_CL_IMAGE_ALIGNMENT_X);
446             size = row_pitch * cl_desc_set.height;
447             cl_buf = new CLBuffer (context, size);
448             XCAM_ASSERT (cl_buf.ptr () && cl_buf->is_valid ());
449             cl_desc_set.row_pitch = row_pitch;
450             this->gauss_image[plane][i_image] = new CLImage2D (context, cl_desc_set, 0, cl_buf);
451             XCAM_ASSERT (this->gauss_image[plane][i_image].ptr ());
452             this->gauss_offset_x[plane][i_image]  = 0; // offset to 0, need recalculate if for deep multi-band blender
453         }
454 
455         cl_desc_set.width = XCAM_ALIGN_UP (this->blend_width, XCAM_CL_BLENDER_ALIGNMENT_X) / 8;
456         cl_desc_set.height = XCAM_ALIGN_UP (this->blend_height, divider_vert[plane]) / divider_vert[plane];
457         row_pitch = CLImage::calculate_pixel_bytes (cl_desc_set.format) *
458                     XCAM_ALIGN_UP (cl_desc_set.width, XCAM_CL_IMAGE_ALIGNMENT_X);
459         size = row_pitch * cl_desc_set.height;
460         cl_buf = new CLBuffer (context, size);
461         XCAM_ASSERT (cl_buf.ptr () && cl_buf->is_valid ());
462         cl_desc_set.row_pitch = row_pitch;
463         this->blend_image[plane][ReconstructImageIndex] = new CLImage2D (context, cl_desc_set, 0, cl_buf);
464         XCAM_ASSERT (this->blend_image[plane][ReconstructImageIndex].ptr ());
465 #if CL_PYRAMID_ENABLE_DUMP
466         this->dump_gauss_resize[plane] = new CLImage2D (context, cl_desc_set);
467         this->dump_original[plane][0] = new CLImage2D (context, cl_desc_set);
468         this->dump_original[plane][1] = new CLImage2D (context, cl_desc_set);
469         this->dump_final[plane] = new CLImage2D (context, cl_desc_set);
470 #endif
471         if (!last_layer) {
472             cl_desc_set.row_pitch = 0;
473             this->blend_image[plane][BlendImageIndex] = new CLImage2D (context, cl_desc_set);
474             XCAM_ASSERT (this->blend_image[plane][BlendImageIndex].ptr ());
475             for (int i_image = 0; i_image < XCAM_BLENDER_IMAGE_NUM; ++i_image) {
476                 this->lap_image[plane][i_image] = new CLImage2D (context, cl_desc_set);
477                 XCAM_ASSERT (this->lap_image[plane][i_image].ptr ());
478                 this->lap_offset_x[plane][i_image]  = 0; // offset to 0, need calculate from next layer if for deep multi-band blender
479             }
480         }
481     }
482 }
483 
484 bool
copy_mask_from_y_to_uv(SmartPtr<CLContext> & context)485 PyramidLayer::copy_mask_from_y_to_uv (SmartPtr<CLContext> &context)
486 {
487     XCamReturn ret = XCAM_RETURN_NO_ERROR;
488     XCAM_ASSERT (this->mask_width[0]);
489     XCAM_ASSERT (this->blend_mask[0].ptr ());
490 
491     this->mask_width[1] = (this->mask_width[0] + 1) / 2;
492     this->blend_mask[1] = new CLBuffer (context, this->mask_width[1] * sizeof(float));
493     XCAM_ASSERT (this->blend_mask[1].ptr ());
494 
495     float *from_ptr = NULL;
496     float *to_ptr = NULL;
497     ret = this->blend_mask[1]->enqueue_map ((void*&)to_ptr, 0, this->mask_width[1] * sizeof(float));
498     XCAM_FAIL_RETURN (ERROR, xcam_ret_is_ok (ret), false, "PyramidLayer copy mask failed in blend_mask[1] mem_map");
499     ret = this->blend_mask[0]->enqueue_map((void*&)from_ptr, 0, this->mask_width[0] * sizeof(float));
500     XCAM_FAIL_RETURN (ERROR, xcam_ret_is_ok (ret), false, "PyramidLayer copy mask failed in blend_mask[0] mem_map");
501 
502     for (int i = 0; i < (int)this->mask_width[1]; ++i) {
503         if (i * 2 + 1 >= (int)this->mask_width[0]) { // todo i* 2 + 1
504             XCAM_ASSERT (i * 2 < (int)this->mask_width[0]);
505             to_ptr[i] = from_ptr[i * 2] / 2.0f;
506         } else {
507             to_ptr[i] = (from_ptr[i * 2] + from_ptr[i * 2 + 1]) / 2.0f;
508         }
509     }
510     this->blend_mask[1]->enqueue_unmap ((void*)to_ptr);
511     this->blend_mask[0]->enqueue_unmap ((void*)from_ptr);
512 
513     return true;
514 }
515 
516 void
last_layer_buffer_redirect()517 CLPyramidBlender::last_layer_buffer_redirect ()
518 {
519     PyramidLayer &layer = _pyramid_layers[_layers - 1];
520     uint32_t max_plane = (need_uv () ? 2 : 1);
521 
522     for (uint32_t plane = 0; plane < max_plane; ++plane) {
523         layer.blend_image[plane][BlendImageIndex] = layer.blend_image[plane][ReconstructImageIndex];
524 
525         for (uint32_t i_image = 0; i_image < XCAM_BLENDER_IMAGE_NUM; ++i_image) {
526             layer.lap_image[plane][i_image] = layer.gauss_image[plane][i_image];
527         }
528     }
529 }
530 
531 void
dump_layer_mask(uint32_t layer,bool is_uv)532 CLPyramidBlender::dump_layer_mask (uint32_t layer, bool is_uv)
533 {
534     const PyramidLayer &pyr_layer = get_pyramid_layer (layer);
535     int plane = (is_uv ? 1 : 0);
536 
537     float *mask_ptr = NULL;
538     XCamReturn ret = pyr_layer.blend_mask[plane]->enqueue_map ((void*&)mask_ptr, 0, pyr_layer.mask_width[plane] * sizeof(float));
539     if (!xcam_ret_is_ok (ret)) {
540         XCAM_LOG_ERROR ("CLPyramidBlender dump mask failed in blend_mask(layer:%d) mem_map", layer);
541         return;
542     }
543 
544     printf ("layer(%d)(-%s) mask, width:%d\n", layer, (is_uv ? "UV" : "Y"), pyr_layer.mask_width[plane]);
545     for (uint32_t i = 0; i < pyr_layer.mask_width[plane]; ++i) {
546         printf ("%.03f\t", mask_ptr[i]);
547     }
548     printf ("\n");
549 
550     pyr_layer.blend_mask[plane]->enqueue_unmap ((void*)mask_ptr);
551 }
552 
553 static bool
gauss_fill_mask(SmartPtr<CLContext> context,PyramidLayer & prev,PyramidLayer & to,bool need_uv,int mask_radius,float mask_sigma)554 gauss_fill_mask (
555     SmartPtr<CLContext> context, PyramidLayer &prev, PyramidLayer &to, bool need_uv,
556     int mask_radius, float mask_sigma)
557 {
558     XCamReturn ret = XCAM_RETURN_NO_ERROR;
559     uint32_t mask_size = to.blend_width * sizeof (float);
560     uint32_t prev_size = prev.mask_width[0] * sizeof (float);
561     float *pre_ptr = NULL;
562     int i;
563 
564     //gauss to[0]
565     to.mask_width[0] = to.blend_width;
566     to.blend_mask[0] = new CLBuffer (context, mask_size);
567     XCAM_ASSERT (to.blend_mask[0].ptr ());
568     float *mask0_ptr = NULL;
569     ret = to.blend_mask[0]->enqueue_map((void*&)mask0_ptr, 0, mask_size);
570     XCAM_FAIL_RETURN (ERROR, xcam_ret_is_ok (ret), false, "gauss_fill_mask failed in destination image mem_map");
571 
572     ret = prev.blend_mask[0]->enqueue_map((void*&)pre_ptr, 0, prev_size);
573     XCAM_FAIL_RETURN (ERROR, xcam_ret_is_ok (ret), false, "gauss_fill_mask failed in source image mem_map");
574 
575     for (i = 0; i < (int)to.blend_width; ++i) {
576         if (i * 2 + 1 >= (int)prev.mask_width[0]) { // todo i* 2 + 1
577             XCAM_ASSERT (i * 2 < (int)prev.mask_width[0]);
578             mask0_ptr[i] = pre_ptr[i * 2] / 2.0f;
579         } else {
580             mask0_ptr[i] = (pre_ptr[i * 2] + pre_ptr[i * 2 + 1]) / 2.0f;
581         }
582     }
583     prev.blend_mask[0]->enqueue_unmap ((void*)pre_ptr);
584     to.blend_mask[0]->enqueue_unmap ((void*)mask0_ptr);
585 
586     gauss_blur_buffer (to.blend_mask[0], to.mask_width[0], mask_radius, mask_sigma);
587 
588     if (need_uv)
589         to.copy_mask_from_y_to_uv (context);
590 
591     return true;
592 }
593 
594 XCamReturn
allocate_cl_buffers(SmartPtr<CLContext> context,SmartPtr<VideoBuffer> & input0,SmartPtr<VideoBuffer> & input1,SmartPtr<VideoBuffer> & output)595 CLPyramidBlender::allocate_cl_buffers (
596     SmartPtr<CLContext> context,
597     SmartPtr<VideoBuffer> &input0, SmartPtr<VideoBuffer> &input1,
598     SmartPtr<VideoBuffer> &output)
599 {
600     uint32_t index = 0;
601     const Rect & window = get_merge_window ();
602     bool need_reallocate = true;
603     XCamReturn ret = XCAM_RETURN_NO_ERROR;
604 
605     BLENDER_PROFILING_START (allocate_cl_buffers);
606 
607     need_reallocate =
608         (window.width != (int32_t)_pyramid_layers[0].blend_width ||
609          (window.height != 0 && window.height != (int32_t)_pyramid_layers[0].blend_height));
610     _pyramid_layers[0].bind_buf_to_layer0 (
611         context, input0, input1, output,
612         get_input_merge_area (0), get_input_merge_area (1),
613         need_uv (), get_scale_mode ());
614 
615     if (need_reallocate) {
616         int g_radius = (((float)(window.width - 1) / 2) / (1 << _layers)) * 1.2f;
617         float g_sigma = (float)g_radius;
618 
619         _pyramid_layers[0].init_layer0 (context, (0 == _layers - 1), need_uv(), g_radius, g_sigma);
620 
621         for (index = 1; index < _layers; ++index) {
622             _pyramid_layers[index].blend_width = (_pyramid_layers[index - 1].blend_width + 1) / 2;
623             _pyramid_layers[index].blend_height = (_pyramid_layers[index - 1].blend_height + 1) / 2;
624 
625             _pyramid_layers[index].build_cl_images (context, (index == _layers - 1), need_uv ());
626             if (!_need_seam) {
627                 gauss_fill_mask (context, _pyramid_layers[index - 1], _pyramid_layers[index], need_uv (), g_radius, g_sigma);
628             }
629         }
630 
631         if (_need_seam) {
632             ret = init_seam_buffers (context);
633             XCAM_FAIL_RETURN (ERROR, ret == XCAM_RETURN_NO_ERROR, ret, "CLPyramidBlender init seam buffer failed");
634         }
635     }
636 
637     //last layer buffer redirect
638     last_layer_buffer_redirect ();
639     _seam_mask_done = false;
640 
641     BLENDER_PROFILING_END (allocate_cl_buffers, 50);
642 
643     return XCAM_RETURN_NO_ERROR;
644 }
645 
646 XCamReturn
init_seam_buffers(SmartPtr<CLContext> context)647 CLPyramidBlender::init_seam_buffers (SmartPtr<CLContext> context)
648 {
649     const PyramidLayer &layer0 = get_pyramid_layer (0);
650     CLImageDesc cl_desc;
651 
652     _seam_width = layer0.blend_width;
653     _seam_height = layer0.blend_height;
654     _seam_pos_stride = XCAM_ALIGN_UP (_seam_width, 64); // need a buffer large enough to avoid judgement in kernel
655     _seam_pos_offset_x = XCAM_ALIGN_UP (_seam_width / 4, XCAM_CL_BLENDER_ALIGNMENT_X);
656     if (_seam_pos_offset_x >= _seam_width)
657         _seam_pos_offset_x = 0;
658     _seam_pos_valid_width = XCAM_ALIGN_DOWN (_seam_width / 2, XCAM_CL_BLENDER_ALIGNMENT_X);
659     if (_seam_pos_valid_width <= 0)
660         _seam_pos_valid_width = XCAM_CL_BLENDER_ALIGNMENT_X;
661     XCAM_ASSERT (_seam_pos_offset_x + _seam_pos_valid_width <= _seam_width);
662 
663     XCAM_ASSERT (layer0.blend_width > 0 && layer0.blend_height > 0);
664     cl_desc.format.image_channel_data_type = CL_UNSIGNED_INT16;
665     cl_desc.format.image_channel_order = CL_RGBA;
666     cl_desc.width = _seam_width / 8;
667     cl_desc.height = _seam_height;
668     cl_desc.row_pitch = CLImage::calculate_pixel_bytes (cl_desc.format) *
669                         XCAM_ALIGN_UP (cl_desc.width, XCAM_CL_IMAGE_ALIGNMENT_X);
670 
671     uint32_t image_diff_size = cl_desc.row_pitch * _seam_height;
672     SmartPtr<CLBuffer> cl_diff_buf = new CLBuffer (context, image_diff_size);
673     XCAM_FAIL_RETURN (
674         ERROR,
675         cl_diff_buf.ptr () && cl_diff_buf->is_valid (),
676         XCAM_RETURN_ERROR_CL,
677         "CLPyramidBlender init seam buffer failed to create image_difference buffers");
678 
679     _image_diff = new CLImage2D (context, cl_desc, 0, cl_diff_buf);
680     XCAM_FAIL_RETURN (
681         ERROR,
682         _image_diff.ptr () && _image_diff->is_valid (),
683         XCAM_RETURN_ERROR_CL,
684         "CLPyramidBlender init seam buffer failed to bind image_difference data");
685 
686     uint32_t pos_buf_size = sizeof (SEAM_POS_TYPE) * _seam_pos_stride * _seam_height;
687     uint32_t sum_buf_size = sizeof (SEAM_SUM_TYPE) * _seam_pos_stride * 2; // 2 lines
688     _seam_pos_buf = new CLBuffer (context, pos_buf_size, CL_MEM_READ_WRITE);
689     _seam_sum_buf = new CLBuffer (context, sum_buf_size, CL_MEM_READ_WRITE);
690     XCAM_FAIL_RETURN (
691         ERROR,
692         _seam_pos_buf.ptr () && _seam_pos_buf->is_valid () &&
693         _seam_sum_buf.ptr () && _seam_sum_buf->is_valid (),
694         XCAM_RETURN_ERROR_CL,
695         "CLPyramidBlender init seam buffer failed to create seam buffers");
696 
697     uint32_t mask_width = XCAM_ALIGN_UP(_seam_width, XCAM_CL_BLENDER_ALIGNMENT_X);
698     uint32_t mask_height = XCAM_ALIGN_UP(_seam_height, 2);
699     for (uint32_t i = 0; i < _layers; ++i) {
700         cl_desc.format.image_channel_data_type = CL_UNSIGNED_INT16;
701         cl_desc.format.image_channel_order = CL_RGBA;
702         cl_desc.width = mask_width / 8;
703         cl_desc.height = mask_height;
704         cl_desc.row_pitch = CLImage::calculate_pixel_bytes (cl_desc.format) *
705                             XCAM_ALIGN_UP (cl_desc.width, XCAM_CL_IMAGE_ALIGNMENT_X);
706 
707         uint32_t mask_size = cl_desc.row_pitch * mask_height;
708         SmartPtr<CLBuffer> cl_buf0 = new CLBuffer (context, mask_size);
709         SmartPtr<CLBuffer> cl_buf1 = new CLBuffer (context, mask_size);
710         XCAM_ASSERT (cl_buf0.ptr () && cl_buf0->is_valid () && cl_buf1.ptr () && cl_buf1->is_valid ());
711 
712         _pyramid_layers[i].seam_mask[CLSeamMaskTmp] = new CLImage2D (context, cl_desc, 0, cl_buf0);
713         _pyramid_layers[i].seam_mask[CLSeamMaskCoeff] = new CLImage2D (context, cl_desc, 0, cl_buf1);
714         XCAM_FAIL_RETURN (
715             ERROR,
716             _pyramid_layers[i].seam_mask[CLSeamMaskTmp].ptr () && _pyramid_layers[i].seam_mask[CLSeamMaskTmp]->is_valid () &&
717             _pyramid_layers[i].seam_mask[CLSeamMaskCoeff].ptr () && _pyramid_layers[i].seam_mask[CLSeamMaskCoeff]->is_valid (),
718             XCAM_RETURN_ERROR_CL,
719             "CLPyramidBlender init seam buffer failed to create seam_mask buffer");
720 
721         mask_width = XCAM_ALIGN_UP(mask_width / 2, XCAM_CL_BLENDER_ALIGNMENT_X);
722         mask_height = XCAM_ALIGN_UP(mask_height / 2, 2);
723     }
724 
725     return XCAM_RETURN_NO_ERROR;
726 }
727 
728 static void
assign_mask_line(SEAM_MASK_TYPE * mask_ptr,int line,int stride,int delimiter)729 assign_mask_line (SEAM_MASK_TYPE *mask_ptr, int line, int stride, int delimiter)
730 {
731 #define MASK_1 0xFFFF
732 #define MASK_0 0x00
733 
734     SEAM_MASK_TYPE *line_ptr = mask_ptr + line * stride;
735     int mask_1_len = delimiter + 1;
736 
737     memset (line_ptr, MASK_1, sizeof (SEAM_MASK_TYPE) * mask_1_len);
738     memset (line_ptr + mask_1_len, MASK_0, sizeof (SEAM_MASK_TYPE) * (stride - mask_1_len));
739 }
740 
741 XCamReturn
fill_seam_mask()742 CLPyramidBlender::fill_seam_mask ()
743 {
744     XCamReturn ret = XCAM_RETURN_NO_ERROR;
745     XCAM_ASSERT (_seam_pos_buf.ptr () && _seam_sum_buf.ptr ());
746     uint32_t pos_buf_size = sizeof (SEAM_POS_TYPE) * _seam_pos_stride * _seam_height;
747     uint32_t sum_buf_size = sizeof (SEAM_SUM_TYPE) * _seam_pos_stride * 2;
748     SEAM_SUM_TYPE *sum_ptr;
749     SEAM_POS_TYPE *pos_ptr;
750     SEAM_MASK_TYPE *mask_ptr;
751 
752     if (_seam_mask_done)
753         return XCAM_RETURN_NO_ERROR;
754 
755     ret = _seam_sum_buf->enqueue_map ((void *&)sum_ptr, 0, sum_buf_size, CL_MAP_READ);
756     XCAM_FAIL_RETURN (ERROR, ret == XCAM_RETURN_NO_ERROR, ret, "CLPyramidBlender map seam_sum_buf failed");
757 
758     float min_sum = 9999999999.0f, tmp_sum;
759     int pos = 0, min_pos0, min_pos1;
760     int i = 0;
761     SEAM_SUM_TYPE *sum_ptr0 = sum_ptr, *sum_ptr1 = sum_ptr + _seam_pos_stride;
762     for (i = (int)_seam_pos_offset_x; i < (int)(_seam_pos_offset_x + _seam_pos_valid_width); ++i) {
763         tmp_sum = sum_ptr0[i] + sum_ptr1[i];
764         if (tmp_sum >= min_sum)
765             continue;
766         pos = (int)i;
767         min_sum = tmp_sum;
768     }
769     _seam_sum_buf->enqueue_unmap ((void*)sum_ptr);
770     min_pos0 = min_pos1 = pos;
771 
772     BLENDER_PROFILING_START (fill_seam_mask);
773 
774     // reset layer0 seam_mask
775     SmartPtr<CLImage> seam_mask = _pyramid_layers[0].seam_mask[CLSeamMaskTmp];
776     const CLImageDesc &mask_desc = seam_mask->get_image_desc ();
777     size_t mask_origin[3] = {0, 0, 0};
778     size_t mask_region[3] = {mask_desc.width, mask_desc.height, 1};
779     size_t mask_row_pitch;
780     size_t mask_slice_pitch;
781     ret = seam_mask->enqueue_map ((void *&)mask_ptr, mask_origin, mask_region,
782                                   &mask_row_pitch, &mask_slice_pitch, CL_MAP_READ);
783     XCAM_FAIL_RETURN (ERROR, ret == XCAM_RETURN_NO_ERROR, ret, "CLPyramidBlender map seam_mask failed");
784     uint32_t mask_stride = mask_row_pitch / sizeof (SEAM_MASK_TYPE);
785     ret = _seam_pos_buf->enqueue_map ((void *&)pos_ptr, 0, pos_buf_size, CL_MAP_READ);
786     XCAM_FAIL_RETURN (ERROR, ret == XCAM_RETURN_NO_ERROR, ret, "CLPyramidBlender map seam_pos_buf failed");
787     //printf ("***********min sum:%.3f, pos:%d, sum0:%.3f, sum1:%.3f\n", min_sum, pos, sum_ptr0[pos], sum_ptr1[pos]);
788     for (i = _seam_height / 2 - 1; i >= 0; --i) {
789         assign_mask_line (mask_ptr, i, mask_stride, min_pos0);
790         min_pos0 = pos_ptr [i * _seam_pos_stride + min_pos0];
791     }
792 
793     for (i = _seam_height / 2; i < (int)_seam_height; ++i) {
794         assign_mask_line (mask_ptr, i, mask_stride, min_pos1);
795         min_pos1 = pos_ptr [i * _seam_pos_stride + min_pos1];
796     }
797     for (; i < (int)mask_desc.height; ++i) {
798         assign_mask_line (mask_ptr, i, mask_stride, min_pos1);
799     }
800 
801     seam_mask->enqueue_unmap ((void*)mask_ptr);
802     _seam_pos_buf->enqueue_unmap ((void*)pos_ptr);
803 
804     _seam_mask_done = true;
805 
806     BLENDER_PROFILING_END (fill_seam_mask, 50);
807     return XCAM_RETURN_NO_ERROR;
808 }
809 
810 XCamReturn
execute_done(SmartPtr<VideoBuffer> & output)811 CLPyramidBlender::execute_done (SmartPtr<VideoBuffer> &output)
812 {
813     int max_plane = (need_uv () ? 2 : 1);
814     XCAM_UNUSED (output);
815 
816 #if CL_PYRAMID_ENABLE_DUMP
817     dump_buffers ();
818 #endif
819 
820     for (int i_plane = 0; i_plane < max_plane; ++i_plane) {
821         _pyramid_layers[0].gauss_image[i_plane][0].release ();
822         _pyramid_layers[0].gauss_image[i_plane][1].release ();
823         _pyramid_layers[0].blend_image[i_plane][ReconstructImageIndex].release ();
824 
825         if (_layers <= 1) {
826             _pyramid_layers[_layers - 1].blend_image[i_plane][BlendImageIndex].release ();
827             _pyramid_layers[_layers - 1].lap_image[i_plane][0].release ();
828             _pyramid_layers[_layers - 1].lap_image[i_plane][1].release ();
829         }
830     }
831 
832     return XCAM_RETURN_NO_ERROR;
833 }
834 
CLPyramidBlendKernel(const SmartPtr<CLContext> & context,SmartPtr<CLPyramidBlender> & blender,uint32_t layer,bool is_uv,bool need_seam)835 CLPyramidBlendKernel::CLPyramidBlendKernel (
836     const SmartPtr<CLContext> &context, SmartPtr<CLPyramidBlender> &blender,
837     uint32_t layer, bool is_uv, bool need_seam)
838     : CLImageKernel (context)
839     , _blender (blender)
840     , _layer (layer)
841     , _is_uv (is_uv)
842     , _need_seam (need_seam)
843 {
844 }
845 
846 XCamReturn
prepare_arguments(CLArgList & args,CLWorkSize & work_size)847 CLPyramidBlendKernel::prepare_arguments (CLArgList &args, CLWorkSize &work_size)
848 {
849     SmartPtr<CLContext> context = get_context ();
850 
851     SmartPtr<CLImage> image_in0 = get_input_0 ();
852     SmartPtr<CLImage> image_in1 = get_input_1 ();
853     SmartPtr<CLImage> image_out = get_output ();
854     SmartPtr<CLMemory> buf_mask;
855     if (_need_seam)
856         buf_mask = get_seam_mask ();
857     else
858         buf_mask = get_blend_mask ();
859 
860     XCAM_ASSERT (image_in0.ptr () && image_in1.ptr () && image_out.ptr ());
861     const CLImageDesc &cl_desc_out = image_out->get_image_desc ();
862 
863     args.push_back (new CLMemArgument (image_in0));
864     args.push_back (new CLMemArgument (image_in1));
865     args.push_back (new CLMemArgument (buf_mask));
866     args.push_back (new CLMemArgument (image_out));
867 
868     work_size.dim = XCAM_DEFAULT_IMAGE_DIM;
869     work_size.local[0] = 8;
870     work_size.local[1] = 8;
871     work_size.global[0] = XCAM_ALIGN_UP (cl_desc_out.width, work_size.local[0]);
872     work_size.global[1] = XCAM_ALIGN_UP (cl_desc_out.height, work_size.local[1]);
873     return XCAM_RETURN_NO_ERROR;
874 }
875 
CLPyramidTransformKernel(const SmartPtr<CLContext> & context,SmartPtr<CLPyramidBlender> & blender,uint32_t layer,uint32_t buf_index,bool is_uv)876 CLPyramidTransformKernel::CLPyramidTransformKernel (
877     const SmartPtr<CLContext> &context,
878     SmartPtr<CLPyramidBlender> &blender,
879     uint32_t layer,
880     uint32_t buf_index,
881     bool is_uv)
882     : CLImageKernel (context)
883     , _blender (blender)
884     , _layer (layer)
885     , _buf_index (buf_index)
886     , _is_uv (is_uv)
887 {
888     XCAM_ASSERT (layer <= XCAM_CL_PYRAMID_MAX_LEVEL);
889     XCAM_ASSERT (buf_index <= XCAM_BLENDER_IMAGE_NUM);
890 }
891 
892 static bool
change_image_format(SmartPtr<CLContext> context,SmartPtr<CLImage> input,SmartPtr<CLImage> & output,const CLImageDesc & new_desc)893 change_image_format (
894     SmartPtr<CLContext> context, SmartPtr<CLImage> input,
895     SmartPtr<CLImage> &output, const CLImageDesc &new_desc)
896 {
897     SmartPtr<CLImage2D> previous = input.dynamic_cast_ptr<CLImage2D> ();
898     if (!previous.ptr () || !previous->get_bind_buf ().ptr ())
899         return false;
900 
901     SmartPtr<CLBuffer> bind_buf = previous->get_bind_buf ();
902     output = new CLImage2D (context, new_desc, 0, bind_buf);
903     if (!output.ptr ())
904         return false;
905     return true;
906 }
907 
908 int32_t
get_input_gauss_offset_x()909 CLPyramidTransformKernel::get_input_gauss_offset_x ()
910 {
911     const PyramidLayer &layer = _blender->get_pyramid_layer (_layer);
912     uint32_t plane_index = (_is_uv ? 1 : 0);
913     return layer.gauss_offset_x[plane_index][_buf_index];
914 }
915 
916 XCamReturn
prepare_arguments(CLArgList & args,CLWorkSize & work_size)917 CLPyramidTransformKernel::prepare_arguments (CLArgList &args, CLWorkSize &work_size)
918 {
919     SmartPtr<CLContext> context = get_context ();
920 
921     SmartPtr<CLImage> image_in_gauss = get_input_gauss();
922     SmartPtr<CLImage> image_out_gauss = get_output_gauss();
923     //SmartPtr<CLImage> image_out_lap = get_output_lap ();
924     const CLImageDesc &cl_desc_out_gauss_pre = image_out_gauss->get_image_desc ();
925 
926     CLImageDesc cl_desc_out_gauss;
927     cl_desc_out_gauss.format.image_channel_data_type = CL_UNSIGNED_INT8;
928     cl_desc_out_gauss.format.image_channel_order = CL_RGBA;
929     cl_desc_out_gauss.width = cl_desc_out_gauss_pre.width * 2;
930     cl_desc_out_gauss.height = cl_desc_out_gauss_pre.height;
931     cl_desc_out_gauss.row_pitch = cl_desc_out_gauss_pre.row_pitch;
932     SmartPtr<CLImage> format_image_out;
933     change_image_format (context, image_out_gauss, format_image_out, cl_desc_out_gauss);
934     XCAM_FAIL_RETURN (
935         ERROR,
936         format_image_out.ptr () && format_image_out->is_valid (),
937         XCAM_RETURN_ERROR_CL,
938         "CLPyramidTransformKernel change output gauss image format failed");
939 
940     int gauss_offset_x = get_input_gauss_offset_x () / 8;
941     XCAM_ASSERT (gauss_offset_x * 8 == get_input_gauss_offset_x ());
942 
943     args.push_back (new CLMemArgument (image_in_gauss));
944     args.push_back (new CLArgumentT<int> (gauss_offset_x));
945     args.push_back (new CLMemArgument (format_image_out));
946 
947 #if CL_PYRAMID_ENABLE_DUMP
948     int plane = _is_uv ? 1 : 0;
949     SmartPtr<CLImage> dump_original = _blender->get_pyramid_layer (_layer).dump_original[plane][_buf_index];
950 
951     args.push_back (new CLMemArgument (dump_original));
952 
953     printf ("L%dI%d: gauss_offset_x:%d \n", _layer, _buf_index, gauss_offset_x);
954 #endif
955 
956     const int workitem_lines = 2;
957     int gloabal_y = XCAM_ALIGN_UP (cl_desc_out_gauss.height, workitem_lines) / workitem_lines;
958     work_size.dim = XCAM_DEFAULT_IMAGE_DIM;
959     work_size.local[0] = 16;
960     work_size.local[1] = 4;
961     work_size.global[0] = XCAM_ALIGN_UP (cl_desc_out_gauss.width, work_size.local[0]);
962     work_size.global[1] = XCAM_ALIGN_UP (gloabal_y, work_size.local[1]);
963 
964     return XCAM_RETURN_NO_ERROR;
965 }
966 
CLSeamDiffKernel(const SmartPtr<CLContext> & context,SmartPtr<CLPyramidBlender> & blender)967 CLSeamDiffKernel::CLSeamDiffKernel (
968     const SmartPtr<CLContext> &context, SmartPtr<CLPyramidBlender> &blender)
969     : CLImageKernel (context)
970     , _blender (blender)
971 {
972 }
973 
974 XCamReturn
prepare_arguments(CLArgList & args,CLWorkSize & work_size)975 CLSeamDiffKernel::prepare_arguments (CLArgList &args, CLWorkSize &work_size)
976 {
977     const PyramidLayer &layer0 = _blender->get_pyramid_layer (0);
978     SmartPtr<CLImage> image0 = layer0.gauss_image[CLBlenderPlaneY][0];
979     SmartPtr<CLImage> image1 = layer0.gauss_image[CLBlenderPlaneY][1];
980     SmartPtr<CLImage> out_diff = _blender->get_image_diff ();
981     CLImageDesc out_diff_desc = out_diff->get_image_desc ();
982 
983     int image_offset_x[XCAM_BLENDER_IMAGE_NUM];
984 
985     for (uint32_t i = 0; i < XCAM_BLENDER_IMAGE_NUM; ++i) {
986         image_offset_x[i] = layer0.gauss_offset_x[CLBlenderPlaneY][i] / 8;
987     }
988 
989     args.push_back (new CLMemArgument (image0));
990     args.push_back (new CLArgumentT<int> (image_offset_x[0]));
991     args.push_back (new CLMemArgument (image1));
992     args.push_back (new CLArgumentT<int> (image_offset_x[1]));
993     args.push_back (new CLMemArgument (out_diff));
994 
995     work_size.dim = XCAM_DEFAULT_IMAGE_DIM;
996     work_size.local[0] = 8;
997     work_size.local[1] = 4;
998     work_size.global[0] = XCAM_ALIGN_UP (out_diff_desc.width, work_size.local[0]);
999     work_size.global[1] = XCAM_ALIGN_UP (out_diff_desc.height, work_size.local[1]);
1000 
1001     return XCAM_RETURN_NO_ERROR;
1002 }
1003 
CLSeamDPKernel(const SmartPtr<CLContext> & context,SmartPtr<CLPyramidBlender> & blender)1004 CLSeamDPKernel::CLSeamDPKernel (
1005     const SmartPtr<CLContext> &context, SmartPtr<CLPyramidBlender> &blender)
1006     : CLImageKernel (context)
1007     , _blender (blender)
1008 {
1009 }
1010 
1011 XCamReturn
prepare_arguments(CLArgList & args,CLWorkSize & work_size)1012 CLSeamDPKernel::prepare_arguments (CLArgList &args, CLWorkSize &work_size)
1013 {
1014 #define ELEMENT_PIXEL 1
1015 
1016     uint32_t width, height, stride;
1017     uint32_t pos_offset_x, pos_valid_width;
1018     _blender->get_seam_info (width, height, stride);
1019     _blender->get_seam_pos_info (pos_offset_x, pos_valid_width);
1020     int seam_height = (int)height;
1021     int seam_stride = (int)stride / ELEMENT_PIXEL;
1022     int seam_offset_x = (int)pos_offset_x / ELEMENT_PIXEL; // ushort8
1023     int seam_valid_with = (int)pos_valid_width / ELEMENT_PIXEL;
1024     int max_pos = (int)(pos_offset_x + pos_valid_width - 1);
1025 
1026     SmartPtr<CLImage> image = _blender->get_image_diff ();
1027     SmartPtr<CLBuffer> pos_buf = _blender->get_seam_pos_buf ();
1028     SmartPtr<CLBuffer> sum_buf = _blender->get_seam_sum_buf ();
1029     XCAM_ASSERT (image.ptr () && pos_buf.ptr () && sum_buf.ptr ());
1030 
1031     CLImageDesc cl_orig = image->get_image_desc ();
1032     CLImageDesc cl_desc_convert;
1033     cl_desc_convert.format.image_channel_data_type = CL_UNSIGNED_INT8;
1034     cl_desc_convert.format.image_channel_order = CL_R;
1035     cl_desc_convert.width = cl_orig.width * (8 / ELEMENT_PIXEL);
1036     cl_desc_convert.height = cl_orig.height;
1037     cl_desc_convert.row_pitch = cl_orig.row_pitch;
1038 
1039     SmartPtr<CLImage> convert_image;
1040     change_image_format (get_context (), image, convert_image, cl_desc_convert);
1041     XCAM_ASSERT (convert_image.ptr () && convert_image->is_valid ());
1042 
1043     args.push_back (new CLMemArgument (convert_image));
1044     args.push_back (new CLMemArgument (pos_buf));
1045     args.push_back (new CLMemArgument (sum_buf));
1046     args.push_back (new CLArgumentT<int> (seam_offset_x));
1047     args.push_back (new CLArgumentT<int> (seam_valid_with));
1048     args.push_back (new CLArgumentT<int> (max_pos));
1049     args.push_back (new CLArgumentT<int> (seam_height));
1050     args.push_back (new CLArgumentT<int> (seam_stride));
1051 
1052     work_size.dim = 1;
1053     work_size.local[0] = XCAM_ALIGN_UP(seam_valid_with, 16);
1054     work_size.global[0] = work_size.local[0] * 2;
1055 
1056     return XCAM_RETURN_NO_ERROR;
1057 }
1058 
CLPyramidSeamMaskKernel(const SmartPtr<CLContext> & context,SmartPtr<CLPyramidBlender> & blender,uint32_t layer,bool scale,bool need_slm)1059 CLPyramidSeamMaskKernel::CLPyramidSeamMaskKernel (
1060     const SmartPtr<CLContext> &context, SmartPtr<CLPyramidBlender> &blender,
1061     uint32_t layer, bool scale, bool need_slm)
1062     : CLImageKernel (context)
1063     , _blender (blender)
1064     , _layer (layer)
1065     , _need_scale (scale)
1066     , _need_slm (need_slm)
1067 {
1068     XCAM_ASSERT (layer < blender->get_layers ());
1069 }
1070 
1071 XCamReturn
prepare_arguments(CLArgList & args,CLWorkSize & work_size)1072 CLPyramidSeamMaskKernel::prepare_arguments (CLArgList &args, CLWorkSize &work_size)
1073 {
1074     XCamReturn ret = XCAM_RETURN_NO_ERROR;
1075     ret = _blender->fill_seam_mask ();
1076     XCAM_FAIL_RETURN (ERROR, ret == XCAM_RETURN_NO_ERROR, ret, "CLPyramidSeamMaskKernel fill seam mask failed");
1077 
1078     SmartPtr<CLContext> context = get_context ();
1079     const PyramidLayer &cur_layer = _blender->get_pyramid_layer (_layer);
1080     SmartPtr<CLImage> input_image = cur_layer.seam_mask[CLSeamMaskTmp];
1081     SmartPtr<CLImage> out_gauss = cur_layer.seam_mask[CLSeamMaskCoeff];
1082     CLImageDesc out_gauss_desc = out_gauss->get_image_desc ();
1083 
1084     XCAM_ASSERT (input_image.ptr () && out_gauss.ptr ());
1085     XCAM_ASSERT (input_image->is_valid () && out_gauss->is_valid ());
1086 
1087     args.push_back (new CLMemArgument (input_image));
1088     args.push_back (new CLMemArgument (out_gauss));
1089 
1090 
1091 
1092     if (_need_slm) {
1093         int image_width = out_gauss_desc.width;
1094         args.push_back (new CLArgumentT<int> (image_width));
1095     }
1096 
1097     if (_need_scale) {
1098         const PyramidLayer &next_layer = _blender->get_pyramid_layer (_layer + 1);
1099         SmartPtr<CLImage> out_orig = next_layer.seam_mask[CLSeamMaskTmp];
1100         CLImageDesc input_desc, output_desc;
1101         input_desc = out_orig->get_image_desc ();
1102         output_desc.format.image_channel_data_type = CL_UNSIGNED_INT8;
1103         output_desc.format.image_channel_order = CL_RGBA;
1104         output_desc.width = input_desc.width * 2;
1105         output_desc.height = input_desc.height;
1106         output_desc.row_pitch = input_desc.row_pitch;
1107 
1108         SmartPtr<CLImage> output_scale_image;
1109         change_image_format (context, out_orig, output_scale_image, output_desc);
1110         args.push_back (new CLMemArgument (output_scale_image));
1111     }
1112 
1113     uint32_t workitem_height = XCAM_ALIGN_UP (out_gauss_desc.height, 2) / 2;
1114 
1115     work_size.dim = XCAM_DEFAULT_IMAGE_DIM;
1116 
1117     if (_need_slm) {
1118         work_size.local[0] = XCAM_ALIGN_UP (out_gauss_desc.width, 16);
1119         work_size.local[1] = 1;
1120         work_size.global[0] = work_size.local[0];
1121         work_size.global[1] = workitem_height;
1122     } else {
1123         work_size.local[0] = 8;
1124         work_size.local[1] = 4;
1125         work_size.global[0] = XCAM_ALIGN_UP (out_gauss_desc.width, work_size.local[0]);
1126         work_size.global[1] = XCAM_ALIGN_UP (workitem_height, work_size.local[1]);
1127     }
1128 
1129     return XCAM_RETURN_NO_ERROR;
1130 }
1131 
CLPyramidLapKernel(const SmartPtr<CLContext> & context,SmartPtr<CLPyramidBlender> & blender,uint32_t layer,uint32_t buf_index,bool is_uv)1132 CLPyramidLapKernel::CLPyramidLapKernel (
1133     const SmartPtr<CLContext> &context,
1134     SmartPtr<CLPyramidBlender> &blender,
1135     uint32_t layer,
1136     uint32_t buf_index,
1137     bool is_uv)
1138     : CLImageKernel (context)
1139     , _blender (blender)
1140     , _layer (layer)
1141     , _buf_index (buf_index)
1142     , _is_uv (is_uv)
1143 {
1144     XCAM_ASSERT (layer <= XCAM_CL_PYRAMID_MAX_LEVEL);
1145     XCAM_ASSERT (buf_index <= XCAM_BLENDER_IMAGE_NUM);
1146 }
1147 
1148 int32_t
get_cur_gauss_offset_x()1149 CLPyramidLapKernel::get_cur_gauss_offset_x ()
1150 {
1151     const PyramidLayer &layer = _blender->get_pyramid_layer (_layer);
1152     uint32_t plane_index = (_is_uv ? 1 : 0);
1153     return layer.gauss_offset_x[plane_index][_buf_index];
1154 }
1155 
1156 int32_t
get_output_lap_offset_x()1157 CLPyramidLapKernel::get_output_lap_offset_x ()
1158 {
1159     const PyramidLayer &layer = _blender->get_pyramid_layer (_layer);
1160     uint32_t plane_index = (_is_uv ? 1 : 0);
1161     return layer.lap_offset_x[plane_index][_buf_index];
1162 }
1163 
1164 XCamReturn
prepare_arguments(CLArgList & args,CLWorkSize & work_size)1165 CLPyramidLapKernel::prepare_arguments (CLArgList &args, CLWorkSize &work_size)
1166 {
1167     SmartPtr<CLContext> context = get_context ();
1168 
1169     SmartPtr<CLImage> cur_gauss_image = get_current_gauss();
1170     SmartPtr<CLImage> next_gauss_image_tmp = get_next_gauss();
1171     SmartPtr<CLImage> image_out_lap = get_output_lap ();
1172     const CLImageDesc &cl_desc_next_gauss_tmp = next_gauss_image_tmp->get_image_desc ();
1173     const CLImageDesc &cl_desc_out_lap = image_out_lap->get_image_desc ();
1174     float next_gauss_pixel_width = 0.0f, next_gauss_pixel_height = 0.0f;
1175 
1176     CLImageDesc cl_desc_next_gauss;
1177     if (!_is_uv) {
1178         cl_desc_next_gauss.format.image_channel_data_type = CL_UNORM_INT8;
1179         cl_desc_next_gauss.format.image_channel_order = CL_R;
1180         cl_desc_next_gauss.width = cl_desc_next_gauss_tmp.width * 8;
1181     } else {
1182         cl_desc_next_gauss.format.image_channel_data_type = CL_UNORM_INT8;
1183         cl_desc_next_gauss.format.image_channel_order = CL_RG;
1184         cl_desc_next_gauss.width = cl_desc_next_gauss_tmp.width * 4;
1185     }
1186     cl_desc_next_gauss.height = cl_desc_next_gauss_tmp.height;
1187     cl_desc_next_gauss.row_pitch = cl_desc_next_gauss_tmp.row_pitch;
1188     SmartPtr<CLImage> next_gauss;
1189     change_image_format (context, next_gauss_image_tmp, next_gauss, cl_desc_next_gauss);
1190     XCAM_FAIL_RETURN (
1191         ERROR,
1192         next_gauss.ptr () && next_gauss->is_valid (),
1193         XCAM_RETURN_ERROR_CL,
1194         "CLPyramidTransformKernel change output gauss image format failed");
1195 
1196     next_gauss_pixel_width = cl_desc_next_gauss.width;
1197     next_gauss_pixel_height = cl_desc_next_gauss.height;
1198 
1199     // out format(current layer): CL_UNSIGNED_INT16 + CL_RGBA
1200     float out_width = CLImage::calculate_pixel_bytes (cl_desc_next_gauss.format) * cl_desc_next_gauss.width * 2.0f / 8.0f;
1201     float out_height = next_gauss_pixel_height * 2.0f;
1202     float sampler_offset_x = SAMPLER_POSITION_OFFSET / next_gauss_pixel_width;
1203     float sampler_offset_y = SAMPLER_POSITION_OFFSET / next_gauss_pixel_height;
1204 
1205     int cur_gauss_offset_x = get_cur_gauss_offset_x () / 8;
1206     XCAM_ASSERT (cur_gauss_offset_x * 8 == get_cur_gauss_offset_x ());
1207     int lap_offset_x = get_output_lap_offset_x () / 8;
1208     XCAM_ASSERT (lap_offset_x * 8 == get_output_lap_offset_x ());
1209 
1210     args.push_back (new CLMemArgument (cur_gauss_image));
1211     args.push_back (new CLArgumentT<int> (cur_gauss_offset_x));
1212     args.push_back (new CLMemArgument (next_gauss));
1213     args.push_back (new CLArgumentT<float> (sampler_offset_x));
1214     args.push_back (new CLArgumentT<float> (sampler_offset_y));
1215     args.push_back (new CLMemArgument (image_out_lap));
1216     args.push_back (new CLArgumentT<int> (lap_offset_x));
1217     args.push_back (new CLArgumentT<float> (out_width));
1218     args.push_back (new CLArgumentT<float> (out_height));
1219 
1220     work_size.dim = XCAM_DEFAULT_IMAGE_DIM;
1221     work_size.local[0] = 8;
1222     work_size.local[1] = 4;
1223     work_size.global[0] = XCAM_ALIGN_UP (cl_desc_out_lap.width, work_size.local[0]);
1224     work_size.global[1] = XCAM_ALIGN_UP (cl_desc_out_lap.height, work_size.local[1]);
1225 
1226     return XCAM_RETURN_NO_ERROR;
1227 }
1228 
CLPyramidReconstructKernel(const SmartPtr<CLContext> & context,SmartPtr<CLPyramidBlender> & blender,uint32_t layer,bool is_uv)1229 CLPyramidReconstructKernel::CLPyramidReconstructKernel (
1230     const SmartPtr<CLContext> &context, SmartPtr<CLPyramidBlender> &blender,
1231     uint32_t layer, bool is_uv)
1232     : CLImageKernel (context)
1233     , _blender (blender)
1234     , _layer (layer)
1235     , _is_uv (is_uv)
1236 {
1237     XCAM_ASSERT (layer <= XCAM_CL_PYRAMID_MAX_LEVEL);
1238 }
1239 
1240 int
get_output_reconstrcut_offset_x()1241 CLPyramidReconstructKernel::get_output_reconstrcut_offset_x ()
1242 {
1243     if (_layer > 0)
1244         return 0;
1245     const Rect & window = _blender->get_merge_window ();
1246     XCAM_ASSERT (window.pos_x % XCAM_CL_BLENDER_ALIGNMENT_X == 0);
1247     return window.pos_x;
1248 }
1249 
1250 XCamReturn
prepare_arguments(CLArgList & args,CLWorkSize & work_size)1251 CLPyramidReconstructKernel::prepare_arguments (CLArgList &args, CLWorkSize &work_size)
1252 {
1253     SmartPtr<CLContext> context = get_context ();
1254 
1255     SmartPtr<CLImage> image_in_reconst = get_input_reconstruct();
1256     SmartPtr<CLImage> image_in_lap = get_input_lap ();
1257     SmartPtr<CLImage> image_out_reconst = get_output_reconstruct();
1258     const CLImageDesc &cl_desc_in_reconst_pre = image_in_reconst->get_image_desc ();
1259     // out_desc should be same as image_in_lap
1260     const CLImageDesc &cl_desc_out_reconst = image_in_lap->get_image_desc (); // don't change
1261     float input_gauss_width = 0.0f, input_gauss_height = 0.0f;
1262 
1263     CLImageDesc cl_desc_in_reconst;
1264     cl_desc_in_reconst.format.image_channel_data_type = CL_UNORM_INT8;
1265     if (_is_uv) {
1266         cl_desc_in_reconst.format.image_channel_order = CL_RG;
1267         cl_desc_in_reconst.width = cl_desc_in_reconst_pre.width * 4;
1268     } else {
1269         cl_desc_in_reconst.format.image_channel_order = CL_R;
1270         cl_desc_in_reconst.width = cl_desc_in_reconst_pre.width * 8;
1271     }
1272     cl_desc_in_reconst.height = cl_desc_in_reconst_pre.height;
1273     cl_desc_in_reconst.row_pitch = cl_desc_in_reconst_pre.row_pitch;
1274     SmartPtr<CLImage> input_reconstruct;
1275     change_image_format (context, image_in_reconst, input_reconstruct, cl_desc_in_reconst);
1276     XCAM_FAIL_RETURN (
1277         ERROR,
1278         input_reconstruct.ptr () && input_reconstruct->is_valid (),
1279         XCAM_RETURN_ERROR_CL,
1280         "CLPyramidTransformKernel change output gauss image format failed");
1281 
1282     input_gauss_width = cl_desc_in_reconst.width;
1283     input_gauss_height = cl_desc_in_reconst.height;
1284 
1285     float out_reconstruct_width = CLImage::calculate_pixel_bytes (cl_desc_in_reconst.format) * cl_desc_in_reconst.width * 2.0f / 8.0f;
1286     float out_reconstruct_height = input_gauss_height * 2.0f;
1287     float in_sampler_offset_x = SAMPLER_POSITION_OFFSET / input_gauss_width;
1288     float in_sampler_offset_y = SAMPLER_POSITION_OFFSET / input_gauss_height;
1289     int out_reconstruct_offset_x = 0;
1290 
1291     if (_blender->get_scale_mode () == CLBlenderScaleLocal) {
1292         out_reconstruct_offset_x = 0;
1293     } else {
1294         out_reconstruct_offset_x = get_output_reconstrcut_offset_x () / 8;
1295         XCAM_ASSERT (out_reconstruct_offset_x * 8 == get_output_reconstrcut_offset_x ());
1296     }
1297 
1298     args.push_back (new CLMemArgument (input_reconstruct));
1299     args.push_back (new CLArgumentT<float> (in_sampler_offset_x));
1300     args.push_back (new CLArgumentT<float> (in_sampler_offset_y));
1301     args.push_back (new CLMemArgument (image_in_lap));
1302     args.push_back (new CLMemArgument (image_out_reconst));
1303     args.push_back (new CLArgumentT<int> (out_reconstruct_offset_x));
1304     args.push_back (new CLArgumentT<float> (out_reconstruct_width));
1305     args.push_back (new CLArgumentT<float> (out_reconstruct_height));
1306 
1307 #if CL_PYRAMID_ENABLE_DUMP
1308     int i_plane = (_is_uv ? 1 : 0);
1309     const PyramidLayer &cur_layer = _blender->get_pyramid_layer (_layer);
1310     SmartPtr<CLImage>  dump_gauss_resize = cur_layer.dump_gauss_resize[i_plane];
1311     SmartPtr<CLImage>  dump_final = cur_layer.dump_final[i_plane];
1312 
1313     args.push_back (new CLMemArgument (dump_gauss_resize));
1314     args.push_back (new CLMemArgument (dump_final));
1315 
1316     printf ("Rec%d: reconstruct_offset_x:%d, out_width:%.2f, out_height:%.2f, in_sampler_offset_x:%.2f, in_sampler_offset_y:%.2f\n",
1317             _layer, out_reconstruct_offset_x, out_reconstruct_width, out_reconstruct_height,
1318             in_sampler_offset_x, in_sampler_offset_y);
1319 #endif
1320 
1321     work_size.dim = XCAM_DEFAULT_IMAGE_DIM;
1322     work_size.local[0] = 4;
1323     work_size.local[1] = 8;
1324     work_size.global[0] = XCAM_ALIGN_UP (cl_desc_out_reconst.width, work_size.local[0]);
1325     work_size.global[1] = XCAM_ALIGN_UP (cl_desc_out_reconst.height, work_size.local[1]);
1326 
1327     return XCAM_RETURN_NO_ERROR;
1328 }
1329 
1330 
1331 void
dump_buffers()1332 CLPyramidBlender::dump_buffers ()
1333 {
1334     static int frame_count = 0;
1335     SmartPtr<CLImage> image;
1336     ++frame_count;
1337 
1338     // dump difference between original image and final image
1339 #if 0
1340 #define CM_NUM 3
1341     SmartPtr<CLImage> images[CM_NUM];
1342     const Rect & window = get_merge_window ();
1343     int offsets[3] = {window.pos_x, window.pos_x, 0};
1344     //right edge
1345     //int offsets[3] = {0 + window.width - 8, window.pos_x + window.width - 8, window.width - 8};
1346     size_t row_pitch[CM_NUM];
1347     size_t slice_pitch[CM_NUM];
1348     uint8_t *ptr[CM_NUM] = {NULL, NULL, NULL};
1349     uint32_t i = 0;
1350 
1351 #if 1
1352     // Y
1353     // left edge
1354     images[0] = this->get_pyramid_layer (0).gauss_image[0][0];
1355     // right edge
1356     //images[0] = this->get_pyramid_layer (0).gauss_image[0][1];
1357     images[1] = this->get_pyramid_layer (0).blend_image[0][ReconstructImageIndex];
1358     images[2] = this->get_pyramid_layer (0).dump_final[0];
1359 #else
1360     // UV
1361     // left edge
1362     images[0] = this->get_pyramid_layer (0).gauss_image[1][0];
1363     // right edge
1364     //images[0] = this->get_pyramid_layer (0).gauss_image[1][1];
1365     images[1] = this->get_pyramid_layer (0).blend_image[1][ReconstructImageIndex];
1366     images[2] = this->get_pyramid_layer (0).dump_final[1];
1367 #endif
1368 
1369     for (i = 0; i < CM_NUM; ++i) {
1370         const CLImageDesc &desc = images[i]->get_image_desc ();
1371         size_t origin[3] = {0, 0, 0};
1372         size_t region[3] = {desc.width, desc.height, 1};
1373         XCamReturn ret = images[i]->enqueue_map ((void *&)ptr[i], origin, region, &row_pitch[i], &slice_pitch[i], CL_MAP_READ);
1374         XCAM_ASSERT (ret == XCAM_RETURN_NO_ERROR);
1375     }
1376     // offset UV, workaround of beignet
1377     //offsets[0] += row_pitch[0] * 1088;
1378     //offsets[1] += row_pitch[1] * 1088;
1379 
1380     printf ("layer 0(UV) comparison, original / final-image / reconstruct offset:%d, width:%d\n", window.pos_x, window.width);
1381     for (int ih = 250; ih < 280; ++ih) {
1382         uint8_t *lines[CM_NUM];
1383         for (i = 0; i < 2 /*CM_NUM*/; ++i) {
1384             uint8_t *l = (uint8_t *)ptr[i] + offsets[i] + row_pitch[i] * ih + 0;
1385             lines[i] = l;
1386             printf ("%02x%02x%02x%02x%02x%02x%02x%02x ", l[0], l[1], l[2], l[3], l[4], l[5], l[6], l[7]);
1387         }
1388         //printf differrence between original and final image
1389         printf ("delta(orig - final):");
1390         for (i = 0; i < 10; ++i) {
1391             printf ("%02x", (uint32_t)(lines[0][i] - lines[1][i]) & 0xFF);
1392         }
1393         printf ("\n");
1394     }
1395 
1396     for (i = 0; i < CM_NUM; ++i) {
1397         images[i]->enqueue_unmap (ptr[i]);
1398     }
1399 #endif
1400 
1401 #define DUMP_IMAGE(prefix, image, layer)        \
1402     desc = (image)->get_image_desc ();   \
1403     snprintf (filename, sizeof(filename), prefix "_L%d-%dx%d",            \
1404               layer, (image)->get_pixel_bytes () * desc.width, desc.height); \
1405     dump_image (image, filename)
1406 
1407     // dump image data to file
1408     CLImageDesc desc;
1409     char filename[1024];
1410 
1411     image = this->get_image_diff ();
1412     if (image.ptr ()) {
1413         DUMP_IMAGE ("dump_image_diff", image, 0);
1414     }
1415 
1416     for (uint32_t i_layer = 0; i_layer < get_layers (); ++i_layer) {
1417         //dump seam mask
1418         image = this->get_pyramid_layer(i_layer).seam_mask[CLSeamMaskTmp];
1419         if (image.ptr ()) {
1420             DUMP_IMAGE ("dump_seam_tmp", image, i_layer);
1421         }
1422 
1423         image = this->get_pyramid_layer(i_layer).seam_mask[CLSeamMaskCoeff];
1424         if (image.ptr ()) {
1425             DUMP_IMAGE ("dump_seam_coeff", image, i_layer);
1426         }
1427 
1428         image = this->get_blend_image (i_layer, false); // layer 1
1429         DUMP_IMAGE ("dump_blend", image, i_layer);
1430 
1431         if (i_layer > 0) { //layer : [1, _layers -1]
1432             image = this->get_gauss_image (i_layer, 0, false);
1433             DUMP_IMAGE ("dump_gaussI0", image, i_layer);
1434             image = this->get_gauss_image (i_layer, 1, false);
1435             DUMP_IMAGE ("dump_gaussI1", image, i_layer);
1436         }
1437 
1438         if (i_layer < get_layers () - 1) {
1439             image = this->get_lap_image (i_layer, 0, false); // layer : [0, _layers -2]
1440             DUMP_IMAGE ("dump_lap_I0", image, i_layer);
1441         }
1442     }
1443 
1444 #if CL_PYRAMID_ENABLE_DUMP
1445     image = this->get_pyramid_layer (0).dump_gauss_resize[0];
1446     DUMP_IMAGE ("dump_gauss_resize", image, 0);
1447 
1448     image = this->get_pyramid_layer (0).dump_original[0][0];
1449     DUMP_IMAGE ("dump_orginalI0", image, 0);
1450     image = this->get_pyramid_layer (0).dump_original[0][1];
1451     DUMP_IMAGE ("dump_orginalI1", image, 0);
1452 
1453     image = this->get_pyramid_layer (0).dump_final[CLBlenderPlaneY];
1454     DUMP_IMAGE ("dump_final", image, 0);
1455 #endif
1456 
1457 #if 0
1458     this->dump_layer_mask (0, false);
1459     this->dump_layer_mask (1, false);
1460 
1461     //this->dump_layer_mask (0, true);
1462     //this->dump_layer_mask (1, true);
1463 #endif
1464 
1465 }
1466 
CLBlenderLocalScaleKernel(const SmartPtr<CLContext> & context,SmartPtr<CLPyramidBlender> & blender,bool is_uv)1467 CLBlenderLocalScaleKernel::CLBlenderLocalScaleKernel (
1468     const SmartPtr<CLContext> &context, SmartPtr<CLPyramidBlender> &blender, bool is_uv)
1469     : CLBlenderScaleKernel (context, is_uv)
1470     , _blender (blender)
1471 {
1472 }
1473 
1474 SmartPtr<CLImage>
get_input_image()1475 CLBlenderLocalScaleKernel::get_input_image ()
1476 {
1477     SmartPtr<CLContext> context = get_context ();
1478 
1479     SmartPtr<CLImage> rec_image = _blender->get_reconstruct_image (0, _is_uv);
1480     const CLImageDesc &rec_desc = rec_image->get_image_desc ();
1481 
1482     CLImageDesc new_desc;
1483     new_desc.format.image_channel_data_type = CL_UNORM_INT8;
1484     if (_is_uv) {
1485         new_desc.format.image_channel_order = CL_RG;
1486         new_desc.width = rec_desc.width * 4;
1487     } else {
1488         new_desc.format.image_channel_order = CL_R;
1489         new_desc.width = rec_desc.width * 8;
1490     }
1491     new_desc.height = rec_desc.height;
1492     new_desc.row_pitch = rec_desc.row_pitch;
1493     SmartPtr<CLImage> new_image;
1494     change_image_format (context, rec_image, new_image, new_desc);
1495     XCAM_FAIL_RETURN (
1496         ERROR,
1497         new_image.ptr () && new_image->is_valid (),
1498         NULL,
1499         "CLBlenderLocalScaleKernel change image format failed");
1500 
1501     _image_in = new_image;
1502     return new_image;
1503 }
1504 
1505 SmartPtr<CLImage>
get_output_image()1506 CLBlenderLocalScaleKernel::get_output_image ()
1507 {
1508     return _blender->get_scale_image (_is_uv);
1509 }
1510 
1511 bool
get_output_info(uint32_t & out_width,uint32_t & out_height,int & out_offset_x)1512 CLBlenderLocalScaleKernel::get_output_info (
1513     uint32_t &out_width, uint32_t &out_height, int &out_offset_x)
1514 {
1515     XCAM_ASSERT (_image_in.ptr ());
1516 
1517     const Rect &window = _blender->get_merge_window ();
1518     const CLImageDesc &desc_in = _image_in->get_image_desc ();
1519 
1520     out_width = window.width / 8;
1521     out_height = desc_in.height;
1522     out_offset_x = window.pos_x / 8;
1523 
1524     XCAM_FAIL_RETURN (ERROR, out_width != 0, false, "get output info failed");
1525     return true;
1526 }
1527 
CLPyramidCopyKernel(const SmartPtr<CLContext> & context,SmartPtr<CLPyramidBlender> & blender,uint32_t buf_index,bool is_uv)1528 CLPyramidCopyKernel::CLPyramidCopyKernel (
1529     const SmartPtr<CLContext> &context, SmartPtr<CLPyramidBlender> &blender,
1530     uint32_t buf_index, bool is_uv)
1531     : CLImageKernel (context)
1532     , _blender (blender)
1533     , _is_uv (is_uv)
1534     , _buf_index (buf_index)
1535 {
1536 }
1537 
1538 XCamReturn
prepare_arguments(CLArgList & args,CLWorkSize & work_size)1539 CLPyramidCopyKernel::prepare_arguments (CLArgList &args, CLWorkSize &work_size)
1540 {
1541     SmartPtr<CLContext> context = get_context ();
1542 
1543     SmartPtr<CLImage> from = get_input ();
1544     SmartPtr<CLImage> to = get_output ();
1545 
1546     const CLImageDesc &to_desc = to->get_image_desc ();
1547     const Rect &window = _blender->get_merge_window ();
1548     const Rect &input_area = _blender->get_input_valid_area (_buf_index);
1549     const Rect &merge_area = _blender->get_input_merge_area (_buf_index);
1550     int in_offset_x = 0;
1551     int out_offset_x = 0;
1552     int max_g_x = 0, max_g_y = 0;
1553 
1554     if (_buf_index == 0) {
1555         in_offset_x = input_area.pos_x / 8;
1556         max_g_x = (merge_area.pos_x - input_area.pos_x) / 8;
1557         out_offset_x = window.pos_x / 8 - max_g_x;
1558     } else {
1559         in_offset_x = (merge_area.pos_x + merge_area.width) / 8;
1560         out_offset_x = (window.pos_x + window.width) / 8;
1561         max_g_x = (input_area.pos_x + input_area.width) / 8 - in_offset_x;
1562     }
1563     max_g_y = to_desc.height;
1564     XCAM_ASSERT (max_g_x > 0 && max_g_x <= (int)to_desc.width);
1565 
1566 #if CL_PYRAMID_ENABLE_DUMP
1567     printf ("copy(%d), in_offset_x:%d, out_offset_x:%d, max_x:%d\n", _buf_index, in_offset_x, out_offset_x, max_g_x);
1568 #endif
1569 
1570     args.push_back (new CLMemArgument (from));
1571     args.push_back (new CLArgumentT<int> (in_offset_x));
1572     args.push_back (new CLMemArgument (to));
1573     args.push_back (new CLArgumentT<int> (out_offset_x));
1574     args.push_back (new CLArgumentT<int> (max_g_x));
1575     args.push_back (new CLArgumentT<int> (max_g_y));
1576 
1577     work_size.dim = XCAM_DEFAULT_IMAGE_DIM;
1578     work_size.local[0] = 16;
1579     work_size.local[1] = 4;
1580     work_size.global[0] = XCAM_ALIGN_UP (max_g_x, work_size.local[0]);
1581     work_size.global[1] = XCAM_ALIGN_UP (max_g_y, work_size.local[1]);
1582 
1583     return XCAM_RETURN_NO_ERROR;
1584 }
1585 
1586 static SmartPtr<CLImageKernel>
create_pyramid_transform_kernel(const SmartPtr<CLContext> & context,SmartPtr<CLPyramidBlender> & blender,uint32_t layer,uint32_t buf_index,bool is_uv)1587 create_pyramid_transform_kernel (
1588     const SmartPtr<CLContext> &context, SmartPtr<CLPyramidBlender> &blender,
1589     uint32_t layer, uint32_t buf_index, bool is_uv)
1590 {
1591     char transform_option[1024];
1592     snprintf (
1593         transform_option, sizeof(transform_option),
1594         "-DPYRAMID_UV=%d -DCL_PYRAMID_ENABLE_DUMP=%d", (is_uv ? 1 : 0), CL_PYRAMID_ENABLE_DUMP);
1595 
1596     SmartPtr<CLImageKernel> kernel;
1597     kernel = new CLPyramidTransformKernel (context, blender, layer, buf_index, is_uv);
1598     XCAM_ASSERT (kernel.ptr ());
1599     XCAM_FAIL_RETURN (
1600         ERROR,
1601         kernel->build_kernel (kernels_info[KernelPyramidTransform], transform_option) == XCAM_RETURN_NO_ERROR,
1602         NULL,
1603         "load pyramid blender kernel(%s) failed", (is_uv ? "UV" : "Y"));
1604     return kernel;
1605 }
1606 
1607 static SmartPtr<CLImageKernel>
create_pyramid_lap_kernel(const SmartPtr<CLContext> & context,SmartPtr<CLPyramidBlender> & blender,uint32_t layer,uint32_t buf_index,bool is_uv)1608 create_pyramid_lap_kernel (
1609     const SmartPtr<CLContext> &context, SmartPtr<CLPyramidBlender> &blender,
1610     uint32_t layer, uint32_t buf_index, bool is_uv)
1611 {
1612     char transform_option[1024];
1613     snprintf (
1614         transform_option, sizeof(transform_option),
1615         "-DPYRAMID_UV=%d -DCL_PYRAMID_ENABLE_DUMP=%d", (is_uv ? 1 : 0), CL_PYRAMID_ENABLE_DUMP);
1616 
1617     SmartPtr<CLImageKernel> kernel;
1618     kernel = new CLPyramidLapKernel (context, blender, layer, buf_index, is_uv);
1619     XCAM_ASSERT (kernel.ptr ());
1620     XCAM_FAIL_RETURN (
1621         ERROR,
1622         kernel->build_kernel (kernels_info[KernelPyramidLap], transform_option) == XCAM_RETURN_NO_ERROR,
1623         NULL,
1624         "load pyramid blender kernel(%s) failed", (is_uv ? "UV" : "Y"));
1625     return kernel;
1626 }
1627 
1628 static SmartPtr<CLImageKernel>
create_pyramid_reconstruct_kernel(const SmartPtr<CLContext> & context,SmartPtr<CLPyramidBlender> & blender,uint32_t layer,bool is_uv)1629 create_pyramid_reconstruct_kernel (
1630     const SmartPtr<CLContext> &context,
1631     SmartPtr<CLPyramidBlender> &blender,
1632     uint32_t layer,
1633     bool is_uv)
1634 {
1635     char transform_option[1024];
1636     snprintf (
1637         transform_option, sizeof(transform_option),
1638         "-DPYRAMID_UV=%d -DCL_PYRAMID_ENABLE_DUMP=%d", (is_uv ? 1 : 0), CL_PYRAMID_ENABLE_DUMP);
1639 
1640     SmartPtr<CLImageKernel> kernel;
1641     kernel = new CLPyramidReconstructKernel (context, blender, layer, is_uv);
1642     XCAM_ASSERT (kernel.ptr ());
1643     XCAM_FAIL_RETURN (
1644         ERROR,
1645         kernel->build_kernel (kernels_info[KernelPyramidReconstruct], transform_option) == XCAM_RETURN_NO_ERROR,
1646         NULL,
1647         "load pyramid blender kernel(%s) failed", (is_uv ? "UV" : "Y"));
1648     return kernel;
1649 }
1650 
1651 static SmartPtr<CLImageKernel>
create_pyramid_blend_kernel(const SmartPtr<CLContext> & context,SmartPtr<CLPyramidBlender> & blender,uint32_t layer,bool is_uv,bool need_seam)1652 create_pyramid_blend_kernel (
1653     const SmartPtr<CLContext> &context,
1654     SmartPtr<CLPyramidBlender> &blender,
1655     uint32_t layer,
1656     bool is_uv,
1657     bool need_seam)
1658 {
1659     char transform_option[1024];
1660     snprintf (
1661         transform_option, sizeof(transform_option),
1662         "-DPYRAMID_UV=%d -DCL_PYRAMID_ENABLE_DUMP=%d", (is_uv ? 1 : 0), CL_PYRAMID_ENABLE_DUMP);
1663 
1664     SmartPtr<CLImageKernel> kernel;
1665     kernel = new CLPyramidBlendKernel (context, blender, layer, is_uv, need_seam);
1666     uint32_t index = KernelPyramidBlender;
1667     if (need_seam)
1668         index = KernelSeamBlender;
1669 
1670     XCAM_ASSERT (kernel.ptr ());
1671     XCAM_FAIL_RETURN (
1672         ERROR,
1673         kernel->build_kernel (kernels_info[index], transform_option) == XCAM_RETURN_NO_ERROR,
1674         NULL,
1675         "load pyramid blender kernel(%s) failed", (is_uv ? "UV" : "Y"));
1676     return kernel;
1677 }
1678 
1679 static SmartPtr<CLImageKernel>
create_pyramid_blender_local_scale_kernel(const SmartPtr<CLContext> & context,SmartPtr<CLPyramidBlender> & blender,bool is_uv)1680 create_pyramid_blender_local_scale_kernel (
1681     const SmartPtr<CLContext> &context,
1682     SmartPtr<CLPyramidBlender> &blender,
1683     bool is_uv)
1684 {
1685     char transform_option[1024];
1686     snprintf (transform_option, sizeof(transform_option), "-DPYRAMID_UV=%d", is_uv ? 1 : 0);
1687 
1688     SmartPtr<CLImageKernel> kernel;
1689     kernel = new CLBlenderLocalScaleKernel (context, blender, is_uv);
1690     XCAM_ASSERT (kernel.ptr ());
1691     XCAM_FAIL_RETURN (
1692         ERROR,
1693         kernel->build_kernel (kernels_info[KernelPyramidScale], transform_option) == XCAM_RETURN_NO_ERROR,
1694         NULL,
1695         "load pyramid blender local scaling kernel(%s) failed", is_uv ? "UV" : "Y");
1696     return kernel;
1697 }
1698 
1699 static SmartPtr<CLImageKernel>
create_pyramid_copy_kernel(const SmartPtr<CLContext> & context,SmartPtr<CLPyramidBlender> & blender,uint32_t buf_index,bool is_uv)1700 create_pyramid_copy_kernel (
1701     const SmartPtr<CLContext> &context,
1702     SmartPtr<CLPyramidBlender> &blender,
1703     uint32_t buf_index,
1704     bool is_uv)
1705 {
1706     char transform_option[1024];
1707     snprintf (transform_option, sizeof(transform_option), "-DPYRAMID_UV=%d", (is_uv ? 1 : 0));
1708 
1709     SmartPtr<CLImageKernel> kernel;
1710     kernel = new CLPyramidCopyKernel (context, blender, buf_index, is_uv);
1711     XCAM_ASSERT (kernel.ptr ());
1712     XCAM_FAIL_RETURN (
1713         ERROR,
1714         kernel->build_kernel (kernels_info[KernelPyramidCopy], transform_option) == XCAM_RETURN_NO_ERROR,
1715         NULL,
1716         "load pyramid blender kernel(%s) failed", (is_uv ? "UV" : "Y"));
1717     return kernel;
1718 }
1719 
1720 static SmartPtr<CLImageKernel>
create_seam_diff_kernel(const SmartPtr<CLContext> & context,SmartPtr<CLPyramidBlender> & blender)1721 create_seam_diff_kernel (
1722     const SmartPtr<CLContext> &context, SmartPtr<CLPyramidBlender> &blender)
1723 {
1724     SmartPtr<CLImageKernel> kernel;
1725     kernel = new CLSeamDiffKernel (context, blender);
1726     XCAM_ASSERT (kernel.ptr ());
1727     XCAM_FAIL_RETURN (
1728         ERROR,
1729         kernel->build_kernel (kernels_info[KernelImageDiff], NULL) == XCAM_RETURN_NO_ERROR,
1730         NULL,
1731         "load seam diff kernel failed");
1732     return kernel;
1733 }
1734 
1735 static SmartPtr<CLImageKernel>
create_seam_DP_kernel(const SmartPtr<CLContext> & context,SmartPtr<CLPyramidBlender> & blender)1736 create_seam_DP_kernel (
1737     const SmartPtr<CLContext> &context, SmartPtr<CLPyramidBlender> &blender)
1738 {
1739     SmartPtr<CLImageKernel> kernel;
1740     kernel = new CLSeamDPKernel (context, blender);
1741     XCAM_ASSERT (kernel.ptr ());
1742     XCAM_FAIL_RETURN (
1743         ERROR,
1744         kernel->build_kernel (kernels_info[KernelSeamDP], NULL) == XCAM_RETURN_NO_ERROR,
1745         NULL,
1746         "load seam DP kernel failed");
1747     return kernel;
1748 }
1749 
1750 static SmartPtr<CLImageKernel>
create_seam_mask_scale_kernel(const SmartPtr<CLContext> & context,SmartPtr<CLPyramidBlender> & blender,uint32_t layer,bool need_scale,bool need_slm)1751 create_seam_mask_scale_kernel (
1752     const SmartPtr<CLContext> &context,
1753     SmartPtr<CLPyramidBlender> &blender,
1754     uint32_t layer,
1755     bool need_scale,
1756     bool need_slm)
1757 {
1758     char build_option[1024];
1759     snprintf (build_option, sizeof(build_option), "-DENABLE_MASK_GAUSS_SCALE=%d", (need_scale ? 1 : 0));
1760     int kernel_idx = (need_slm ? KernelSeamMaskScaleSLM : KernelSeamMaskScale);
1761 
1762     SmartPtr<CLImageKernel> kernel;
1763     kernel = new CLPyramidSeamMaskKernel (context, blender, layer, need_scale, need_slm);
1764     XCAM_ASSERT (kernel.ptr ());
1765     XCAM_FAIL_RETURN (
1766         ERROR,
1767         kernel->build_kernel (kernels_info[kernel_idx], build_option) == XCAM_RETURN_NO_ERROR,
1768         NULL,
1769         "load seam mask scale kernel failed");
1770     return kernel;
1771 }
1772 
1773 SmartPtr<CLImageHandler>
create_pyramid_blender(const SmartPtr<CLContext> & context,int layer,bool need_uv,bool need_seam,CLBlenderScaleMode scale_mode)1774 create_pyramid_blender (
1775     const SmartPtr<CLContext> &context, int layer, bool need_uv,
1776     bool need_seam, CLBlenderScaleMode scale_mode)
1777 {
1778     SmartPtr<CLPyramidBlender> blender;
1779     SmartPtr<CLImageKernel> kernel;
1780     int i = 0;
1781     uint32_t buf_index = 0;
1782     int max_plane = (need_uv ? 2 : 1);
1783     bool uv_status[2] = {false, true};
1784 
1785     XCAM_FAIL_RETURN (
1786         ERROR,
1787         layer > 0 && layer <= XCAM_CL_PYRAMID_MAX_LEVEL,
1788         NULL,
1789         "create_pyramid_blender failed with wrong layer:%d, please set it between %d and %d",
1790         layer, 1, XCAM_CL_PYRAMID_MAX_LEVEL);
1791 
1792     blender = new CLPyramidBlender (context, "cl_pyramid_blender", layer, need_uv, need_seam, scale_mode);
1793     XCAM_ASSERT (blender.ptr ());
1794 
1795     if (need_seam) {
1796         kernel = create_seam_diff_kernel (context, blender);
1797         XCAM_FAIL_RETURN (ERROR, kernel.ptr (), NULL, "create seam diff kernel failed");
1798         blender->add_kernel (kernel);
1799 
1800         kernel = create_seam_DP_kernel (context, blender);
1801         XCAM_FAIL_RETURN (ERROR, kernel.ptr (), NULL, "create seam DP kernel failed");
1802         blender->add_kernel (kernel);
1803 
1804         for (i = 0; i < layer; ++i) {
1805             bool need_scale = (i < layer - 1);
1806             bool need_slm = (i == 0);
1807             kernel = create_seam_mask_scale_kernel (context, blender, (uint32_t)i, need_scale, need_slm);
1808             XCAM_FAIL_RETURN (ERROR, kernel.ptr (), NULL, "create seam mask scale kernel failed");
1809             blender->add_kernel (kernel);
1810         }
1811     }
1812 
1813     for (int plane = 0; plane < max_plane; ++plane) {
1814         for (buf_index = 0; buf_index < XCAM_BLENDER_IMAGE_NUM; ++buf_index) {
1815             for (i = 0; i < layer - 1; ++i) {
1816                 kernel = create_pyramid_transform_kernel (context, blender, (uint32_t)i, buf_index, uv_status[plane]);
1817                 XCAM_FAIL_RETURN (ERROR, kernel.ptr (), NULL, "create pyramid transform kernel failed");
1818                 blender->add_kernel (kernel);
1819 
1820                 kernel = create_pyramid_lap_kernel (context, blender, (uint32_t)i, buf_index, uv_status[plane]);
1821                 XCAM_FAIL_RETURN (ERROR, kernel.ptr (), NULL, "create pyramid lap transform kernel failed");
1822                 blender->add_kernel (kernel);
1823             }
1824         }
1825 
1826         for (i = 0; i < layer; ++i) {
1827             kernel = create_pyramid_blend_kernel (context, blender, (uint32_t)i, uv_status[plane], need_seam);
1828             XCAM_FAIL_RETURN (ERROR, kernel.ptr (), NULL, "create pyramid blend kernel failed");
1829             blender->add_kernel (kernel);
1830         }
1831 
1832         for (i = layer - 2; i >= 0 && i < layer; --i) {
1833             kernel = create_pyramid_reconstruct_kernel (context, blender, (uint32_t)i, uv_status[plane]);
1834             XCAM_FAIL_RETURN (ERROR, kernel.ptr (), NULL, "create pyramid reconstruct kernel failed");
1835             blender->add_kernel (kernel);
1836         }
1837 
1838         if (scale_mode == CLBlenderScaleLocal) {
1839             kernel = create_pyramid_blender_local_scale_kernel (context, blender, uv_status[plane]);
1840             XCAM_FAIL_RETURN (ERROR, kernel.ptr (), NULL, "create pyramid blender local scaling kernel failed");
1841             blender->add_kernel (kernel);
1842         }
1843 
1844         for (buf_index = 0; buf_index < XCAM_BLENDER_IMAGE_NUM; ++buf_index) {
1845             kernel = create_pyramid_copy_kernel (context, blender, buf_index, uv_status[plane]);
1846             XCAM_FAIL_RETURN (ERROR, kernel.ptr (), NULL, "create pyramid copy kernel failed");
1847             blender->add_kernel (kernel);
1848         }
1849     }
1850 
1851     return blender;
1852 }
1853 
1854 }
1855