1 /*
2 * Copyright (C) 2011-2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "rsdCore.h"
18 #include "rsdBcc.h"
19 #include "rsdRuntime.h"
20
21 #include <bcinfo/MetadataExtractor.h>
22
23 #include "rsContext.h"
24 #include "rsElement.h"
25 #include "rsScriptC.h"
26
27 #include "utils/Timers.h"
28 #include "utils/StopWatch.h"
29
30 using namespace android;
31 using namespace android::renderscript;
32
33 struct DrvScript {
34 int (*mRoot)();
35 int (*mRootExpand)();
36 void (*mInit)();
37 void (*mFreeChildren)();
38
39 BCCScriptRef mBccScript;
40
41 bcinfo::MetadataExtractor *ME;
42
43 InvokeFunc_t *mInvokeFunctions;
44 ForEachFunc_t *mForEachFunctions;
45 void ** mFieldAddress;
46 bool * mFieldIsObject;
47 const uint32_t *mExportForEachSignatureList;
48
49 const uint8_t * mScriptText;
50 uint32_t mScriptTextLength;
51 };
52
53 typedef void (*outer_foreach_t)(
54 const android::renderscript::RsForEachStubParamStruct *,
55 uint32_t x1, uint32_t x2,
56 uint32_t instep, uint32_t outstep);
57
setTLS(Script * sc)58 static Script * setTLS(Script *sc) {
59 ScriptTLSStruct * tls = (ScriptTLSStruct *)pthread_getspecific(rsdgThreadTLSKey);
60 rsAssert(tls);
61 Script *old = tls->mScript;
62 tls->mScript = sc;
63 return old;
64 }
65
66
rsdScriptInit(const Context * rsc,ScriptC * script,char const * resName,char const * cacheDir,uint8_t const * bitcode,size_t bitcodeSize,uint32_t flags)67 bool rsdScriptInit(const Context *rsc,
68 ScriptC *script,
69 char const *resName,
70 char const *cacheDir,
71 uint8_t const *bitcode,
72 size_t bitcodeSize,
73 uint32_t flags) {
74 //ALOGE("rsdScriptCreate %p %p %p %p %i %i %p", rsc, resName, cacheDir, bitcode, bitcodeSize, flags, lookupFunc);
75
76 pthread_mutex_lock(&rsdgInitMutex);
77
78 size_t exportFuncCount = 0;
79 size_t exportVarCount = 0;
80 size_t objectSlotCount = 0;
81 size_t exportForEachSignatureCount = 0;
82
83 const char* coreLib = "/system/lib/libclcore.bc";
84 bcinfo::RSFloatPrecision prec;
85
86 DrvScript *drv = (DrvScript *)calloc(1, sizeof(DrvScript));
87 if (drv == NULL) {
88 goto error;
89 }
90 script->mHal.drv = drv;
91
92 drv->mBccScript = bccCreateScript();
93 script->mHal.info.isThreadable = true;
94 drv->mScriptText = bitcode;
95 drv->mScriptTextLength = bitcodeSize;
96
97
98 drv->ME = new bcinfo::MetadataExtractor((const char*)drv->mScriptText,
99 drv->mScriptTextLength);
100 if (!drv->ME->extract()) {
101 ALOGE("bcinfo: failed to read script metadata");
102 goto error;
103 }
104
105 //ALOGE("mBccScript %p", script->mBccScript);
106
107 if (bccRegisterSymbolCallback(drv->mBccScript, &rsdLookupRuntimeStub, script) != 0) {
108 ALOGE("bcc: FAILS to register symbol callback");
109 goto error;
110 }
111
112 if (bccReadBC(drv->mBccScript,
113 resName,
114 (char const *)drv->mScriptText,
115 drv->mScriptTextLength, 0) != 0) {
116 ALOGE("bcc: FAILS to read bitcode");
117 goto error;
118 }
119
120 // NEON-capable devices can use an accelerated math library for all
121 // reduced precision scripts.
122 #if defined(ARCH_ARM_HAVE_NEON)
123 prec = drv->ME->getRSFloatPrecision();
124 if (prec != bcinfo::RS_FP_Full) {
125 coreLib = "/system/lib/libclcore_neon.bc";
126 }
127 #endif
128
129 if (bccLinkFile(drv->mBccScript, coreLib, 0) != 0) {
130 ALOGE("bcc: FAILS to link bitcode");
131 goto error;
132 }
133
134 if (bccPrepareExecutable(drv->mBccScript, cacheDir, resName, 0) != 0) {
135 ALOGE("bcc: FAILS to prepare executable");
136 goto error;
137 }
138
139 drv->mRoot = reinterpret_cast<int (*)()>(bccGetFuncAddr(drv->mBccScript, "root"));
140 drv->mRootExpand = reinterpret_cast<int (*)()>(bccGetFuncAddr(drv->mBccScript, "root.expand"));
141 drv->mInit = reinterpret_cast<void (*)()>(bccGetFuncAddr(drv->mBccScript, "init"));
142 drv->mFreeChildren = reinterpret_cast<void (*)()>(bccGetFuncAddr(drv->mBccScript, ".rs.dtor"));
143
144 exportFuncCount = drv->ME->getExportFuncCount();
145 if (exportFuncCount > 0) {
146 drv->mInvokeFunctions = (InvokeFunc_t*) calloc(exportFuncCount,
147 sizeof(InvokeFunc_t));
148 bccGetExportFuncList(drv->mBccScript, exportFuncCount,
149 (void **) drv->mInvokeFunctions);
150 } else {
151 drv->mInvokeFunctions = NULL;
152 }
153
154 exportVarCount = drv->ME->getExportVarCount();
155 if (exportVarCount > 0) {
156 drv->mFieldAddress = (void **) calloc(exportVarCount, sizeof(void*));
157 drv->mFieldIsObject = (bool *) calloc(exportVarCount, sizeof(bool));
158 bccGetExportVarList(drv->mBccScript, exportVarCount,
159 (void **) drv->mFieldAddress);
160 } else {
161 drv->mFieldAddress = NULL;
162 drv->mFieldIsObject = NULL;
163 }
164
165 objectSlotCount = drv->ME->getObjectSlotCount();
166 if (objectSlotCount > 0) {
167 const uint32_t *objectSlotList = drv->ME->getObjectSlotList();
168 for (uint32_t ct=0; ct < objectSlotCount; ct++) {
169 drv->mFieldIsObject[objectSlotList[ct]] = true;
170 }
171 }
172
173 exportForEachSignatureCount = drv->ME->getExportForEachSignatureCount();
174 drv->mExportForEachSignatureList = drv->ME->getExportForEachSignatureList();
175 if (exportForEachSignatureCount > 0) {
176 drv->mForEachFunctions =
177 (ForEachFunc_t*) calloc(exportForEachSignatureCount,
178 sizeof(ForEachFunc_t));
179 bccGetExportForEachList(drv->mBccScript, exportForEachSignatureCount,
180 (void **) drv->mForEachFunctions);
181 } else {
182 drv->mForEachFunctions = NULL;
183 }
184
185 // Copy info over to runtime
186 script->mHal.info.exportedFunctionCount = drv->ME->getExportFuncCount();
187 script->mHal.info.exportedVariableCount = drv->ME->getExportVarCount();
188 script->mHal.info.exportedPragmaCount = drv->ME->getPragmaCount();
189 script->mHal.info.exportedPragmaKeyList = drv->ME->getPragmaKeyList();
190 script->mHal.info.exportedPragmaValueList = drv->ME->getPragmaValueList();
191
192 if (drv->mRootExpand) {
193 script->mHal.info.root = drv->mRootExpand;
194 } else {
195 script->mHal.info.root = drv->mRoot;
196 }
197
198 pthread_mutex_unlock(&rsdgInitMutex);
199 return true;
200
201 error:
202
203 pthread_mutex_unlock(&rsdgInitMutex);
204 if (drv->ME) {
205 delete drv->ME;
206 drv->ME = NULL;
207 }
208 free(drv);
209 return false;
210
211 }
212
213 typedef struct {
214 Context *rsc;
215 Script *script;
216 ForEachFunc_t kernel;
217 uint32_t sig;
218 const Allocation * ain;
219 Allocation * aout;
220 const void * usr;
221 size_t usrLen;
222
223 uint32_t mSliceSize;
224 volatile int mSliceNum;
225
226 const uint8_t *ptrIn;
227 uint32_t eStrideIn;
228 uint8_t *ptrOut;
229 uint32_t eStrideOut;
230
231 uint32_t yStrideIn;
232 uint32_t yStrideOut;
233
234 uint32_t xStart;
235 uint32_t xEnd;
236 uint32_t yStart;
237 uint32_t yEnd;
238 uint32_t zStart;
239 uint32_t zEnd;
240 uint32_t arrayStart;
241 uint32_t arrayEnd;
242
243 uint32_t dimX;
244 uint32_t dimY;
245 uint32_t dimZ;
246 uint32_t dimArray;
247 } MTLaunchStruct;
248 typedef void (*rs_t)(const void *, void *, const void *, uint32_t, uint32_t, uint32_t, uint32_t);
249
wc_xy(void * usr,uint32_t idx)250 static void wc_xy(void *usr, uint32_t idx) {
251 MTLaunchStruct *mtls = (MTLaunchStruct *)usr;
252 RsForEachStubParamStruct p;
253 memset(&p, 0, sizeof(p));
254 p.usr = mtls->usr;
255 p.usr_len = mtls->usrLen;
256 RsdHal * dc = (RsdHal *)mtls->rsc->mHal.drv;
257 uint32_t sig = mtls->sig;
258
259 outer_foreach_t fn = (outer_foreach_t) mtls->kernel;
260 while (1) {
261 uint32_t slice = (uint32_t)android_atomic_inc(&mtls->mSliceNum);
262 uint32_t yStart = mtls->yStart + slice * mtls->mSliceSize;
263 uint32_t yEnd = yStart + mtls->mSliceSize;
264 yEnd = rsMin(yEnd, mtls->yEnd);
265 if (yEnd <= yStart) {
266 return;
267 }
268
269 //ALOGE("usr idx %i, x %i,%i y %i,%i", idx, mtls->xStart, mtls->xEnd, yStart, yEnd);
270 //ALOGE("usr ptr in %p, out %p", mtls->ptrIn, mtls->ptrOut);
271 for (p.y = yStart; p.y < yEnd; p.y++) {
272 p.out = mtls->ptrOut + (mtls->yStrideOut * p.y);
273 p.in = mtls->ptrIn + (mtls->yStrideIn * p.y);
274 fn(&p, mtls->xStart, mtls->xEnd, mtls->eStrideIn, mtls->eStrideOut);
275 }
276 }
277 }
278
wc_x(void * usr,uint32_t idx)279 static void wc_x(void *usr, uint32_t idx) {
280 MTLaunchStruct *mtls = (MTLaunchStruct *)usr;
281 RsForEachStubParamStruct p;
282 memset(&p, 0, sizeof(p));
283 p.usr = mtls->usr;
284 p.usr_len = mtls->usrLen;
285 RsdHal * dc = (RsdHal *)mtls->rsc->mHal.drv;
286 uint32_t sig = mtls->sig;
287
288 outer_foreach_t fn = (outer_foreach_t) mtls->kernel;
289 while (1) {
290 uint32_t slice = (uint32_t)android_atomic_inc(&mtls->mSliceNum);
291 uint32_t xStart = mtls->xStart + slice * mtls->mSliceSize;
292 uint32_t xEnd = xStart + mtls->mSliceSize;
293 xEnd = rsMin(xEnd, mtls->xEnd);
294 if (xEnd <= xStart) {
295 return;
296 }
297
298 //ALOGE("usr slice %i idx %i, x %i,%i", slice, idx, xStart, xEnd);
299 //ALOGE("usr ptr in %p, out %p", mtls->ptrIn, mtls->ptrOut);
300
301 p.out = mtls->ptrOut + (mtls->eStrideOut * xStart);
302 p.in = mtls->ptrIn + (mtls->eStrideIn * xStart);
303 fn(&p, xStart, xEnd, mtls->eStrideIn, mtls->eStrideOut);
304 }
305 }
306
rsdScriptInvokeForEach(const Context * rsc,Script * s,uint32_t slot,const Allocation * ain,Allocation * aout,const void * usr,uint32_t usrLen,const RsScriptCall * sc)307 void rsdScriptInvokeForEach(const Context *rsc,
308 Script *s,
309 uint32_t slot,
310 const Allocation * ain,
311 Allocation * aout,
312 const void * usr,
313 uint32_t usrLen,
314 const RsScriptCall *sc) {
315
316 RsdHal * dc = (RsdHal *)rsc->mHal.drv;
317
318 MTLaunchStruct mtls;
319 memset(&mtls, 0, sizeof(mtls));
320
321 DrvScript *drv = (DrvScript *)s->mHal.drv;
322 mtls.kernel = drv->mForEachFunctions[slot];
323 rsAssert(mtls.kernel != NULL);
324 mtls.sig = 0x1f; // temp fix for old apps, full table in slang_rs_export_foreach.cpp
325 if (drv->mExportForEachSignatureList) {
326 mtls.sig = drv->mExportForEachSignatureList[slot];
327 }
328 if (ain) {
329 mtls.dimX = ain->getType()->getDimX();
330 mtls.dimY = ain->getType()->getDimY();
331 mtls.dimZ = ain->getType()->getDimZ();
332 //mtls.dimArray = ain->getType()->getDimArray();
333 } else if (aout) {
334 mtls.dimX = aout->getType()->getDimX();
335 mtls.dimY = aout->getType()->getDimY();
336 mtls.dimZ = aout->getType()->getDimZ();
337 //mtls.dimArray = aout->getType()->getDimArray();
338 } else {
339 rsc->setError(RS_ERROR_BAD_SCRIPT, "rsForEach called with null allocations");
340 return;
341 }
342
343 if (!sc || (sc->xEnd == 0)) {
344 mtls.xEnd = mtls.dimX;
345 } else {
346 rsAssert(sc->xStart < mtls.dimX);
347 rsAssert(sc->xEnd <= mtls.dimX);
348 rsAssert(sc->xStart < sc->xEnd);
349 mtls.xStart = rsMin(mtls.dimX, sc->xStart);
350 mtls.xEnd = rsMin(mtls.dimX, sc->xEnd);
351 if (mtls.xStart >= mtls.xEnd) return;
352 }
353
354 if (!sc || (sc->yEnd == 0)) {
355 mtls.yEnd = mtls.dimY;
356 } else {
357 rsAssert(sc->yStart < mtls.dimY);
358 rsAssert(sc->yEnd <= mtls.dimY);
359 rsAssert(sc->yStart < sc->yEnd);
360 mtls.yStart = rsMin(mtls.dimY, sc->yStart);
361 mtls.yEnd = rsMin(mtls.dimY, sc->yEnd);
362 if (mtls.yStart >= mtls.yEnd) return;
363 }
364
365 mtls.xEnd = rsMax((uint32_t)1, mtls.xEnd);
366 mtls.yEnd = rsMax((uint32_t)1, mtls.yEnd);
367 mtls.zEnd = rsMax((uint32_t)1, mtls.zEnd);
368 mtls.arrayEnd = rsMax((uint32_t)1, mtls.arrayEnd);
369
370 rsAssert(!ain || (ain->getType()->getDimZ() == 0));
371
372 Context *mrsc = (Context *)rsc;
373 Script * oldTLS = setTLS(s);
374
375 mtls.rsc = mrsc;
376 mtls.ain = ain;
377 mtls.aout = aout;
378 mtls.script = s;
379 mtls.usr = usr;
380 mtls.usrLen = usrLen;
381 mtls.mSliceSize = 10;
382 mtls.mSliceNum = 0;
383
384 mtls.ptrIn = NULL;
385 mtls.eStrideIn = 0;
386 if (ain) {
387 mtls.ptrIn = (const uint8_t *)ain->getPtr();
388 mtls.eStrideIn = ain->getType()->getElementSizeBytes();
389 mtls.yStrideIn = ain->mHal.drvState.stride;
390 }
391
392 mtls.ptrOut = NULL;
393 mtls.eStrideOut = 0;
394 if (aout) {
395 mtls.ptrOut = (uint8_t *)aout->getPtr();
396 mtls.eStrideOut = aout->getType()->getElementSizeBytes();
397 mtls.yStrideOut = aout->mHal.drvState.stride;
398 }
399
400 if ((dc->mWorkers.mCount > 1) && s->mHal.info.isThreadable && !dc->mInForEach) {
401 dc->mInForEach = true;
402 if (mtls.dimY > 1) {
403 rsdLaunchThreads(mrsc, wc_xy, &mtls);
404 } else {
405 rsdLaunchThreads(mrsc, wc_x, &mtls);
406 }
407 dc->mInForEach = false;
408
409 //ALOGE("launch 1");
410 } else {
411 RsForEachStubParamStruct p;
412 memset(&p, 0, sizeof(p));
413 p.usr = mtls.usr;
414 p.usr_len = mtls.usrLen;
415 uint32_t sig = mtls.sig;
416
417 //ALOGE("launch 3");
418 outer_foreach_t fn = (outer_foreach_t) mtls.kernel;
419 for (p.ar[0] = mtls.arrayStart; p.ar[0] < mtls.arrayEnd; p.ar[0]++) {
420 for (p.z = mtls.zStart; p.z < mtls.zEnd; p.z++) {
421 for (p.y = mtls.yStart; p.y < mtls.yEnd; p.y++) {
422 uint32_t offset = mtls.dimX * mtls.dimY * mtls.dimZ * p.ar[0] +
423 mtls.dimX * mtls.dimY * p.z +
424 mtls.dimX * p.y;
425 p.out = mtls.ptrOut + (mtls.eStrideOut * offset);
426 p.in = mtls.ptrIn + (mtls.eStrideIn * offset);
427 fn(&p, mtls.xStart, mtls.xEnd, mtls.eStrideIn,
428 mtls.eStrideOut);
429 }
430 }
431 }
432 }
433
434 setTLS(oldTLS);
435 }
436
437
rsdScriptInvokeRoot(const Context * dc,Script * script)438 int rsdScriptInvokeRoot(const Context *dc, Script *script) {
439 DrvScript *drv = (DrvScript *)script->mHal.drv;
440
441 Script * oldTLS = setTLS(script);
442 int ret = drv->mRoot();
443 setTLS(oldTLS);
444
445 return ret;
446 }
447
rsdScriptInvokeInit(const Context * dc,Script * script)448 void rsdScriptInvokeInit(const Context *dc, Script *script) {
449 DrvScript *drv = (DrvScript *)script->mHal.drv;
450
451 if (drv->mInit) {
452 drv->mInit();
453 }
454 }
455
rsdScriptInvokeFreeChildren(const Context * dc,Script * script)456 void rsdScriptInvokeFreeChildren(const Context *dc, Script *script) {
457 DrvScript *drv = (DrvScript *)script->mHal.drv;
458
459 if (drv->mFreeChildren) {
460 drv->mFreeChildren();
461 }
462 }
463
rsdScriptInvokeFunction(const Context * dc,Script * script,uint32_t slot,const void * params,size_t paramLength)464 void rsdScriptInvokeFunction(const Context *dc, Script *script,
465 uint32_t slot,
466 const void *params,
467 size_t paramLength) {
468 DrvScript *drv = (DrvScript *)script->mHal.drv;
469 //ALOGE("invoke %p %p %i %p %i", dc, script, slot, params, paramLength);
470
471 Script * oldTLS = setTLS(script);
472 ((void (*)(const void *, uint32_t))
473 drv->mInvokeFunctions[slot])(params, paramLength);
474 setTLS(oldTLS);
475 }
476
rsdScriptSetGlobalVar(const Context * dc,const Script * script,uint32_t slot,void * data,size_t dataLength)477 void rsdScriptSetGlobalVar(const Context *dc, const Script *script,
478 uint32_t slot, void *data, size_t dataLength) {
479 DrvScript *drv = (DrvScript *)script->mHal.drv;
480 //rsAssert(!script->mFieldIsObject[slot]);
481 //ALOGE("setGlobalVar %p %p %i %p %i", dc, script, slot, data, dataLength);
482
483 int32_t *destPtr = ((int32_t **)drv->mFieldAddress)[slot];
484 if (!destPtr) {
485 //ALOGV("Calling setVar on slot = %i which is null", slot);
486 return;
487 }
488
489 memcpy(destPtr, data, dataLength);
490 }
491
rsdScriptSetGlobalVarWithElemDims(const android::renderscript::Context * dc,const android::renderscript::Script * script,uint32_t slot,void * data,size_t dataLength,const android::renderscript::Element * elem,const size_t * dims,size_t dimLength)492 void rsdScriptSetGlobalVarWithElemDims(
493 const android::renderscript::Context *dc,
494 const android::renderscript::Script *script,
495 uint32_t slot, void *data, size_t dataLength,
496 const android::renderscript::Element *elem,
497 const size_t *dims, size_t dimLength) {
498 DrvScript *drv = (DrvScript *)script->mHal.drv;
499
500 int32_t *destPtr = ((int32_t **)drv->mFieldAddress)[slot];
501 if (!destPtr) {
502 //ALOGV("Calling setVar on slot = %i which is null", slot);
503 return;
504 }
505
506 // We want to look at dimension in terms of integer components,
507 // but dimLength is given in terms of bytes.
508 dimLength /= sizeof(int);
509
510 // Only a single dimension is currently supported.
511 rsAssert(dimLength == 1);
512 if (dimLength == 1) {
513 // First do the increment loop.
514 size_t stride = elem->getSizeBytes();
515 char *cVal = reinterpret_cast<char *>(data);
516 for (size_t i = 0; i < dims[0]; i++) {
517 elem->incRefs(cVal);
518 cVal += stride;
519 }
520
521 // Decrement loop comes after (to prevent race conditions).
522 char *oldVal = reinterpret_cast<char *>(destPtr);
523 for (size_t i = 0; i < dims[0]; i++) {
524 elem->decRefs(oldVal);
525 oldVal += stride;
526 }
527 }
528
529 memcpy(destPtr, data, dataLength);
530 }
531
rsdScriptSetGlobalBind(const Context * dc,const Script * script,uint32_t slot,void * data)532 void rsdScriptSetGlobalBind(const Context *dc, const Script *script, uint32_t slot, void *data) {
533 DrvScript *drv = (DrvScript *)script->mHal.drv;
534 //rsAssert(!script->mFieldIsObject[slot]);
535 //ALOGE("setGlobalBind %p %p %i %p", dc, script, slot, data);
536
537 int32_t *destPtr = ((int32_t **)drv->mFieldAddress)[slot];
538 if (!destPtr) {
539 //ALOGV("Calling setVar on slot = %i which is null", slot);
540 return;
541 }
542
543 memcpy(destPtr, &data, sizeof(void *));
544 }
545
rsdScriptSetGlobalObj(const Context * dc,const Script * script,uint32_t slot,ObjectBase * data)546 void rsdScriptSetGlobalObj(const Context *dc, const Script *script, uint32_t slot, ObjectBase *data) {
547 DrvScript *drv = (DrvScript *)script->mHal.drv;
548 //rsAssert(script->mFieldIsObject[slot]);
549 //ALOGE("setGlobalObj %p %p %i %p", dc, script, slot, data);
550
551 int32_t *destPtr = ((int32_t **)drv->mFieldAddress)[slot];
552 if (!destPtr) {
553 //ALOGV("Calling setVar on slot = %i which is null", slot);
554 return;
555 }
556
557 rsrSetObject(dc, script, (ObjectBase **)destPtr, data);
558 }
559
rsdScriptDestroy(const Context * dc,Script * script)560 void rsdScriptDestroy(const Context *dc, Script *script) {
561 DrvScript *drv = (DrvScript *)script->mHal.drv;
562
563 if (drv->mFieldAddress) {
564 size_t exportVarCount = drv->ME->getExportVarCount();
565 for (size_t ct = 0; ct < exportVarCount; ct++) {
566 if (drv->mFieldIsObject[ct]) {
567 // The field address can be NULL if the script-side has
568 // optimized the corresponding global variable away.
569 if (drv->mFieldAddress[ct]) {
570 rsrClearObject(dc, script, (ObjectBase **)drv->mFieldAddress[ct]);
571 }
572 }
573 }
574 free(drv->mFieldAddress);
575 drv->mFieldAddress = NULL;
576 free(drv->mFieldIsObject);
577 drv->mFieldIsObject = NULL; }
578
579 if (drv->mInvokeFunctions) {
580 free(drv->mInvokeFunctions);
581 drv->mInvokeFunctions = NULL;
582 }
583
584 if (drv->mForEachFunctions) {
585 free(drv->mForEachFunctions);
586 drv->mForEachFunctions = NULL;
587 }
588
589 delete drv->ME;
590 drv->ME = NULL;
591
592 free(drv);
593 script->mHal.drv = NULL;
594
595 }
596
597
598