blob: ef0d437747006baca6eba6ce95bf904b96de05e0 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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.
*/
#include "rsovScript.h"
#include "bcinfo/MetadataExtractor.h"
#include "rsContext.h"
#include "rsType.h"
#include "rsUtils.h"
#include "rsovAllocation.h"
#include "rsovContext.h"
#include "rsovCore.h"
#include <fstream>
namespace android {
namespace renderscript {
namespace rsov {
namespace {
const char *COMPILER_EXE_PATH = "/system/bin/bcc_rsov";
std::vector<const char *> setCompilerArgs(const char *bcFileName,
const char *cacheDir) {
rsAssert(bcFileName && cacheDir);
std::vector<const char *> args;
args.push_back(COMPILER_EXE_PATH);
args.push_back(bcFileName);
args.push_back(nullptr);
return args;
}
void writeBytes(const char *filename, const char *bytes, size_t size) {
std::ofstream ofs(filename, std::ios::binary);
ofs.write(bytes, size);
ofs.close();
}
std::vector<uint32_t> readWords(const char *filename) {
std::ifstream ifs(filename, std::ios::binary);
ifs.seekg(0, ifs.end);
int length = ifs.tellg();
ifs.seekg(0, ifs.beg);
rsAssert(((length & 3) == 0) && "File size expected to be multiples of 4");
std::vector<uint32_t> spvWords(length / sizeof(uint32_t));
ifs.read((char *)(spvWords.data()), length);
ifs.close();
return spvWords;
}
std::vector<uint32_t> compileBitcode(const char *resName, const char *cacheDir,
const char *bitcode, size_t bitcodeSize) {
rsAssert(bitcode && bitcodeSize);
// TODO: Cache the generated code
std::string bcFileName(cacheDir);
bcFileName.append("/");
bcFileName.append(resName);
bcFileName.append(".bc");
writeBytes(bcFileName.c_str(), bitcode, bitcodeSize);
auto args = setCompilerArgs(bcFileName.c_str(), cacheDir);
if (!rsuExecuteCommand(COMPILER_EXE_PATH, args.size() - 1, args.data())) {
ALOGE("compiler command line failed");
return std::vector<uint32_t>();
}
ALOGV("compiler command line succeeded");
std::string spvFileName(cacheDir);
spvFileName.append("/");
spvFileName.append(resName);
spvFileName.append(".spv");
return readWords(spvFileName.c_str());
}
} // anonymous namespace
bool RSoVScript::isScriptCpuBacked(const Script *s) {
return s->mHal.info.mVersionMinor == CPU_SCRIPT_MAGIC_NUMBER;
}
void RSoVScript::initScriptOnCpu(Script *s, RsdCpuReference::CpuScript *cs) {
s->mHal.drv = cs;
s->mHal.info.mVersionMajor = 0; // Unused. Don't care.
s->mHal.info.mVersionMinor = CPU_SCRIPT_MAGIC_NUMBER;
}
void RSoVScript::initScriptOnRSoV(Script *s, RSoVScript *rsovScript) {
s->mHal.drv = rsovScript;
s->mHal.info.mVersionMajor = 0; // Unused. Don't care.
s->mHal.info.mVersionMinor = 0;
}
RSoVScript::RSoVScript(RSoVContext *context, std::vector<uint32_t> &&spvWords,
bcinfo::MetadataExtractor *ME)
: mRSoV(context),
mDevice(context->getDevice()),
mSPIRVWords(std::move(spvWords)),
mME(ME) {}
RSoVScript::~RSoVScript() {
delete mCpuScript;
delete mME;
// TODO: destroy shader
}
void RSoVScript::populateScript(Script *) {
// TODO: implement this
}
void RSoVScript::invokeFunction(uint32_t slot, const void *params,
size_t paramLength) {
getCpuScript()->invokeFunction(slot, params, paramLength);
}
int RSoVScript::invokeRoot() { return getCpuScript()->invokeRoot(); }
void RSoVScript::invokeForEach(uint32_t slot, const Allocation **ains,
uint32_t inLen, Allocation *aout,
const void *usr, uint32_t usrLen,
const RsScriptCall *sc) {
// TODO: Handle kernel without input Allocation
rsAssert(ains);
std::vector<RSoVAllocation *> inputAllocations(inLen);
for (uint32_t i = 0; i < inLen; ++i) {
inputAllocations[i] = static_cast<RSoVAllocation *>(ains[i]->mHal.drv);
}
RSoVAllocation *outputAllocation =
static_cast<RSoVAllocation *>(aout->mHal.drv);
runForEach(slot, inLen, inputAllocations, outputAllocation);
}
void RSoVScript::invokeReduce(uint32_t slot, const Allocation **ains,
uint32_t inLen, Allocation *aout,
const RsScriptCall *sc) {
getCpuScript()->invokeReduce(slot, ains, inLen, aout, sc);
}
void RSoVScript::invokeInit() {
// TODO: implement this
}
void RSoVScript::invokeFreeChildren() {
// TODO: implement this
}
void RSoVScript::setGlobalVar(uint32_t slot, const void *data,
size_t dataLength) {
// TODO: implement this
}
void RSoVScript::getGlobalVar(uint32_t slot, void *data, size_t dataLength) {
// TODO: implement this
}
void RSoVScript::setGlobalVarWithElemDims(uint32_t slot, const void *data,
size_t dataLength, const Element *e,
const uint32_t *dims,
size_t dimLength) {
// TODO: implement this
}
void RSoVScript::setGlobalBind(uint32_t slot, Allocation *data) {
// TODO: implement this
}
void RSoVScript::setGlobalObj(uint32_t slot, ObjectBase *obj) {
// TODO: implement this
}
Allocation *RSoVScript::getAllocationForPointer(const void *ptr) const {
// TODO: implement this
return nullptr;
}
int RSoVScript::getGlobalEntries() const {
// TODO: implement this
return 0;
}
const char *RSoVScript::getGlobalName(int i) const {
// TODO: implement this
return nullptr;
}
const void *RSoVScript::getGlobalAddress(int i) const {
// TODO: implement this
return nullptr;
}
size_t RSoVScript::getGlobalSize(int i) const {
// TODO: implement this
return 0;
}
uint32_t RSoVScript::getGlobalProperties(int i) const {
// TODO: implement this
return 0;
}
void RSoVScript::InitDescriptorAndPipelineLayouts(uint32_t inLen) {
// TODO: global variables
// TODO: kernels with zero output allocations
std::vector<VkDescriptorSetLayoutBinding> layout_bindings{
{
// for the output allocation
.binding = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
};
// initialize descriptors for input allocations
for (uint32_t i = 0; i < inLen; ++i) {
layout_bindings.push_back({
.binding = i + 2, // input allocations start from bining #2
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
});
}
VkDescriptorSetLayoutCreateInfo descriptor_layout = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.bindingCount = static_cast<uint32_t>(layout_bindings.size()),
.pBindings = layout_bindings.data(),
};
VkResult res;
mDescLayout.resize(NUM_DESCRIPTOR_SETS);
res = vkCreateDescriptorSetLayout(mDevice, &descriptor_layout, NULL,
mDescLayout.data());
if (res != VK_SUCCESS) {
__android_log_print(ANDROID_LOG_ERROR, "ComputeTest",
"vkCreateDescriptorSetLayout() returns %d", res);
}
rsAssert(res == VK_SUCCESS);
/* Now use the descriptor layout to create a pipeline layout */
VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.pushConstantRangeCount = 0,
.pPushConstantRanges = nullptr,
.setLayoutCount = NUM_DESCRIPTOR_SETS,
.pSetLayouts = mDescLayout.data(),
};
res = vkCreatePipelineLayout(mDevice, &pPipelineLayoutCreateInfo, NULL,
&mPipelineLayout);
rsAssert(res == VK_SUCCESS);
ALOGV("%s succeeded.", __FUNCTION__);
}
void RSoVScript::InitShader(uint32_t slot) {
VkResult res;
mShaderStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
mShaderStage.pNext = nullptr;
mShaderStage.pSpecializationInfo = nullptr;
mShaderStage.flags = 0;
mShaderStage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
const char **RSKernelNames = mME->getExportForEachNameList();
size_t RSKernelNum = mME->getExportForEachSignatureCount();
rsAssert(slot < RSKernelNum);
rsAssert(RSKernelNames);
rsAssert(RSKernelNames[slot]);
ALOGV("slot = %d kernel name = %s", slot, RSKernelNames[slot]);
mShaderStage.pName = RSKernelNames[slot];
VkShaderModuleCreateInfo moduleCreateInfo = {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.codeSize = mSPIRVWords.size() * sizeof(unsigned int),
.pCode = mSPIRVWords.data(),
};
res = vkCreateShaderModule(mDevice, &moduleCreateInfo, NULL,
&mShaderStage.module);
rsAssert(res == VK_SUCCESS);
ALOGV("%s succeeded.", __FUNCTION__);
}
void RSoVScript::InitDescriptorPool() {
/* DEPENDS on InitDescriptorAndPipelineLayouts() */
VkResult res;
VkDescriptorPoolSize type_count[] = {
{
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .descriptorCount = 1,
},
{
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .descriptorCount = 1,
},
#ifdef SUPPORT_GLOBAL_VARIABLES
{
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .descriptorCount = 1,
}
#endif
};
VkDescriptorPoolCreateInfo descriptor_pool = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.pNext = nullptr,
.maxSets = 1,
.poolSizeCount = NELEM(type_count),
.pPoolSizes = type_count,
};
res = vkCreateDescriptorPool(mDevice, &descriptor_pool, NULL, &mDescPool);
rsAssert(res == VK_SUCCESS);
ALOGV("%s succeeded.", __FUNCTION__);
}
void RSoVScript::InitDescriptorSet(
const std::vector<RSoVAllocation *> &inputAllocations,
RSoVAllocation *outputAllocation) {
VkResult res;
VkDescriptorSetAllocateInfo alloc_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.pNext = NULL,
.descriptorPool = mDescPool,
.descriptorSetCount = NUM_DESCRIPTOR_SETS,
.pSetLayouts = mDescLayout.data(),
};
mDescSet.resize(NUM_DESCRIPTOR_SETS);
res = vkAllocateDescriptorSets(mDevice, &alloc_info, mDescSet.data());
rsAssert(res == VK_SUCCESS);
// TODO: support for set up the binding(s) of global variables
uint32_t nBindings = inputAllocations.size() + 1; // input + output.
std::vector<VkWriteDescriptorSet> writes{
{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = mDescSet[0],
.dstBinding = 1,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.pBufferInfo = outputAllocation->getBufferInfo(),
},
};
for (uint32_t i = 0; i < inputAllocations.size(); ++i) {
writes.push_back({
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = mDescSet[0],
.dstBinding = 2 + i, // input allocations start from binding #2
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.pBufferInfo = inputAllocations[i]->getBufferInfo(),
});
}
vkUpdateDescriptorSets(mDevice, writes.size(), writes.data(), 0, NULL);
ALOGV("%s succeeded.", __FUNCTION__);
}
void RSoVScript::InitPipeline() {
// DEPENDS on mShaderStage, i.e., InitShader()
VkResult res;
VkComputePipelineCreateInfo pipeline_info = {
.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
.pNext = nullptr,
.layout = mPipelineLayout,
.basePipelineHandle = VK_NULL_HANDLE,
.basePipelineIndex = 0,
.flags = 0,
.stage = mShaderStage,
};
res = vkCreateComputePipelines(mDevice, VK_NULL_HANDLE, 1, &pipeline_info,
NULL, &mComputePipeline);
rsAssert(res == VK_SUCCESS);
ALOGV("%s succeeded.", __FUNCTION__);
}
void RSoVScript::runForEach(
uint32_t slot, uint32_t inLen,
const std::vector<RSoVAllocation *> &inputAllocations,
RSoVAllocation *outputAllocation) {
VkResult res;
InitDescriptorAndPipelineLayouts(inLen);
InitShader(slot);
InitDescriptorPool();
InitDescriptorSet(inputAllocations, outputAllocation);
// InitPipelineCache();
InitPipeline();
VkCommandBuffer cmd;
VkCommandBufferAllocateInfo cmd_info = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.pNext = nullptr,
.commandPool = mRSoV->getCmdPool(),
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1,
};
res = vkAllocateCommandBuffers(mDevice, &cmd_info, &cmd);
rsAssert(res == VK_SUCCESS);
VkCommandBufferBeginInfo cmd_buf_info = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.pNext = nullptr,
.flags = 0,
.pInheritanceInfo = nullptr,
};
res = vkBeginCommandBuffer(cmd, &cmd_buf_info);
rsAssert(res == VK_SUCCESS);
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, mComputePipeline);
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, mPipelineLayout,
0, mDescSet.size(), mDescSet.data(), 0, nullptr);
// Assuming all input allocations are of the same dimensionality
const uint32_t width = inputAllocations[0]->getWidth();
const uint32_t height = rsMax(inputAllocations[0]->getHeight(), 1U);
const uint32_t depth = rsMax(inputAllocations[0]->getDepth(), 1U);
vkCmdDispatch(cmd, width, height, depth);
res = vkEndCommandBuffer(cmd);
assert(res == VK_SUCCESS);
VkSubmitInfo submit_info = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.commandBufferCount = 1,
.pCommandBuffers = &cmd,
};
VkFence fence;
VkFenceCreateInfo fenceInfo = {
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
};
vkCreateFence(mDevice, &fenceInfo, NULL, &fence);
vkQueueSubmit(mRSoV->getQueue(), 1, &submit_info, fence);
// Make sure command buffer is finished
do {
res = vkWaitForFences(mDevice, 1, &fence, VK_TRUE, 100000);
} while (res == VK_TIMEOUT);
rsAssert(res == VK_SUCCESS);
vkDestroyFence(mDevice, fence, NULL);
// TODO: shall we reuse command buffers?
VkCommandBuffer cmd_bufs[] = {cmd};
vkFreeCommandBuffers(mDevice, mRSoV->getCmdPool(), 1, cmd_bufs);
vkDestroyPipeline(mDevice, mComputePipeline, nullptr);
for (int i = 0; i < NUM_DESCRIPTOR_SETS; i++)
vkDestroyDescriptorSetLayout(mDevice, mDescLayout[i], nullptr);
vkDestroyPipelineLayout(mDevice, mPipelineLayout, nullptr);
vkDestroyDescriptorPool(mDevice, mDescPool, nullptr);
vkDestroyShaderModule(mDevice, mShaderStage.module, nullptr);
ALOGV("%s succeeded.", __FUNCTION__);
}
} // namespace rsov
} // namespace renderscript
} // namespace android
using android::renderscript::Allocation;
using android::renderscript::Context;
using android::renderscript::Element;
using android::renderscript::ObjectBase;
using android::renderscript::RsdCpuReference;
using android::renderscript::Script;
using android::renderscript::ScriptC;
using android::renderscript::rs_script;
using android::renderscript::rsov::RSoVContext;
using android::renderscript::rsov::RSoVScript;
using android::renderscript::rsov::compileBitcode;
bool rsovScriptInit(const Context *rsc, ScriptC *script, char const *resName,
char const *cacheDir, uint8_t const *bitcode,
size_t bitcodeSize, uint32_t flags) {
RSoVHal *hal = static_cast<RSoVHal *>(rsc->mHal.drv);
std::unique_ptr<RsdCpuReference::CpuScript> cs(hal->mCpuRef->createScript(
script, resName, cacheDir, bitcode, bitcodeSize, flags));
if (cs == nullptr) {
ALOGE("Failed creating a CPU script %p for %s (%p)", cs.get(), resName,
script);
return false;
}
cs->populateScript(script);
std::unique_ptr<bcinfo::MetadataExtractor> bitcodeMetadata(
new bcinfo::MetadataExtractor((const char *)bitcode, bitcodeSize));
if (!bitcodeMetadata || !bitcodeMetadata->extract()) {
ALOGE("Could not extract metadata from bitcode from %s", resName);
return false;
}
auto spvWords =
compileBitcode(resName, cacheDir, (const char *)bitcode, bitcodeSize);
if (!spvWords.empty()) {
RSoVScript *rsovScript = new RSoVScript(hal->mRSoV, std::move(spvWords),
bitcodeMetadata.release());
if (rsovScript) {
rsovScript->setCpuScript(cs.release());
RSoVScript::initScriptOnRSoV(script, rsovScript);
return true;
}
}
ALOGD("Failed creating an RSoV script for %s", resName);
// Fall back to CPU driver instead
RSoVScript::initScriptOnCpu(script, cs.release());
return true;
}
bool rsovInitIntrinsic(const Context *rsc, Script *s, RsScriptIntrinsicID iid,
Element *e) {
RSoVHal *dc = (RSoVHal *)rsc->mHal.drv;
RsdCpuReference::CpuScript *cs = dc->mCpuRef->createIntrinsic(s, iid, e);
if (cs == nullptr) {
return false;
}
s->mHal.drv = cs;
cs->populateScript(s);
return true;
}
void rsovScriptInvokeForEach(const Context *rsc, Script *s, uint32_t slot,
const Allocation *ain, Allocation *aout,
const void *usr, size_t usrLen,
const RsScriptCall *sc) {
if (ain == nullptr) {
rsovScriptInvokeForEachMulti(rsc, s, slot, nullptr, 0, aout, usr, usrLen,
sc);
} else {
const Allocation *ains[1] = {ain};
rsovScriptInvokeForEachMulti(rsc, s, slot, ains, 1, aout, usr, usrLen, sc);
}
}
void rsovScriptInvokeForEachMulti(const Context *rsc, Script *s, uint32_t slot,
const Allocation **ains, size_t inLen,
Allocation *aout, const void *usr,
size_t usrLen, const RsScriptCall *sc) {
RsdCpuReference::CpuScript *cs = (RsdCpuReference::CpuScript *)s->mHal.drv;
cs->invokeForEach(slot, ains, inLen, aout, usr, usrLen, sc);
}
int rsovScriptInvokeRoot(const Context *dc, Script *s) {
RsdCpuReference::CpuScript *cs = (RsdCpuReference::CpuScript *)s->mHal.drv;
return cs->invokeRoot();
}
void rsovScriptInvokeInit(const Context *dc, Script *s) {
RsdCpuReference::CpuScript *cs = (RsdCpuReference::CpuScript *)s->mHal.drv;
cs->invokeInit();
}
void rsovScriptInvokeFreeChildren(const Context *dc, Script *s) {
RsdCpuReference::CpuScript *cs = (RsdCpuReference::CpuScript *)s->mHal.drv;
cs->invokeFreeChildren();
}
void rsovScriptInvokeFunction(const Context *dc, Script *s, uint32_t slot,
const void *params, size_t paramLength) {
RsdCpuReference::CpuScript *cs = (RsdCpuReference::CpuScript *)s->mHal.drv;
cs->invokeFunction(slot, params, paramLength);
}
void rsovScriptInvokeReduce(const Context *dc, Script *s, uint32_t slot,
const Allocation **ains, size_t inLen,
Allocation *aout, const RsScriptCall *sc) {
RsdCpuReference::CpuScript *cs = (RsdCpuReference::CpuScript *)s->mHal.drv;
cs->invokeReduce(slot, ains, inLen, aout, sc);
}
void rsovScriptSetGlobalVar(const Context *dc, const Script *s, uint32_t slot,
void *data, size_t dataLength) {
RsdCpuReference::CpuScript *cs = (RsdCpuReference::CpuScript *)s->mHal.drv;
cs->setGlobalVar(slot, data, dataLength);
}
void rsovScriptGetGlobalVar(const Context *dc, const Script *s, uint32_t slot,
void *data, size_t dataLength) {
RsdCpuReference::CpuScript *cs = (RsdCpuReference::CpuScript *)s->mHal.drv;
cs->getGlobalVar(slot, data, dataLength);
}
void rsovScriptSetGlobalVarWithElemDims(
const Context *dc, const Script *s, uint32_t slot, void *data,
size_t dataLength, const android::renderscript::Element *elem,
const uint32_t *dims, size_t dimLength) {
RsdCpuReference::CpuScript *cs = (RsdCpuReference::CpuScript *)s->mHal.drv;
cs->setGlobalVarWithElemDims(slot, data, dataLength, elem, dims, dimLength);
}
void rsovScriptSetGlobalBind(const Context *dc, const Script *s, uint32_t slot,
Allocation *data) {
RsdCpuReference::CpuScript *cs = (RsdCpuReference::CpuScript *)s->mHal.drv;
cs->setGlobalBind(slot, data);
}
void rsovScriptSetGlobalObj(const Context *dc, const Script *s, uint32_t slot,
ObjectBase *data) {
RsdCpuReference::CpuScript *cs = (RsdCpuReference::CpuScript *)s->mHal.drv;
cs->setGlobalObj(slot, data);
}
void rsovScriptDestroy(const Context *dc, Script *s) {
RsdCpuReference::CpuScript *cs = (RsdCpuReference::CpuScript *)s->mHal.drv;
delete cs;
s->mHal.drv = nullptr;
}
Allocation *rsovScriptGetAllocationForPointer(
const android::renderscript::Context *dc,
const android::renderscript::Script *sc, const void *ptr) {
RsdCpuReference::CpuScript *cs = (RsdCpuReference::CpuScript *)sc->mHal.drv;
return cs->getAllocationForPointer(ptr);
}
void rsovScriptUpdateCachedObject(const Context *rsc, const Script *script,
rs_script *obj) {
obj->p = script;
#ifdef __LP64__
obj->r = nullptr;
if (script != nullptr) {
obj->v1 = script->mHal.drv;
} else {
obj->v1 = nullptr;
}
obj->v2 = nullptr;
#endif
}