// // VulkanDeconvolution.cpp // MNN // // Created by MNN on 2019/01/31. // Copyright © 2018, Alibaba Group Holding Limited // #include "VulkanDeconvolution.hpp" #include "core/Macro.h" #include "core/TensorUtils.hpp" namespace MNN { VulkanDeconvolution::VulkanDeconvolution(Backend* bn) : VulkanBasicExecution(bn) { // Donthing } VulkanDeconvolution* VulkanDeconvolution::create(Backend* bn, const Op* op, OpType type, bool multiInputs) { auto conv = op->main_as_Convolution2D(); auto exeRes = new VulkanDeconvolution(bn); exeRes->mConvCommonOption = conv->common(); auto vkBn = (VulkanBackend*)bn; int outputC4 = UP_DIV(exeRes->mConvCommonOption->outputCount(), 4); bool useFP16 = vkBn->useFP16(); size_t elementSize = useFP16 ? sizeof(int16_t) : sizeof(float); auto biasBuffer = std::make_shared(vkBn->getMemoryPool(), false, outputC4 * 4 * elementSize); auto biasPtr = biasBuffer->map(); ::memset(biasPtr, 0, outputC4 * 4 * elementSize); if (conv->bias() != nullptr) { if (useFP16) { FLOAT_TO_HALF(conv->bias()->data(), (int16_t *)biasPtr, conv->bias()->size()); } else { ::memcpy(biasPtr, conv->bias()->data(), conv->bias()->size() * sizeof(float)); } } biasBuffer->unmap(); exeRes->mBias = biasBuffer; exeRes->mConvParam = std::make_shared(vkBn->getMemoryPool(), false, sizeof(VulkanConvolutionCommon::ConvolutionParameter), nullptr, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT); int kh = exeRes->mConvCommonOption->kernelY(); int kw = exeRes->mConvCommonOption->kernelX(); int co = exeRes->mConvCommonOption->outputCount(); int coC4 = UP_DIV(co, 4); int ci = exeRes->mConvCommonOption->inputCount(); if (type == OpType_DeconvolutionDepthwise) { ci = 1; } const float* tempWeight = nullptr; int tempWeightSize = 0; std::shared_ptr quanCommon; if (!multiInputs) { ConvolutionCommon::getConvParameters(&quanCommon, bn, op, &tempWeight, &tempWeightSize); MNN_ASSERT(nullptr != tempWeight); if (0 >= ci) { ci = tempWeightSize / co / kw / kh; } } int ciC4 = UP_DIV(ci, 4); if (type == OpType_Deconvolution) { exeRes->mKernel.reset(MNN::Tensor::createDevice({kw*kh, coC4, ciC4, 16})); } else { exeRes->mKernel.reset(MNN::Tensor::createDevice({kw*kh, coC4, 4})); } exeRes->mKernelReorder = VulkanRaster::create(exeRes->mKernel.get(), vkBn); auto des = TensorUtils::getDescribe(exeRes->mKernel.get()); int pack = 4; if (OpType_DeconvolutionDepthwise == type) { for (int i=0; iregions.emplace_back(std::move(reg)); } } else { for (int i=0; iregions.emplace_back(std::move(reg)); } } } if (!multiInputs) { MNN_ASSERT(nullptr != tempWeight); auto res = vkBn->onAcquireBuffer(exeRes->mKernel.get(), Backend::STATIC); if (!res) { return nullptr; } std::shared_ptr tempWeightTensor(Tensor::createDevice({tempWeightSize})); res = vkBn->onAcquireBuffer(tempWeightTensor.get(), Backend::STATIC); if (!res) { return nullptr; } // FP32->FP16 and cp data from cpu to gpu { const void * tempWeightData; std::vector tempFP16Weight; if (useFP16) { tempFP16Weight.resize(tempWeightSize * elementSize); FLOAT_TO_HALF(tempWeight, (int16_t *) tempFP16Weight.data(), tempWeightSize); tempWeightData = (const void *) tempFP16Weight.data(); } else { tempWeightData = (const void *)tempWeight; } auto tempWeightBuffer = reinterpret_cast(tempWeightTensor->deviceId()); vkBn->copyToGPUBuffer(tempWeightData, tempWeightBuffer->buffer(), tempWeightSize * elementSize, TensorUtils::getDescribeOrigin(tempWeightTensor.get())->offset); } std::shared_ptr prearrangeCmd( vkBn->getPool().allocBuffer()); for (auto& reg : des->regions) { reg.origin = tempWeightTensor.get(); } prearrangeCmd->begin(0); exeRes->mKernelReorder.exe->onEncode({}, {exeRes->mKernel.get()}, prearrangeCmd.get()); prearrangeCmd->end(); vkBn->pushCommand(prearrangeCmd->get()); vkBn->finish(); exeRes->mKernelReorder.exe = nullptr; } std::vector types{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, }; std::string pKey = "glsl_"; if (type == OpType_Deconvolution) { pKey += "deconvolution_"; } else { pKey += "deconvolutionDepthwise_"; } std::string macro = VulkanConvolutionCommon::getPostTreatMacro(exeRes->mConvCommonOption); pKey += macro; if (vkBn->useFP16()) { pKey += "FP16_"; } pKey += "comp"; MNN_ASSERT(type == OpType_Deconvolution || type == OpType_DeconvolutionDepthwise); exeRes->mPipeline = vkBn->getPipeline(pKey, types); exeRes->mPipelineSet.reset(exeRes->mPipeline->createSet()); return exeRes; } ErrorCode VulkanDeconvolution::onEncode(const std::vector& inputs, const std::vector& outputs, const VulkanCommandPool::Buffer* cmdBuffer) { auto src = inputs[0]; auto dst = outputs[0]; const int ocDiv4 = UP_DIV(dst->channel(), 4); auto common = mConvCommonOption; auto extra = static_cast(backend()); if (inputs.size() >= 2) { auto res = extra->onAcquireBuffer(mKernel.get(), Backend::DYNAMIC); if (!res) { return NO_ERROR; } auto kernelBuffer = extra->getBuffer(mKernel.get()); auto des = TensorUtils::getDescribe(mKernel.get()); for (auto& reg : des->regions) { reg.origin = inputs[1]; } auto rasterCode = mKernelReorder.exe->onEncode({}, {mKernel.get()}, cmdBuffer); if (NO_ERROR != rasterCode) { return rasterCode; } cmdBuffer->barrierSource(kernelBuffer); } { auto convCons = reinterpret_cast(mConvParam->map()); VulkanConvolutionCommon::writeDeconvolution(convCons, common, src, dst); mConvParam->unmap(); } auto dstBuffer = extra->getBuffer(dst); auto srcBuffer = extra->getBuffer(src); auto kernelBuffer = extra->getBuffer(mKernel.get()); mPipelineSet->writeBuffer(dstBuffer, 0); mPipelineSet->writeBuffer(srcBuffer, 1); mPipelineSet->writeBuffer(kernelBuffer, 2); if (inputs.size() >= 3) { auto biasBuffer = extra->getBuffer(inputs[2]); mPipelineSet->writeBuffer(biasBuffer, 3); } else { mPipelineSet->writeBuffer(mBias->buffer(), 3, mBias->size()); } mPipelineSet->writeBuffer(mConvParam->buffer(), 4, mConvParam->size()); mPipeline->bind(cmdBuffer->get(), mPipelineSet->get()); auto totalCount = dst->width() * dst->height() * ocDiv4 * dst->batch(); vkCmdDispatch(cmdBuffer->get(), UP_DIV(totalCount, 64), 1, 1); if (inputs.size() >= 2) { extra->onReleaseBuffer(mKernel.get(), Backend::DYNAMIC); } return NO_ERROR; } class VulkanDeconvolutionCreator : public VulkanBackend::Creator { public: virtual VulkanBasicExecution* onCreate(const std::vector& inputs, const std::vector& outputs, const MNN::Op* op, Backend* backend) const override { return VulkanDeconvolution::create(backend, op, op->type(), inputs.size() > 1); } }; static bool gResistor = []() { VulkanBackend::addCreator(OpType_DeconvolutionDepthwise, new VulkanDeconvolutionCreator); VulkanBackend::addCreator(OpType_Deconvolution, new VulkanDeconvolutionCreator); return true; }(); } // namespace MNN