aboutsummaryrefslogtreecommitdiff
path: root/modules/vulkan/vk_blender.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/vulkan/vk_blender.cpp')
-rw-r--r--modules/vulkan/vk_blender.cpp1537
1 files changed, 1537 insertions, 0 deletions
diff --git a/modules/vulkan/vk_blender.cpp b/modules/vulkan/vk_blender.cpp
new file mode 100644
index 0000000..f880649
--- /dev/null
+++ b/modules/vulkan/vk_blender.cpp
@@ -0,0 +1,1537 @@
+/*
+ * vk_blender.cpp - vulkan blender implementation
+ *
+ * Copyright (c) 2018 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Yinhang Liu <yinhangx.liu@intel.com>
+ */
+
+#include "xcam_utils.h"
+
+#include "vk_device.h"
+#include "vk_worker.h"
+#include "vk_blender.h"
+#include "vk_video_buf_allocator.h"
+
+#define DUMP_BUFFER 0
+
+#define GAUSS_RADIUS 2
+#define GAUSS_DIAMETER ((GAUSS_RADIUS)*2+1)
+
+const float gauss_coeffs[GAUSS_DIAMETER] = {0.152f, 0.222f, 0.252f, 0.222f, 0.152f};
+
+#define GS_SHADER_BINDING_COUNT 4
+#define LAP_SHADER_BINDING_COUNT 6
+#define BLEND_SHADER_BINDING_COUNT 7
+#define RECONSTRUCT_SHADER_BINDING_COUNT 9
+
+#define CHECK_RET(ret, format, ...) \
+ if (!xcam_ret_is_ok (ret)) { \
+ XCAM_LOG_ERROR (format, ## __VA_ARGS__); \
+ }
+
+#define DECLARE_VK_PUSH_CONST(PushConstClass, PushConstsProp) \
+ class PushConstClass : public VKConstRange::VKPushConstArg { \
+ private: PushConstsProp _prop; \
+ public: \
+ PushConstClass (const PushConstsProp &prop) : _prop (prop) {} \
+ bool get_const_data (VkPushConstantRange &range, void *& ptr) { \
+ range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; \
+ range.offset = 0; \
+ range.size = sizeof (_prop); \
+ ptr = &_prop; \
+ return true; } \
+ }
+
+namespace XCam {
+
+#if DUMP_BUFFER
+static void
+dump_vkbuf_with_perfix (const SmartPtr<VKBuffer> &buf, const char *perfix_name)
+{
+ XCAM_ASSERT (buf.ptr ());
+ XCAM_ASSERT (perfix_name);
+
+ const VKBufInfo &info = buf->get_buf_info ();
+ char file_name[XCAM_VK_NAME_LENGTH];
+ snprintf (
+ file_name, XCAM_VK_NAME_LENGTH, "%s-%dx%d.%s",
+ perfix_name, info.width, info.height, xcam_fourcc_to_string (info.format));
+
+ FILE *fp = fopen (file_name, "wb");
+ if (!fp) {
+ XCAM_LOG_ERROR ( "vk-blend open file(%s) failed", file_name);
+ }
+
+ uint8_t *ptr = (uint8_t *)buf->map ();
+ for (uint32_t i = 0; i < info.height * 3 / 2; ++i) {
+ uint8_t *start = ptr + info.aligned_width * i;
+ fwrite (start, info.width, 1, fp);
+ }
+ buf->unmap ();
+ fclose (fp);
+}
+#define dump_vkbuf dump_vkbuf_with_perfix
+
+static void
+dump_level_vkbuf (const SmartPtr<VKBuffer> &buf, const char *name, uint32_t level, uint32_t idx)
+{
+ char file_name[XCAM_VK_NAME_LENGTH];
+ snprintf (file_name, XCAM_VK_NAME_LENGTH, "%s-L%d-Idx%d", name, level, idx);
+
+ dump_vkbuf_with_perfix (buf, file_name);
+}
+#endif
+
+namespace VKBlenderPriv {
+
+enum ShaderID {
+ ShaderGaussScalePyr = 0,
+ ShaderLapTransPyr,
+ ShaderBlendPyr,
+ ShaderReconstructPyr
+};
+
+static const VKShaderInfo shaders_info[] = {
+ VKShaderInfo (
+ "main",
+ std::vector<uint32_t> {
+#include "shader_gauss_scale_pyr.comp.spv"
+ }),
+ VKShaderInfo (
+ "main",
+ std::vector<uint32_t> {
+#include "shader_lap_trans_pyr.comp.spv"
+ }),
+ VKShaderInfo (
+ "main",
+ std::vector<uint32_t> {
+#include "shader_blend_pyr.comp.spv"
+ }),
+ VKShaderInfo (
+ "main",
+ std::vector<uint32_t> {
+#include "shader_reconstruct_pyr.comp.spv"
+ })
+};
+
+struct GaussScalePushConstsProp {
+ uint in_img_width;
+ uint in_img_height;
+ uint in_offset_x;
+ uint out_img_width;
+ uint out_img_height;
+ uint merge_width;
+
+ GaussScalePushConstsProp ()
+ : in_img_width (0)
+ , in_img_height (0)
+ , in_offset_x (0)
+ , out_img_width (0)
+ , out_img_height (0)
+ , merge_width (0)
+ {}
+};
+
+struct LapPushConstsProp {
+ uint in_img_width;
+ uint in_img_height;
+ uint in_offset_x;
+ uint gaussscale_img_width;
+ uint gaussscale_img_height;
+ uint merge_width;
+
+ LapPushConstsProp ()
+ : in_img_width (0)
+ , in_img_height (0)
+ , in_offset_x (0)
+ , gaussscale_img_width (0)
+ , gaussscale_img_height (0)
+ , merge_width (0)
+ {}
+};
+
+struct BlendPushConstsProp {
+ uint in_img_width;
+
+ BlendPushConstsProp ()
+ : in_img_width (0)
+ {}
+};
+
+struct ReconstructPushConstsProp {
+ uint lap_img_width;
+ uint lap_img_height;
+ uint out_img_width;
+ uint out_offset_x;
+ uint prev_blend_img_width;
+ uint prev_blend_img_height;
+
+ ReconstructPushConstsProp ()
+ : lap_img_width (0)
+ , lap_img_height (0)
+ , out_img_width (0)
+ , out_offset_x (0)
+ , prev_blend_img_width (0)
+ , prev_blend_img_height (0)
+ {}
+};
+
+DECLARE_VK_PUSH_CONST (VKGaussScalePushConst, GaussScalePushConstsProp);
+DECLARE_VK_PUSH_CONST (VKLapPushConst, LapPushConstsProp);
+DECLARE_VK_PUSH_CONST (VKBlendPushConst, BlendPushConstsProp);
+DECLARE_VK_PUSH_CONST (VKReconstructPushConst, ReconstructPushConstsProp);
+
+DECLARE_WORK_CALLBACK (CbGaussScalePyr, VKBlender, gauss_scale_done);
+DECLARE_WORK_CALLBACK (CbLapTransPyr, VKBlender, lap_trans_done);
+DECLARE_WORK_CALLBACK (CbBlendPyr, VKBlender, blend_done);
+DECLARE_WORK_CALLBACK (CbReconstructPyr, VKBlender, reconstruct_done);
+
+class BlendArgs
+ : public VKWorker::VKArguments
+{
+public:
+ BlendArgs (uint32_t lv, VKBlender::BufIdx i = VKBlender::BufIdx0);
+
+ uint32_t get_level () {
+ return _level;
+ }
+ VKBlender::BufIdx get_idx () {
+ return _idx;
+ }
+
+private:
+ uint32_t _level;
+ VKBlender::BufIdx _idx;
+};
+
+struct PyrLayer {
+ uint32_t blend_width;
+ uint32_t blend_height;
+
+ SmartPtr<VKBlender::Sync> lap_sync[VKBlender::BufIdxMax];
+ SmartPtr<VKBlender::Sync> blend_sync;
+ SmartPtr<VKBlender::Sync> reconstruct_sync;
+
+ SmartPtr<VKBuffer> gs_buf[VKBlender::BufIdxMax];
+ SmartPtr<VKBuffer> lap_buf[VKBlender::BufIdxMax];
+ SmartPtr<VKBuffer> mask;
+ SmartPtr<VKBuffer> blend_buf;
+ SmartPtr<VKBuffer> reconstruct_buf;
+
+ VKDescriptor::SetBindInfoArray gs_bindings[VKBlender::BufIdxMax];
+ VKDescriptor::SetBindInfoArray lap_bindings[VKBlender::BufIdxMax];
+ VKDescriptor::SetBindInfoArray blend_bindings;
+ VKDescriptor::SetBindInfoArray reconstruct_bindings;
+
+ SmartPtr<VKConstRange::VKPushConstArg> gs_consts[VKBlender::BufIdxMax];
+ SmartPtr<VKConstRange::VKPushConstArg> lap_consts[VKBlender::BufIdxMax];
+ SmartPtr<VKConstRange::VKPushConstArg> blend_consts;
+ SmartPtr<VKConstRange::VKPushConstArg> reconstruct_consts;
+
+ WorkSize gs_global_size[VKBlender::BufIdxMax];
+ WorkSize lap_global_size[VKBlender::BufIdxMax];
+ WorkSize blend_global_size;
+ WorkSize reconstruct_global_size;
+
+ VKWorker *gauss_scale[VKBlender::BufIdxMax];
+ VKWorker *lap_trans[VKBlender::BufIdxMax];
+ VKWorker *blend;
+ VKWorker *reconstruct;
+
+ PyrLayer ();
+};
+
+typedef std::map<ShaderID, SmartPtr<VKWorker>> VKWorkers;
+
+class BlenderImpl {
+public:
+ PyrLayer pyr_layer[XCAM_VK_MAX_LEVEL];
+ uint32_t pyr_layers_num;
+
+private:
+ VKBlender *_blender;
+ VKWorkers _workers;
+
+public:
+ BlenderImpl (VKBlender *blender, uint32_t layers_num)
+ : pyr_layers_num (layers_num)
+ , _blender (blender)
+ {
+ XCAM_ASSERT (layers_num >= 2 && layers_num <= XCAM_VK_MAX_LEVEL);
+ }
+
+ XCamReturn start_gauss_scale (uint32_t level, VKBlender::BufIdx idx);
+ XCamReturn start_lap_trans (uint32_t level, VKBlender::BufIdx idx);
+ XCamReturn start_top_blend ();
+ XCamReturn start_reconstruct (uint32_t level);
+ XCamReturn stop ();
+
+ void init_syncs ();
+ XCamReturn init_layers_bufs (const SmartPtr<ImageHandler::Parameters> &base);
+ XCamReturn bind_io_bufs_to_layer0 (
+ SmartPtr<VideoBuffer> &input0, SmartPtr<VideoBuffer> &input1, SmartPtr<VideoBuffer> &output);
+ XCamReturn bind_io_vkbufs_to_desc ();
+ XCamReturn fix_parameters ();
+ XCamReturn create_workers (const SmartPtr<VKBlender> &blender);
+ XCamReturn redirect_workers ();
+
+private:
+ XCamReturn start_lap_tran (uint32_t level, VKBlender::BufIdx idx);
+
+ XCamReturn layer0_allocate_bufs (SmartPtr<VKDevice> dev);
+ XCamReturn layer0_init_mask (SmartPtr<VKDevice> dev);
+
+ XCamReturn layerx_allocate_bufs (SmartPtr<VKDevice> dev, uint32_t level);
+ XCamReturn allocate_vk_bufs (SmartPtr<VKDevice> dev, uint32_t level);
+ XCamReturn scale_down_mask (SmartPtr<VKDevice> dev, uint32_t level);
+
+ XCamReturn fix_gs_params (uint32_t level, VKBlender::BufIdx idx);
+ XCamReturn fix_lap_trans_params (uint32_t level, VKBlender::BufIdx idx);
+ XCamReturn fix_blend_params ();
+ XCamReturn fix_reconstruct_params (uint32_t level);
+};
+
+BlendArgs::BlendArgs (uint32_t lv, VKBlender::BufIdx i)
+ : _level (lv)
+ , _idx (i)
+{
+ XCAM_ASSERT (lv < XCAM_VK_DEFAULT_LEVEL);
+ XCAM_ASSERT (i < VKBlender::BufIdxMax);
+}
+
+PyrLayer::PyrLayer ()
+ : blend_width (0)
+ , blend_height (0)
+{
+}
+
+void
+BlenderImpl::init_syncs ()
+{
+ for (uint32_t i = 0; i < pyr_layers_num - 1; ++i) {
+ PyrLayer &layer = pyr_layer[i];
+
+ layer.lap_sync[VKBlender::BufIdx0] = new VKBlender::Sync (2);
+ XCAM_ASSERT (layer.lap_sync[VKBlender::BufIdx0].ptr ());
+ layer.lap_sync[VKBlender::BufIdx1] = new VKBlender::Sync (2);
+ XCAM_ASSERT (layer.lap_sync[VKBlender::BufIdx1].ptr ());
+
+ layer.reconstruct_sync = new VKBlender::Sync (3);
+ XCAM_ASSERT (layer.reconstruct_sync.ptr ());
+ }
+
+ pyr_layer[pyr_layers_num - 1].blend_sync = new VKBlender::Sync (2);
+ XCAM_ASSERT (pyr_layer[pyr_layers_num - 1].blend_sync.ptr ());
+}
+
+XCamReturn
+BlenderImpl::bind_io_bufs_to_layer0 (
+ SmartPtr<VideoBuffer> &input0, SmartPtr<VideoBuffer> &input1, SmartPtr<VideoBuffer> &output)
+{
+ XCAM_ASSERT (input0.ptr () && input1.ptr ());
+
+ SmartPtr<VKVideoBuffer> in0_vk = input0.dynamic_cast_ptr<VKVideoBuffer> ();
+ SmartPtr<VKVideoBuffer> in1_vk = input1.dynamic_cast_ptr<VKVideoBuffer> ();
+
+ PyrLayer &layer0 = pyr_layer[0];
+ layer0.gs_buf[VKBlender::BufIdx0] = in0_vk->get_vk_buf ();
+ layer0.gs_buf[VKBlender::BufIdx1] = in1_vk->get_vk_buf ();
+ XCAM_ASSERT (layer0.gs_buf[VKBlender::BufIdx0].ptr () && layer0.gs_buf[VKBlender::BufIdx1].ptr ());
+
+ if (!output.ptr ())
+ return XCAM_RETURN_NO_ERROR;
+
+ SmartPtr<VKVideoBuffer> out_vk = output.dynamic_cast_ptr<VKVideoBuffer> ();
+ XCAM_ASSERT (out_vk.ptr ());
+
+ layer0.reconstruct_buf = out_vk->get_vk_buf ();
+ XCAM_ASSERT (layer0.reconstruct_buf.ptr ());
+ layer0.blend_buf = layer0.reconstruct_buf;
+
+ return XCAM_RETURN_NO_ERROR;
+}
+
+XCamReturn
+BlenderImpl::bind_io_vkbufs_to_desc ()
+{
+ PyrLayer &layer0 = pyr_layer[0];
+ PyrLayer &layer1 = pyr_layer[1];
+ XCAM_ASSERT (layer0.gs_buf[VKBlender::BufIdx0].ptr () && layer0.gs_buf[VKBlender::BufIdx1].ptr ());
+ XCAM_ASSERT (layer0.reconstruct_buf.ptr ());
+
+ VKDescriptor::SetBindInfoArray &gs_bindings0 = layer1.gs_bindings[VKBlender::BufIdx0];
+ VKDescriptor::SetBindInfoArray &gs_bindings1 = layer1.gs_bindings[VKBlender::BufIdx1];
+ gs_bindings0[0].desc = VKBufDesc (layer0.gs_buf[VKBlender::BufIdx0], NV12PlaneYIdx);
+ gs_bindings0[1].desc = VKBufDesc (layer0.gs_buf[VKBlender::BufIdx0], NV12PlaneUVIdx);
+ gs_bindings1[0].desc = VKBufDesc (layer0.gs_buf[VKBlender::BufIdx1], NV12PlaneYIdx);
+ gs_bindings1[1].desc = VKBufDesc (layer0.gs_buf[VKBlender::BufIdx1], NV12PlaneUVIdx);
+
+ VKDescriptor::SetBindInfoArray &lap_bindings0 = layer0.lap_bindings[VKBlender::BufIdx0];
+ VKDescriptor::SetBindInfoArray &lap_bindings1 = layer0.lap_bindings[VKBlender::BufIdx1];
+ lap_bindings0[0].desc = VKBufDesc (layer0.gs_buf[VKBlender::BufIdx0], NV12PlaneYIdx);
+ lap_bindings0[1].desc = VKBufDesc (layer0.gs_buf[VKBlender::BufIdx0], NV12PlaneUVIdx);
+ lap_bindings1[0].desc = VKBufDesc (layer0.gs_buf[VKBlender::BufIdx1], NV12PlaneYIdx);
+ lap_bindings1[1].desc = VKBufDesc (layer0.gs_buf[VKBlender::BufIdx1], NV12PlaneUVIdx);
+
+ layer0.reconstruct_bindings[4].desc = VKBufDesc (layer0.reconstruct_buf, NV12PlaneYIdx);
+ layer0.reconstruct_bindings[5].desc = VKBufDesc (layer0.reconstruct_buf, NV12PlaneUVIdx);
+
+ return XCAM_RETURN_NO_ERROR;
+}
+
+static void
+convert_to_vkinfo (const VideoBufferInfo &info, VKBufInfo &vk_info)
+{
+ vk_info.format = info.format;
+ vk_info.width = info.width;
+ vk_info.height = info.height;
+ vk_info.aligned_width = info.aligned_width;
+ vk_info.aligned_height = info.aligned_height;
+ vk_info.size = info.size;
+ vk_info.strides[0] = info.strides[0];
+ vk_info.strides[1] = info.strides[1];
+ vk_info.offsets[0] = info.offsets[0];
+ vk_info.offsets[1] = info.offsets[1];
+ vk_info.slice_size[0] = info.strides[0] * info.aligned_height;
+ vk_info.slice_size[1] = info.size - info.offsets[1];
+}
+
+XCamReturn
+BlenderImpl::layer0_allocate_bufs (SmartPtr<VKDevice> dev)
+{
+ if (pyr_layers_num == 1)
+ return XCAM_RETURN_NO_ERROR;
+
+ PyrLayer &layer0 = pyr_layer[0];
+ XCAM_FAIL_RETURN (
+ ERROR, layer0.blend_width && layer0.blend_height, XCAM_RETURN_ERROR_PARAM,
+ "vk-blend invalid blend size:%dx%d", layer0.blend_width, layer0.blend_height);
+
+ VideoBufferInfo info;
+ info.init (
+ V4L2_PIX_FMT_NV12, layer0.blend_width, layer0.blend_height,
+ XCAM_ALIGN_UP (layer0.blend_width, VK_BLENDER_ALIGN_X),
+ XCAM_ALIGN_UP (layer0.blend_height, VK_BLENDER_ALIGN_X));
+
+ VKBufInfo vk_info;
+ convert_to_vkinfo (info, vk_info);
+
+ for (int idx = 0; idx < VKBlender::BufIdxMax; ++idx) {
+ layer0.lap_buf[idx] = VKBuffer::create_buffer (dev, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, vk_info.size);
+ XCAM_ASSERT (layer0.lap_buf[idx].ptr ());
+ layer0.lap_buf[idx]->set_buf_info (vk_info);
+ }
+
+ return XCAM_RETURN_NO_ERROR;
+}
+
+XCamReturn
+BlenderImpl::allocate_vk_bufs (SmartPtr<VKDevice> dev, uint32_t level)
+{
+ XCAM_ASSERT (level >= 1 && level < pyr_layers_num);
+
+ PyrLayer &layer = pyr_layer[level];
+ VideoBufferInfo info;
+ info.init (
+ V4L2_PIX_FMT_NV12, layer.blend_width, layer.blend_height,
+ XCAM_ALIGN_UP (layer.blend_width, VK_BLENDER_ALIGN_X),
+ XCAM_ALIGN_UP (layer.blend_height, VK_BLENDER_ALIGN_X));
+
+ VKBufInfo vk_info;
+ convert_to_vkinfo (info, vk_info);
+
+ bool top_layer = (level == pyr_layers_num - 1);
+ for (int idx = 0; idx < VKBlender::BufIdxMax; ++idx) {
+ layer.gs_buf[idx] = VKBuffer::create_buffer (dev, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, vk_info.size);
+ XCAM_ASSERT (layer.gs_buf[idx].ptr ());
+ layer.gs_buf[idx]->set_buf_info (vk_info);
+
+ if (top_layer)
+ continue;
+
+ layer.lap_buf[idx] = VKBuffer::create_buffer (dev, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, vk_info.size);
+ XCAM_ASSERT (layer.lap_buf[idx].ptr ());
+ layer.lap_buf[idx]->set_buf_info (vk_info);
+ }
+
+ layer.reconstruct_buf = VKBuffer::create_buffer (dev, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, vk_info.size);
+ XCAM_ASSERT (layer.reconstruct_buf.ptr ());
+ layer.reconstruct_buf->set_buf_info (vk_info);
+
+ if (top_layer)
+ layer.blend_buf = layer.reconstruct_buf;
+
+ return XCAM_RETURN_NO_ERROR;
+}
+
+XCamReturn
+BlenderImpl::layer0_init_mask (SmartPtr<VKDevice> dev)
+{
+ PyrLayer &layer = pyr_layer[0];
+ XCAM_ASSERT (layer.blend_width && ((layer.blend_width % VK_BLENDER_ALIGN_X) == 0));
+
+ uint32_t buf_size = layer.blend_width * sizeof (uint8_t);
+ SmartPtr<VKBuffer> buf = VKBuffer::create_buffer (dev, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, buf_size);
+ XCAM_ASSERT (buf.ptr ());
+
+ VKBufInfo info;
+ info.width = layer.blend_width;
+ info.height = 1;
+ info.size = buf_size;
+ buf->set_buf_info (info);
+
+ std::vector<float> gauss_table;
+ uint32_t quater = info.width / 4;
+
+ get_gauss_table (quater, (quater + 1) / 4.0f, gauss_table, false);
+ for (uint32_t i = 0; i < gauss_table.size (); ++i) {
+ float value = ((i < quater) ? (128.0f * (2.0f - gauss_table[i])) : (128.0f * gauss_table[i]));
+ value = XCAM_CLAMP (value, 0.0f, 255.0f);
+ gauss_table[i] = value;
+ }
+
+ uint8_t *mask_ptr = (uint8_t *) buf->map (buf_size, 0);
+ XCAM_FAIL_RETURN (ERROR, mask_ptr, XCAM_RETURN_ERROR_PARAM, "vk-blend map range failed");
+
+ uint32_t gauss_start_pos = (info.width - gauss_table.size ()) / 2;
+ uint32_t idx = 0;
+ for (idx = 0; idx < gauss_start_pos; ++idx) {
+ mask_ptr[idx] = 255;
+ }
+ for (uint32_t i = 0; i < gauss_table.size (); ++idx, ++i) {
+ mask_ptr[idx] = (uint8_t) gauss_table[i];
+ }
+ for (; idx < info.width; ++idx) {
+ mask_ptr[idx] = 0;
+ }
+ buf->unmap ();
+
+ layer.mask = buf;
+
+ return XCAM_RETURN_NO_ERROR;
+}
+
+XCamReturn
+BlenderImpl::scale_down_mask (SmartPtr<VKDevice> dev, uint32_t level)
+{
+ XCAM_ASSERT (level >= 1 && level < pyr_layers_num);
+
+ PyrLayer &layer = pyr_layer[level];
+ PyrLayer &prev_layer = pyr_layer[level - 1];
+
+ XCAM_ASSERT (prev_layer.mask.ptr ());
+ XCAM_ASSERT (layer.blend_width && ((layer.blend_width % VK_BLENDER_ALIGN_X) == 0));
+
+ uint32_t buf_size = layer.blend_width * sizeof (uint8_t);
+ SmartPtr<VKBuffer> buf = VKBuffer::create_buffer (dev, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, buf_size);
+ XCAM_ASSERT (buf.ptr ());
+
+ VKBufInfo info;
+ info.width = layer.blend_width;
+ info.height = 1;
+ info.size = buf_size;
+ buf->set_buf_info (info);
+
+ const VKBufInfo prev_info = prev_layer.mask->get_buf_info ();
+ uint8_t *prev_ptr = (uint8_t *) prev_layer.mask->map (prev_info.size, 0);
+ XCAM_FAIL_RETURN (ERROR, prev_ptr, XCAM_RETURN_ERROR_PARAM, "vk-blend map range failed");
+
+ uint8_t *cur_ptr = (uint8_t *) buf->map (info.size, 0);
+ XCAM_FAIL_RETURN (ERROR, cur_ptr, XCAM_RETURN_ERROR_PARAM, "vk-blend map range failed");
+
+ for (uint32_t i = 0; i < info.width; ++i) {
+ int prev_start = i * 2 - 2;
+ float sum = 0.0f;
+
+ for (int j = 0; j < GAUSS_DIAMETER; ++j) {
+ int prev_idx = XCAM_CLAMP (prev_start + j, 0, (int)prev_info.width);
+ sum += prev_ptr[prev_idx] * gauss_coeffs[j];
+ }
+
+ cur_ptr[i] = XCAM_CLAMP (sum, 0.0f, 255.0f);
+ }
+
+ buf->unmap ();
+ prev_layer.mask->unmap ();
+
+ layer.mask = buf;
+
+ return XCAM_RETURN_NO_ERROR;
+}
+
+XCamReturn
+BlenderImpl::layerx_allocate_bufs (SmartPtr<VKDevice> dev, uint32_t level)
+{
+ XCAM_ASSERT (level >= 1 && level < pyr_layers_num);
+
+ PyrLayer &layer = pyr_layer[level];
+ PyrLayer &prev_layer = pyr_layer[level - 1];
+ XCAM_FAIL_RETURN (
+ ERROR, prev_layer.blend_width && prev_layer.blend_height, XCAM_RETURN_ERROR_PARAM,
+ "vk-blend invalid blend size:%dx%d", prev_layer.blend_width, prev_layer.blend_height);
+
+ layer.blend_width = XCAM_ALIGN_UP ((prev_layer.blend_width + 1) / 2, VK_BLENDER_ALIGN_X);
+ layer.blend_height = XCAM_ALIGN_UP ((prev_layer.blend_height + 1) / 2, VK_BLENDER_ALIGN_Y);
+
+ XCamReturn ret = allocate_vk_bufs (dev, level);
+ XCAM_FAIL_RETURN (ERROR, xcam_ret_is_ok (ret), ret, "vk-blend build vk buffers failed, level:%d", level);
+
+ ret = scale_down_mask (dev, level);
+ XCAM_FAIL_RETURN (ERROR, xcam_ret_is_ok (ret), ret, "vk-blend scale down mask failed, level:%d", level);
+
+ return ret;
+}
+
+static XCamReturn
+check_desc (
+ const VideoBufferInfo &in0_info, const VideoBufferInfo &in1_info,
+ const Rect &merge0_area, const Rect &merge1_area)
+{
+ XCAM_FAIL_RETURN (
+ ERROR,
+ in0_info.width && in0_info.height && in1_info.width &&
+ in0_info.height == in1_info.height,
+ XCAM_RETURN_ERROR_PARAM,
+ "vk-blend invalid buffer size: in0:%dx%d in1:%dx%d out:%dx%d",
+ in0_info.width, in0_info.height, in1_info.width, in1_info.height);
+
+ XCAM_FAIL_RETURN (
+ ERROR,
+ merge0_area.width && merge0_area.width == merge1_area.width &&
+ merge0_area.pos_y == 0 && merge1_area.pos_y == 0 &&
+ merge0_area.height == merge1_area.height && merge0_area.height == (int32_t)in0_info.height,
+ XCAM_RETURN_ERROR_PARAM,
+ "vk-blend invalid merge area: merge0(%d, %d, %d, %d) merge1(%d, %d, %d, %d)",
+ merge0_area.pos_x, merge0_area.pos_y, merge0_area.width, merge0_area.height,
+ merge1_area.pos_x, merge1_area.pos_y, merge1_area.width, merge1_area.height);
+
+ return XCAM_RETURN_NO_ERROR;
+}
+
+XCamReturn
+BlenderImpl::init_layers_bufs (const SmartPtr<ImageHandler::Parameters> &base)
+{
+ XCAM_ASSERT (base.ptr ());
+ SmartPtr<VKBlender::BlenderParam> param = base.dynamic_cast_ptr<VKBlender::BlenderParam> ();
+ XCAM_ASSERT (param.ptr () && param->in_buf.ptr () && param->in1_buf.ptr ());
+
+ const VideoBufferInfo &in0_info = param->in_buf->get_video_info ();
+ const VideoBufferInfo &in1_info = param->in1_buf->get_video_info ();
+ const Rect merge0_area = _blender->get_input_merge_area (VKBlender::BufIdx0);
+ const Rect merge1_area = _blender->get_input_merge_area (VKBlender::BufIdx1);
+
+ XCamReturn ret = check_desc (in0_info, in1_info, merge0_area, merge1_area);
+ XCAM_FAIL_RETURN (ERROR, xcam_ret_is_ok (ret), ret, "vk-blend check desc failed");
+
+ PyrLayer &layer0 = pyr_layer[0];
+ layer0.blend_width = XCAM_ALIGN_UP (merge0_area.width, VK_BLENDER_ALIGN_X);
+ layer0.blend_height = XCAM_ALIGN_UP (merge0_area.height, VK_BLENDER_ALIGN_Y);
+
+ SmartPtr<VKDevice> dev = _blender->get_vk_device ();
+ XCAM_ASSERT (dev.ptr ());
+
+ ret = bind_io_bufs_to_layer0 (param->in_buf, param->in1_buf, param->out_buf);
+ XCAM_FAIL_RETURN (ERROR, xcam_ret_is_ok (ret), ret, "vk-blend bind bufs to layer0 failed");
+ ret = layer0_allocate_bufs (dev);
+ XCAM_FAIL_RETURN (ERROR, xcam_ret_is_ok (ret), ret, "vk-blend layer0 build buffers failed");
+ ret = layer0_init_mask (dev);
+ XCAM_FAIL_RETURN (ERROR, xcam_ret_is_ok (ret), ret, "vk-blend layer0 init mask failed");
+
+ for (uint32_t level = 1; level < pyr_layers_num; ++level) {
+ layerx_allocate_bufs (dev, level);
+ XCAM_FAIL_RETURN (
+ ERROR, xcam_ret_is_ok (ret), ret,
+ "vk-blend build buffers failed, level:%d", level);
+ }
+
+ return XCAM_RETURN_NO_ERROR;
+}
+
+static SmartPtr<VKWorker>
+create_gauss_scale_pyr_shader (const SmartPtr<VKBlender> &blender)
+{
+ SmartPtr<VKDevice> dev = blender->get_vk_device ();
+ XCAM_ASSERT (dev.ptr ());
+
+ GaussScalePushConstsProp prop;
+ VKConstRange::VKPushConstArgs push_consts;
+ push_consts.push_back (new VKGaussScalePushConst (prop));
+
+ VKDescriptor::BindingArray binding_layout;
+ binding_layout.clear ();
+ for (int i = 0; i < GS_SHADER_BINDING_COUNT; ++i) {
+ SmartPtr<VKDescriptor::SetLayoutBinding> binding =
+ new VKDescriptor::ComputeLayoutBinding (VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, i);
+ binding_layout.push_back (binding);
+ }
+
+ SmartPtr<VKWorker> worker = new VKWorker (dev, "VKGaussScaleShader", new CbGaussScalePyr (blender));
+ XCAM_ASSERT (worker.ptr ());
+
+ XCamReturn ret = worker->build (shaders_info[ShaderGaussScalePyr], binding_layout, push_consts);
+ XCAM_FAIL_RETURN (ERROR, xcam_ret_is_ok (ret), NULL, "vk-blend build VKGaussScaleShader failed");
+
+ return worker;
+}
+
+static SmartPtr<VKWorker>
+create_lap_trans_pyr_shader (const SmartPtr<VKBlender> &blender)
+{
+ SmartPtr<VKDevice> dev = blender->get_vk_device ();
+ XCAM_ASSERT (dev.ptr ());
+
+ LapPushConstsProp prop;
+ VKConstRange::VKPushConstArgs push_consts;
+ push_consts.push_back (new VKLapPushConst (prop));
+
+ VKDescriptor::BindingArray binding_layout;
+ binding_layout.clear ();
+ for (int i = 0; i < LAP_SHADER_BINDING_COUNT; ++i) {
+ SmartPtr<VKDescriptor::SetLayoutBinding> binding =
+ new VKDescriptor::ComputeLayoutBinding (VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, i);
+ binding_layout.push_back (binding);
+ }
+
+ SmartPtr<VKWorker> worker = new VKWorker (dev, "VKLapTransShader", new CbLapTransPyr (blender));
+ XCAM_ASSERT (worker.ptr ());
+
+ XCamReturn ret = worker->build (shaders_info[ShaderLapTransPyr], binding_layout, push_consts);
+ XCAM_FAIL_RETURN (ERROR, xcam_ret_is_ok (ret), NULL, "vk-blend build VKLapTransShader failed");
+
+ return worker;
+}
+
+static SmartPtr<VKWorker>
+create_blend_pyr_shader (const SmartPtr<VKBlender> &blender)
+{
+ SmartPtr<VKDevice> dev = blender->get_vk_device ();
+ XCAM_ASSERT (dev.ptr ());
+
+ BlendPushConstsProp prop;
+ VKConstRange::VKPushConstArgs push_consts;
+ push_consts.push_back (new VKBlendPushConst (prop));
+
+ VKDescriptor::BindingArray binding_layout;
+ binding_layout.clear ();
+ for (int i = 0; i < BLEND_SHADER_BINDING_COUNT; ++i) {
+ SmartPtr<VKDescriptor::SetLayoutBinding> binding =
+ new VKDescriptor::ComputeLayoutBinding (VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, i);
+ binding_layout.push_back (binding);
+ }
+
+ SmartPtr<VKWorker> worker = new VKWorker (dev, "VKBlendPyrShader", new CbBlendPyr (blender));
+ XCAM_ASSERT (worker.ptr ());
+
+ XCamReturn ret = worker->build (shaders_info[ShaderBlendPyr], binding_layout, push_consts);
+ XCAM_FAIL_RETURN (ERROR, xcam_ret_is_ok (ret), NULL, "vk-blend build VKBlendPyrShader failed");
+
+ return worker;
+}
+
+static SmartPtr<VKWorker>
+create_reconstruct_pyr_shader (const SmartPtr<VKBlender> &blender)
+{
+ SmartPtr<VKDevice> dev = blender->get_vk_device ();
+ XCAM_ASSERT (dev.ptr ());
+
+ ReconstructPushConstsProp prop;
+ VKConstRange::VKPushConstArgs push_consts;
+ push_consts.push_back (new VKReconstructPushConst (prop));
+
+ VKDescriptor::BindingArray binding_layout;
+ binding_layout.clear ();
+ for (int i = 0; i < RECONSTRUCT_SHADER_BINDING_COUNT; ++i) {
+ SmartPtr<VKDescriptor::SetLayoutBinding> binding =
+ new VKDescriptor::ComputeLayoutBinding (VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, i);
+ binding_layout.push_back (binding);
+ }
+
+ SmartPtr<VKWorker> worker = new VKWorker (dev, "VKReconstructShader", new CbReconstructPyr (blender));
+ XCAM_ASSERT (worker.ptr ());
+
+ XCamReturn ret = worker->build (shaders_info[ShaderReconstructPyr], binding_layout, push_consts);
+ XCAM_FAIL_RETURN (ERROR, xcam_ret_is_ok (ret), NULL, "vk-blend build VKReconstructShader failed");
+
+ return worker;
+}
+
+XCamReturn
+BlenderImpl::create_workers (const SmartPtr<VKBlender> &blender)
+{
+ XCAM_ASSERT (blender.ptr ());
+
+ VKWorkers::iterator i = _workers.find (ShaderGaussScalePyr);
+ if (i == _workers.end ()) {
+ SmartPtr<VKWorker> gauss_scale = create_gauss_scale_pyr_shader (blender);
+ XCAM_ASSERT (gauss_scale.ptr ());
+ _workers.insert (std::make_pair (ShaderGaussScalePyr, gauss_scale));
+ }
+
+ i = _workers.find (ShaderLapTransPyr);
+ if (i == _workers.end ()) {
+ SmartPtr<VKWorker> lap_trans = create_lap_trans_pyr_shader (blender);
+ XCAM_ASSERT (lap_trans.ptr ());
+ _workers.insert (std::make_pair (ShaderLapTransPyr, lap_trans));
+ }
+
+ i = _workers.find (ShaderBlendPyr);
+ if (i == _workers.end ()) {
+ SmartPtr<VKWorker> blend = create_blend_pyr_shader (blender);
+ XCAM_ASSERT (blend.ptr ());
+ _workers.insert (std::make_pair (ShaderBlendPyr, blend));
+ }
+
+ i = _workers.find (ShaderReconstructPyr);
+ if (i == _workers.end ()) {
+ SmartPtr<VKWorker> reconstruct = create_reconstruct_pyr_shader (blender);
+ XCAM_ASSERT (reconstruct.ptr ());
+ _workers.insert (std::make_pair (ShaderReconstructPyr, reconstruct));
+ }
+
+ return XCAM_RETURN_NO_ERROR;
+}
+
+XCamReturn
+BlenderImpl::redirect_workers ()
+{
+ VKWorkers::iterator i = _workers.find (ShaderGaussScalePyr);
+ XCAM_ASSERT (i != _workers.end ());
+ SmartPtr<VKWorker> gauss_scale = i->second;
+
+ i = _workers.find (ShaderLapTransPyr);
+ XCAM_ASSERT (i != _workers.end ());
+ SmartPtr<VKWorker> lap_trans = i->second;
+
+ i = _workers.find (ShaderBlendPyr);
+ XCAM_ASSERT (i != _workers.end ());
+ SmartPtr<VKWorker> top_blend = i->second;
+
+ i = _workers.find (ShaderReconstructPyr);
+ XCAM_ASSERT (i != _workers.end ());
+ SmartPtr<VKWorker> reconstruct = i->second;
+
+ XCAM_ASSERT (gauss_scale.ptr () && lap_trans.ptr () && reconstruct.ptr () && top_blend.ptr ());
+ for (uint32_t i = 0; i < pyr_layers_num - 1; ++i) {
+ PyrLayer &layer_next = pyr_layer[i + 1];
+ layer_next.gauss_scale[VKBlender::BufIdx0] = gauss_scale.ptr ();
+ layer_next.gauss_scale[VKBlender::BufIdx1] = gauss_scale.ptr ();
+
+ PyrLayer &layer = pyr_layer[i];
+ layer.lap_trans[VKBlender::BufIdx0] = lap_trans.ptr ();
+ layer.lap_trans[VKBlender::BufIdx1] = lap_trans.ptr ();
+ layer.reconstruct = reconstruct.ptr ();
+ }
+
+ PyrLayer &top_layer = pyr_layer[pyr_layers_num - 1];
+ top_layer.blend = top_blend.ptr ();
+
+ return XCAM_RETURN_NO_ERROR;
+}
+
+XCamReturn
+BlenderImpl::fix_parameters ()
+{
+ for (uint32_t i = 0; i < pyr_layers_num - 1; ++i) {
+ fix_gs_params (i + 1, VKBlender::BufIdx0);
+ fix_gs_params (i + 1, VKBlender::BufIdx1);
+
+ fix_lap_trans_params (i, VKBlender::BufIdx0);
+ fix_lap_trans_params (i, VKBlender::BufIdx1);
+
+ fix_reconstruct_params (i);
+ }
+
+ fix_blend_params ();
+
+ return XCAM_RETURN_NO_ERROR;
+}
+
+XCamReturn
+BlenderImpl::fix_gs_params (uint32_t level, VKBlender::BufIdx idx)
+{
+ XCAM_ASSERT (level >= 1);
+
+ uint32_t level_in = level - 1;
+ PyrLayer &layer_in = pyr_layer[level_in];
+ PyrLayer &layer_out = pyr_layer[level];
+ XCAM_ASSERT (layer_out.gs_buf[idx].ptr () && (layer_in.gs_buf[idx].ptr () || level == 1));
+
+ VKDescriptor::BindingArray binding_layout;
+ binding_layout.clear ();
+ for (int i = 0; i < GS_SHADER_BINDING_COUNT; ++i) {
+ SmartPtr<VKDescriptor::SetLayoutBinding> binding =
+ new VKDescriptor::ComputeLayoutBinding (VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, i);
+ binding_layout.push_back (binding);
+ }
+
+ VKDescriptor::SetBindInfoArray bindings (GS_SHADER_BINDING_COUNT);
+ bindings[0].layout = binding_layout[0];
+ bindings[1].layout = binding_layout[1];
+ if (layer_in.gs_buf[idx].ptr ()) {
+ bindings[0].desc = VKBufDesc (layer_in.gs_buf[idx], NV12PlaneYIdx);
+ bindings[1].desc = VKBufDesc (layer_in.gs_buf[idx], NV12PlaneUVIdx);
+ }
+ bindings[2].layout = binding_layout[2];
+ bindings[2].desc = VKBufDesc (layer_out.gs_buf[idx], NV12PlaneYIdx);
+ bindings[3].layout = binding_layout[3];
+ bindings[3].desc = VKBufDesc (layer_out.gs_buf[idx], NV12PlaneUVIdx);
+ layer_out.gs_bindings[idx] = bindings;
+
+ const VKBufInfo in_info = layer_in.gs_buf[idx]->get_buf_info ();
+ const VKBufInfo out_info = layer_out.gs_buf[idx]->get_buf_info ();
+
+ size_t unit_bytes = sizeof (uint32_t);
+ GaussScalePushConstsProp prop;
+ prop.in_img_width = XCAM_ALIGN_UP (in_info.width, unit_bytes) / unit_bytes;
+ prop.in_img_height = in_info.height;
+ prop.out_img_width = XCAM_ALIGN_UP (out_info.width, unit_bytes) / unit_bytes;
+ prop.out_img_height = out_info.height;
+ if (level == 1) {
+ const Rect area = _blender->get_input_merge_area (idx);
+ prop.in_offset_x = XCAM_ALIGN_UP (area.pos_x, unit_bytes) / unit_bytes;
+ prop.merge_width = XCAM_ALIGN_UP (area.width, unit_bytes) / unit_bytes;
+ } else {
+ prop.in_offset_x = 0;
+ prop.merge_width = XCAM_ALIGN_UP (in_info.width, unit_bytes) / unit_bytes;
+ }
+ layer_out.gs_consts[idx] = new VKGaussScalePushConst (prop);
+
+ layer_out.gs_global_size[idx] = WorkSize (
+ XCAM_ALIGN_UP (prop.out_img_width, 8) / 8,
+ XCAM_ALIGN_UP (out_info.height, 16) / 16);
+
+ return XCAM_RETURN_NO_ERROR;
+}
+
+XCamReturn
+BlenderImpl::fix_lap_trans_params (uint32_t level, VKBlender::BufIdx idx)
+{
+ XCAM_ASSERT (level < pyr_layers_num - 1);
+
+ PyrLayer &layer = pyr_layer[level];
+ PyrLayer &layer_next = pyr_layer[level + 1];
+ XCAM_ASSERT ((layer.gs_buf[idx].ptr () || level == 0) && layer_next.gs_buf[idx].ptr ());
+ XCAM_ASSERT (layer.lap_buf[idx].ptr ());
+
+ VKDescriptor::BindingArray binding_layout;
+ binding_layout.clear ();
+ for (int i = 0; i < LAP_SHADER_BINDING_COUNT; ++i) {
+ SmartPtr<VKDescriptor::SetLayoutBinding> binding =
+ new VKDescriptor::ComputeLayoutBinding (VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, i);
+ binding_layout.push_back (binding);
+ }
+
+ VKDescriptor::SetBindInfoArray bindings (LAP_SHADER_BINDING_COUNT);
+ bindings[0].layout = binding_layout[0];
+ bindings[1].layout = binding_layout[1];
+ if (layer.gs_buf[idx].ptr ()) {
+ bindings[0].desc = VKBufDesc (layer.gs_buf[idx], NV12PlaneYIdx);
+ bindings[1].desc = VKBufDesc (layer.gs_buf[idx], NV12PlaneUVIdx);
+ }
+ bindings[2].layout = binding_layout[2];
+ bindings[2].desc = VKBufDesc (layer_next.gs_buf[idx], NV12PlaneYIdx);
+ bindings[3].layout = binding_layout[3];
+ bindings[3].desc = VKBufDesc (layer_next.gs_buf[idx], NV12PlaneUVIdx);
+ bindings[4].layout = binding_layout[4];
+ bindings[4].desc = VKBufDesc (layer.lap_buf[idx], NV12PlaneYIdx);
+ bindings[5].layout = binding_layout[5];
+ bindings[5].desc = VKBufDesc (layer.lap_buf[idx], NV12PlaneUVIdx);
+ layer.lap_bindings[idx] = bindings;
+
+ const VKBufInfo in_info = layer.gs_buf[idx]->get_buf_info ();
+ const VKBufInfo gs_info = layer_next.gs_buf[idx]->get_buf_info ();
+
+ size_t unit_bytes = sizeof (uint32_t) * 2;
+ LapPushConstsProp prop;
+ prop.in_img_width = XCAM_ALIGN_UP (in_info.width, unit_bytes) / unit_bytes;
+ prop.in_img_height = in_info.height;
+ prop.gaussscale_img_width = XCAM_ALIGN_UP (gs_info.width, sizeof (uint32_t)) / sizeof (uint32_t);
+ prop.gaussscale_img_height = gs_info.height;
+ if (level == 0) {
+ const Rect area = _blender->get_input_merge_area (idx);
+ prop.in_offset_x = XCAM_ALIGN_UP (area.pos_x, unit_bytes) / unit_bytes;
+ prop.merge_width = XCAM_ALIGN_UP (area.width, unit_bytes) / unit_bytes;
+ } else {
+ prop.in_offset_x = 0;
+ prop.merge_width = XCAM_ALIGN_UP (in_info.width, unit_bytes) / unit_bytes;
+ }
+ layer.lap_consts[idx] = new VKLapPushConst (prop);
+
+ layer.lap_global_size[idx] = WorkSize (
+ XCAM_ALIGN_UP (prop.merge_width, 8) / 8,
+ XCAM_ALIGN_UP (in_info.height, 32) / 32);
+
+ return XCAM_RETURN_NO_ERROR;
+}
+
+XCamReturn
+BlenderImpl::fix_blend_params ()
+{
+ PyrLayer &top_layer = pyr_layer[pyr_layers_num - 1];
+ XCAM_ASSERT (top_layer.gs_buf[VKBlender::BufIdx0].ptr () && top_layer.gs_buf[VKBlender::BufIdx1].ptr ());
+ XCAM_ASSERT (top_layer.mask.ptr ());
+ XCAM_ASSERT (top_layer.blend_buf.ptr ());
+
+ VKDescriptor::BindingArray binding_layout;
+ binding_layout.clear ();
+ for (int i = 0; i < BLEND_SHADER_BINDING_COUNT; ++i) {
+ SmartPtr<VKDescriptor::SetLayoutBinding> binding =
+ new VKDescriptor::ComputeLayoutBinding (VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, i);
+ binding_layout.push_back (binding);
+ }
+
+ VKDescriptor::SetBindInfoArray bindings (BLEND_SHADER_BINDING_COUNT);
+ bindings[0].layout = binding_layout[0];
+ bindings[0].desc = VKBufDesc (top_layer.gs_buf[VKBlender::BufIdx0], NV12PlaneYIdx);
+ bindings[1].layout = binding_layout[1];
+ bindings[1].desc = VKBufDesc (top_layer.gs_buf[VKBlender::BufIdx0], NV12PlaneUVIdx);
+ bindings[2].layout = binding_layout[2];
+ bindings[2].desc = VKBufDesc (top_layer.gs_buf[VKBlender::BufIdx1], NV12PlaneYIdx);
+ bindings[3].layout = binding_layout[3];
+ bindings[3].desc = VKBufDesc (top_layer.gs_buf[VKBlender::BufIdx1], NV12PlaneUVIdx);
+ bindings[4].layout = binding_layout[4];
+ bindings[4].desc = VKBufDesc (top_layer.blend_buf, NV12PlaneYIdx);
+ bindings[5].layout = binding_layout[5];
+ bindings[5].desc = VKBufDesc (top_layer.blend_buf, NV12PlaneUVIdx);
+ bindings[6].layout = binding_layout[6];
+ bindings[6].desc = VKBufDesc (top_layer.mask);
+ top_layer.blend_bindings = bindings;
+
+ const VKBufInfo in0_info = top_layer.gs_buf[VKBlender::BufIdx0]->get_buf_info ();
+ size_t unit_bytes = sizeof (uint32_t) * 2;
+ BlendPushConstsProp prop;
+ prop.in_img_width = XCAM_ALIGN_UP (in0_info.width, unit_bytes) / unit_bytes;
+ top_layer.blend_consts = new VKBlendPushConst (prop);
+
+ top_layer.blend_global_size = WorkSize (
+ XCAM_ALIGN_UP (prop.in_img_width, 8) / 8,
+ XCAM_ALIGN_UP (in0_info.height, 16) / 16);
+
+ return XCAM_RETURN_NO_ERROR;
+}
+
+XCamReturn
+BlenderImpl::fix_reconstruct_params (uint32_t level)
+{
+ XCAM_ASSERT (level < pyr_layers_num - 1);
+
+ PyrLayer &layer = pyr_layer[level];
+ PyrLayer &prev_layer = pyr_layer[level + 1];
+
+ XCAM_ASSERT (layer.lap_buf[VKBlender::BufIdx0].ptr ());
+ XCAM_ASSERT (layer.lap_buf[VKBlender::BufIdx1].ptr ());
+ XCAM_ASSERT (prev_layer.reconstruct_buf.ptr () && (layer.reconstruct_buf.ptr () || level == 0));
+ XCAM_ASSERT (layer.mask.ptr ());
+
+ VKDescriptor::BindingArray binding_layout;
+ binding_layout.clear ();
+ for (int i = 0; i < RECONSTRUCT_SHADER_BINDING_COUNT; ++i) {
+ SmartPtr<VKDescriptor::SetLayoutBinding> binding =
+ new VKDescriptor::ComputeLayoutBinding (VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, i);
+ binding_layout.push_back (binding);
+ }
+
+ VKDescriptor::SetBindInfoArray bindings (RECONSTRUCT_SHADER_BINDING_COUNT);
+ bindings[0].layout = binding_layout[0];
+ bindings[0].desc = VKBufDesc (layer.lap_buf[VKBlender::BufIdx0], NV12PlaneYIdx);
+ bindings[1].layout = binding_layout[1];
+ bindings[1].desc = VKBufDesc (layer.lap_buf[VKBlender::BufIdx0], NV12PlaneUVIdx);
+ bindings[2].layout = binding_layout[2];
+ bindings[2].desc = VKBufDesc (layer.lap_buf[VKBlender::BufIdx1], NV12PlaneYIdx);
+ bindings[3].layout = binding_layout[3];
+ bindings[3].desc = VKBufDesc (layer.lap_buf[VKBlender::BufIdx1], NV12PlaneUVIdx);
+ bindings[4].layout = binding_layout[4];
+ bindings[5].layout = binding_layout[5];
+ if (layer.reconstruct_buf.ptr ()) {
+ bindings[4].desc = VKBufDesc (layer.reconstruct_buf, NV12PlaneYIdx);
+ bindings[5].desc = VKBufDesc (layer.reconstruct_buf, NV12PlaneUVIdx);
+ }
+ bindings[6].layout = binding_layout[6];
+ bindings[6].desc = VKBufDesc (prev_layer.reconstruct_buf, NV12PlaneYIdx);
+ bindings[7].layout = binding_layout[7];
+ bindings[7].desc = VKBufDesc (prev_layer.reconstruct_buf, NV12PlaneUVIdx);
+ bindings[8].layout = binding_layout[8];
+ bindings[8].desc = VKBufDesc (layer.mask);
+ layer.reconstruct_bindings = bindings;
+
+ const VKBufInfo lap0_info = layer.lap_buf[VKBlender::BufIdx0]->get_buf_info ();
+ const VKBufInfo prev_recons_info = prev_layer.reconstruct_buf->get_buf_info ();
+
+ size_t unit_bytes = sizeof (uint32_t) * 2;
+ ReconstructPushConstsProp prop;
+ prop.lap_img_width = XCAM_ALIGN_UP (lap0_info.width, unit_bytes) / unit_bytes;
+ prop.lap_img_height = lap0_info.height;
+ prop.prev_blend_img_width = XCAM_ALIGN_UP (prev_recons_info.width, sizeof (uint32_t)) / sizeof (uint32_t);
+ prop.prev_blend_img_height = prev_recons_info.height;
+ if (level == 0) {
+ const VideoBufferInfo info = _blender->get_out_video_info ();
+ prop.out_img_width = XCAM_ALIGN_UP (info.width, unit_bytes) / unit_bytes;
+
+ const Rect area = _blender->get_merge_window ();
+ prop.out_offset_x = XCAM_ALIGN_UP (area.pos_x, unit_bytes) / unit_bytes;
+ } else {
+ const VKBufInfo info = layer.reconstruct_buf->get_buf_info ();
+ prop.out_img_width = XCAM_ALIGN_UP (info.width, unit_bytes) / unit_bytes;
+ prop.out_offset_x = 0;
+ }
+ layer.reconstruct_consts = new VKReconstructPushConst (prop);
+
+ layer.reconstruct_global_size = WorkSize (
+ XCAM_ALIGN_UP (prop.lap_img_width, 8) / 8,
+ XCAM_ALIGN_UP (lap0_info.height, 32) / 32);
+
+ return XCAM_RETURN_NO_ERROR;
+}
+
+XCamReturn
+BlenderImpl::start_gauss_scale (uint32_t level, VKBlender::BufIdx idx)
+{
+ XCAM_ASSERT (level >= 1 && level < pyr_layers_num);
+
+ PyrLayer &layer = pyr_layer[level];
+ layer.gauss_scale[idx]->set_global_size (layer.gs_global_size[idx]);
+
+ SmartPtr<BlendArgs> args = new BlendArgs (level, idx);
+ args->set_bindings (layer.gs_bindings[idx]);
+ args->add_push_const (layer.gs_consts[idx]);
+
+ return layer.gauss_scale[idx]->work (args);
+}
+
+XCamReturn
+BlenderImpl::start_lap_tran (uint32_t level, VKBlender::BufIdx idx)
+{
+ PyrLayer &layer = pyr_layer[level];
+
+ SmartPtr<VKBlender::Sync> &sync = layer.lap_sync[idx];
+ if (!sync->is_synced ())
+ return XCAM_RETURN_NO_ERROR;
+ sync->reset ();
+
+ layer.lap_trans[idx]->set_global_size (layer.lap_global_size[idx]);
+
+ SmartPtr<BlendArgs> args = new BlendArgs (level, idx);
+ args->set_bindings (layer.lap_bindings[idx]);
+ args->add_push_const (layer.lap_consts[idx]);
+
+ return layer.lap_trans[idx]->work (args);
+}
+
+XCamReturn
+BlenderImpl::start_lap_trans (uint32_t level, VKBlender::BufIdx idx)
+{
+ XCAM_ASSERT (level >= 1 && level < pyr_layers_num);
+
+ uint32_t pre_level = level - 1;
+ pyr_layer[pre_level].lap_sync[idx]->increment ();
+
+ XCamReturn ret = start_lap_tran (pre_level, idx);
+ XCAM_FAIL_RETURN (
+ ERROR, xcam_ret_is_ok (ret), ret,
+ "vk-blend start lap tran failed, level:%d idx:%d", pre_level, idx);
+
+ if (level == pyr_layers_num - 1)
+ return XCAM_RETURN_NO_ERROR;
+ pyr_layer[level].lap_sync[idx]->increment ();
+
+ ret = start_lap_tran (level, idx);
+ XCAM_FAIL_RETURN (
+ ERROR, xcam_ret_is_ok (ret), ret,
+ "vk-blend start lap tran failed, level:%d idx:%d", level, idx);
+
+ return XCAM_RETURN_NO_ERROR;
+}
+
+XCamReturn
+BlenderImpl::start_top_blend ()
+{
+ uint32_t level = pyr_layers_num - 1;
+ PyrLayer &layer = pyr_layer[level];
+
+ SmartPtr<VKBlender::Sync> &sync = layer.blend_sync;
+ if (!sync->is_synced ())
+ return XCAM_RETURN_NO_ERROR;
+ sync->reset ();
+
+ SmartPtr<VKWorker::VKArguments> args = new VKWorker::VKArguments;
+ args->set_bindings (layer.blend_bindings);
+ args->add_push_const (layer.blend_consts);
+
+ layer.blend->set_global_size (layer.blend_global_size);
+
+ return layer.blend->work (args);
+}
+
+XCamReturn
+BlenderImpl::start_reconstruct (uint32_t level)
+{
+ XCAM_ASSERT (level < pyr_layers_num - 1);
+ PyrLayer &layer = pyr_layer[level];
+
+ SmartPtr<VKBlender::Sync> &sync = layer.reconstruct_sync;
+ if (!sync->is_synced ())
+ return XCAM_RETURN_NO_ERROR;
+ sync->reset ();
+
+ SmartPtr<BlendArgs> args = new BlendArgs (level);
+ args->set_bindings (layer.reconstruct_bindings);
+ args->add_push_const (layer.reconstruct_consts);
+
+ layer.reconstruct->set_global_size (layer.reconstruct_global_size);
+
+ return layer.reconstruct->work (args);
+}
+
+XCamReturn
+BlenderImpl::stop ()
+{
+ for (uint32_t lv = 0; lv < pyr_layers_num; ++lv) {
+ pyr_layer[lv].gs_buf[VKBlender::BufIdx0].release ();
+ pyr_layer[lv].gs_buf[VKBlender::BufIdx1].release ();
+ pyr_layer[lv].lap_buf[VKBlender::BufIdx0].release ();
+ pyr_layer[lv].lap_buf[VKBlender::BufIdx1].release ();
+ pyr_layer[lv].reconstruct_buf.release ();
+ pyr_layer[lv].blend_buf.release ();
+
+ pyr_layer[lv].gs_consts[VKBlender::BufIdx0].release ();
+ pyr_layer[lv].gs_consts[VKBlender::BufIdx1].release ();
+ pyr_layer[lv].lap_consts[VKBlender::BufIdx0].release ();
+ pyr_layer[lv].lap_consts[VKBlender::BufIdx1].release ();
+ pyr_layer[lv].reconstruct_consts.release ();
+ pyr_layer[lv].blend_consts.release ();
+ }
+
+ return XCAM_RETURN_NO_ERROR;
+}
+
+}
+
+VKBlender::VKBlender (const SmartPtr<VKDevice> dev, const char *name)
+ : VKHandler (dev, name)
+ , Blender (VK_BLENDER_ALIGN_X, VK_BLENDER_ALIGN_Y)
+{
+ SmartPtr<VKBlenderPriv::BlenderImpl> impl =
+ new VKBlenderPriv::BlenderImpl (this, XCAM_VK_DEFAULT_LEVEL);
+ XCAM_ASSERT (impl.ptr ());
+
+ _impl = impl;
+}
+
+VKBlender::~VKBlender ()
+{
+}
+
+XCamReturn
+VKBlender::blend (
+ const SmartPtr<VideoBuffer> &in0, const SmartPtr<VideoBuffer> &in1, SmartPtr<VideoBuffer> &out_buf)
+{
+ XCAM_ASSERT (in0.ptr () && in1.ptr ());
+
+ SmartPtr<BlenderParam> param = new BlenderParam (in0, in1, out_buf);
+ XCAM_ASSERT (param.ptr ());
+
+ XCamReturn ret = execute_buffer (param, true);
+ XCAM_FAIL_RETURN (ERROR, xcam_ret_is_ok (ret), ret, "vk-blend execute buffer failed");
+
+ finish ();
+ if (!out_buf.ptr ()) {
+ out_buf = param->out_buf;
+ }
+
+ return ret;
+}
+
+static XCamReturn
+check_merge_area (const SmartPtr<VKBlender> &blender)
+{
+ Rect in0_area, in1_area, out_area;
+
+ in0_area = blender->get_input_merge_area (VKBlender::BufIdx0);
+ XCAM_FAIL_RETURN (
+ ERROR,
+ in0_area.pos_y == 0 && in0_area.width && in0_area.height &&
+ in0_area.pos_x % VK_BLENDER_ALIGN_X == 0 &&
+ in0_area.width % VK_BLENDER_ALIGN_X == 0 &&
+ in0_area.height % VK_BLENDER_ALIGN_Y == 0,
+ XCAM_RETURN_ERROR_PARAM,
+ "vk-blend invalid input0 merge area, pos_x:%d, pos_y:%d, width:%d, height:%d",
+ in0_area.pos_x, in0_area.pos_y, in0_area.width, in0_area.height);
+
+ in1_area = blender->get_input_merge_area (VKBlender::BufIdx1);
+ XCAM_FAIL_RETURN (
+ ERROR,
+ in1_area.pos_y == 0 && in1_area.width && in1_area.height &&
+ in1_area.pos_x % VK_BLENDER_ALIGN_X == 0 &&
+ in1_area.width % VK_BLENDER_ALIGN_X == 0 &&
+ in1_area.height % VK_BLENDER_ALIGN_Y == 0,
+ XCAM_RETURN_ERROR_PARAM,
+ "vk-blend invalid input1 merge area, pos_x:%d, pos_y:%d, width:%d, height:%d",
+ in1_area.pos_x, in1_area.pos_y, in1_area.width, in1_area.height);
+
+ out_area = blender->get_merge_window ();
+ XCAM_FAIL_RETURN (
+ ERROR,
+ out_area.pos_y == 0 && out_area.width && out_area.height &&
+ out_area.pos_x % VK_BLENDER_ALIGN_X == 0 &&
+ out_area.width % VK_BLENDER_ALIGN_X == 0 &&
+ out_area.height % VK_BLENDER_ALIGN_Y == 0,
+ XCAM_RETURN_ERROR_PARAM,
+ "vk-blend invalid output merge area, pos_x:%d, pos_y:%d, width:%d, height:%d",
+ out_area.pos_x, out_area.pos_y, out_area.width, out_area.height);
+
+ XCAM_FAIL_RETURN (
+ ERROR,
+ in0_area.width == in1_area.width && in0_area.height == in1_area.height &&
+ in0_area.width == out_area.width && in0_area.height == out_area.height,
+ XCAM_RETURN_ERROR_PARAM,
+ "vk-blend invalid input or output overlap area, input0:%dx%d, input1:%dx%d, output:%dx%d",
+ in0_area.width, in0_area.height, in1_area.width, in1_area.height, out_area.width, out_area.height);
+
+ return XCAM_RETURN_NO_ERROR;
+}
+
+XCamReturn
+VKBlender::set_output_info (const SmartPtr<ImageHandler::Parameters> &param)
+{
+ const VideoBufferInfo &in0_info = param->in_buf->get_video_info ();
+ XCAM_FAIL_RETURN (
+ ERROR, in0_info.format == V4L2_PIX_FMT_NV12, XCAM_RETURN_ERROR_PARAM,
+ "vk-blend only support NV12 format, but input format is %s",
+ xcam_fourcc_to_string (in0_info.format));
+
+ uint32_t out_width, out_height;
+ get_output_size (out_width, out_height);
+ XCAM_FAIL_RETURN (
+ ERROR, out_width && out_height, XCAM_RETURN_ERROR_PARAM,
+ "vk-blend invalid output size:%dx%d", out_width, out_height);
+
+ VideoBufferInfo out_info;
+ out_info.init (
+ in0_info.format, out_width, out_height,
+ XCAM_ALIGN_UP (out_width, VK_BLENDER_ALIGN_X),
+ XCAM_ALIGN_UP (out_height, VK_BLENDER_ALIGN_Y));
+ set_out_video_info (out_info);
+
+ return XCAM_RETURN_NO_ERROR;
+}
+
+XCamReturn
+VKBlender::configure_resource (const SmartPtr<Parameters> &param)
+{
+ XCAM_ASSERT (param.ptr ());
+ XCAM_ASSERT (_impl->pyr_layers_num <= XCAM_VK_MAX_LEVEL);
+
+ SmartPtr<BlenderParam> blend_param = param.dynamic_cast_ptr<BlenderParam> ();
+ XCAM_ASSERT (blend_param.ptr () && blend_param->in_buf.ptr () && blend_param->in1_buf.ptr ());
+
+ XCamReturn ret = set_output_info (param);
+ XCAM_FAIL_RETURN (ERROR, xcam_ret_is_ok (ret), ret, "vk-blend set output video info failed");
+
+ ret = check_merge_area (this);
+ XCAM_FAIL_RETURN (ERROR, xcam_ret_is_ok (ret), ret, "vk-blend check merge area failed");
+
+ _impl->init_syncs ();
+
+ ret = _impl->init_layers_bufs (param);
+ XCAM_FAIL_RETURN (ERROR, xcam_ret_is_ok (ret), ret, "vk-blend init buffers failed");
+
+ ret = _impl->fix_parameters ();
+ XCAM_FAIL_RETURN (ERROR, xcam_ret_is_ok (ret), ret, "vk-blend fix parameters failed");
+
+ ret = _impl->create_workers (this);
+ XCAM_FAIL_RETURN (ERROR, xcam_ret_is_ok (ret), ret, "vk-blend create workers failed");
+
+ ret = _impl->redirect_workers ();
+ XCAM_FAIL_RETURN (ERROR, xcam_ret_is_ok (ret), ret, "vk-blend redirect workers failed");
+
+ return XCAM_RETURN_NO_ERROR;
+}
+
+XCamReturn
+VKBlender::start_work (const SmartPtr<ImageHandler::Parameters> &param)
+{
+ SmartPtr<VKBlender::BlenderParam> blend_param = param.dynamic_cast_ptr<BlenderParam> ();
+ XCAM_ASSERT (blend_param.ptr ());
+ XCAM_ASSERT (blend_param->in_buf.ptr () && blend_param->in1_buf.ptr () && blend_param->out_buf.ptr ());
+
+#if DUMP_BUFFER
+ SmartPtr<VKVideoBuffer> in0_vk = blend_param->in_buf.dynamic_cast_ptr<VKVideoBuffer> ();
+ SmartPtr<VKVideoBuffer> in1_vk = blend_param->in1_buf.dynamic_cast_ptr<VKVideoBuffer> ();
+ XCAM_ASSERT (in0_vk.ptr () && in1_vk.ptr ());
+ dump_level_vkbuf (in0_vk->get_vk_buf (), "blend-in", 0, VKBlender::BufIdx0);
+ dump_level_vkbuf (in1_vk->get_vk_buf (), "blend-in", 0, VKBlender::BufIdx1);
+#endif
+
+ _impl->bind_io_bufs_to_layer0 (blend_param->in_buf, blend_param->in1_buf, blend_param->out_buf);
+ _impl->bind_io_vkbufs_to_desc ();
+
+ _impl->pyr_layer[0].lap_sync[BufIdx0]->increment ();
+ _impl->pyr_layer[0].lap_sync[BufIdx1]->increment ();
+
+ XCamReturn ret = XCAM_RETURN_NO_ERROR;
+ ret = _impl->start_gauss_scale (1, BufIdx0);
+ CHECK_RET (ret, "vk-blend start gauss scale failed, level:1 index:0\n");
+
+ ret = _impl->start_gauss_scale (1, BufIdx1);
+ CHECK_RET (ret, "vk-blend start gauss scale failed, level:1 index:1\n");
+
+ execute_done (param, ret);
+
+ return ret;
+}
+
+void
+VKBlender::gauss_scale_done (
+ const SmartPtr<Worker> &worker, const SmartPtr<Worker::Arguments> &base, const XCamReturn error)
+{
+ if (!xcam_ret_is_ok (error)) {
+ XCAM_LOG_ERROR ("vk-blend gauss scale failed");
+ return ;
+ }
+
+ SmartPtr<VKBlenderPriv::BlendArgs> args = base.dynamic_cast_ptr<VKBlenderPriv::BlendArgs> ();
+ XCAM_ASSERT (args.ptr ());
+ uint32_t level = args->get_level ();
+ uint32_t next_level = level + 1;
+ BufIdx idx = args->get_idx ();
+
+ SmartPtr<VKWorker> gs_worker = worker.dynamic_cast_ptr<VKWorker> ();
+ XCAM_ASSERT (gs_worker.ptr ());
+ gs_worker->wait_fence ();
+
+#if DUMP_BUFFER
+ dump_level_vkbuf (_impl->pyr_layer[level].gs_buf[idx], "gauss-scale", level, idx);
+#endif
+
+ XCamReturn ret = _impl->start_lap_trans (level, idx);
+ CHECK_RET (ret, "vk-blend execute laplace transformation failed, level:%d idx:%d", level, idx);
+
+ if (next_level == _impl->pyr_layers_num) {
+ _impl->pyr_layer[level].blend_sync->increment ();
+
+ ret = _impl->start_top_blend ();
+ CHECK_RET (ret, "vk-blend execute top blend failed, level:%d idx:%d", level, idx);
+ } else {
+ ret = _impl->start_gauss_scale (next_level, idx);
+ CHECK_RET (ret, "vk-blend execute gauss scale failed, level:%d idx:%d", next_level, idx);
+ }
+}
+
+void
+VKBlender::lap_trans_done (
+ const SmartPtr<Worker> &worker, const SmartPtr<Worker::Arguments> &base, const XCamReturn error)
+{
+ XCAM_UNUSED (base);
+ if (!xcam_ret_is_ok (error)) {
+ XCAM_LOG_ERROR ("vk-blend laplace transformation failed");
+ return ;
+ }
+
+ SmartPtr<VKBlenderPriv::BlendArgs> args = base.dynamic_cast_ptr<VKBlenderPriv::BlendArgs> ();
+ XCAM_ASSERT (args.ptr ());
+ uint32_t level = args->get_level ();
+
+ SmartPtr<VKWorker> laptrans_worker = worker.dynamic_cast_ptr<VKWorker> ();
+ XCAM_ASSERT (laptrans_worker.ptr ());
+ laptrans_worker->wait_fence ();
+
+#if DUMP_BUFFER
+ BufIdx idx = args->get_idx ();
+ dump_level_vkbuf (_impl->pyr_layer[level].lap_buf[idx], "lap", level, idx);
+#endif
+
+ _impl->pyr_layer[level].reconstruct_sync->increment ();
+
+ XCamReturn ret = _impl->start_reconstruct (level);
+ CHECK_RET (ret, "vk-blend execute reconstruct failed, level:%d", level);
+}
+
+void
+VKBlender::blend_done (
+ const SmartPtr<Worker> &worker, const SmartPtr<Worker::Arguments> &base, const XCamReturn error)
+{
+ XCAM_UNUSED (base);
+ if (!xcam_ret_is_ok (error)) {
+ XCAM_LOG_ERROR ("vk-blend blend failed");
+ return ;
+ }
+
+ SmartPtr<VKWorker> blend_worker = worker.dynamic_cast_ptr<VKWorker> ();
+ XCAM_ASSERT (blend_worker.ptr ());
+ blend_worker->wait_fence ();
+
+#if DUMP_BUFFER
+ dump_vkbuf (_impl->pyr_layer[_impl->pyr_layers_num - 1].blend_buf, "blend-top");
+#endif
+
+ uint32_t pre_level = _impl->pyr_layers_num - 2;
+ _impl->pyr_layer[pre_level].reconstruct_sync->increment ();
+
+ XCamReturn ret = _impl->start_reconstruct (pre_level);
+ CHECK_RET (ret, "vk-blend execute reconstruct failed, level:%d", pre_level);
+}
+
+void
+VKBlender::reconstruct_done (
+ const SmartPtr<Worker> &worker, const SmartPtr<Worker::Arguments> &base, const XCamReturn error)
+{
+ XCAM_UNUSED (base);
+ if (!xcam_ret_is_ok (error)) {
+ XCAM_LOG_ERROR ("vk-blend reconstruct failed");
+ return ;
+ }
+
+ SmartPtr<VKBlenderPriv::BlendArgs> args = base.dynamic_cast_ptr<VKBlenderPriv::BlendArgs> ();
+ XCAM_ASSERT (args.ptr ());
+ uint32_t level = args->get_level ();
+
+ SmartPtr<VKWorker> reconstruct_worker = worker.dynamic_cast_ptr<VKWorker> ();
+ XCAM_ASSERT (reconstruct_worker.ptr ());
+ reconstruct_worker->wait_fence ();
+
+#if DUMP_BUFFER
+ BufIdx idx = args->get_idx ();
+ dump_level_vkbuf (_impl->pyr_layer[level].reconstruct_buf, "reconstruct", level, idx);
+#endif
+
+ if (level == 0) {
+ return;
+ }
+
+ uint32_t pre_level = level - 1;
+ _impl->pyr_layer[pre_level].reconstruct_sync->increment ();
+
+ XCamReturn ret = _impl->start_reconstruct (pre_level);
+ CHECK_RET (ret, "vk-blend execute reconstruct failed, level:%d", pre_level);
+}
+
+SmartPtr<VKHandler>
+create_vk_blender (const SmartPtr<VKDevice> &dev)
+{
+ SmartPtr<VKBlender> blender = new VKBlender (dev);
+ XCAM_ASSERT (blender.ptr ());
+
+ return blender;
+}
+
+SmartPtr<Blender>
+Blender::create_vk_blender (const SmartPtr<VKDevice> &dev)
+{
+ SmartPtr<VKHandler> handler = XCam::create_vk_blender (dev);
+ return handler.dynamic_cast_ptr<Blender> ();
+}
+
+}