Skip to content

Commit 4d12b1c

Browse files
author
Matthieu Hog
committed
created plugin and environement node systems
1 parent cf656b6 commit 4d12b1c

16 files changed

+692
-53
lines changed

README.md

+8
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,14 @@ You can create custom nodes in python and make them available in Meshroom using
6666
In a standard precompiled version of Meshroom, you can also directly add custom nodes in `lib/meshroom/nodes`.
6767
To be recognized by Meshroom, a custom folder with nodes should be a Python module (an `__init__.py` file is needed).
6868

69+
### Plugins
70+
71+
Meshroom supports installing containerised plugins via Docker (with the [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html)) or [Anaconda](https://docs.anaconda.com/free/miniconda/index.html).
72+
73+
To do so, make sure docker or anaconda is installed properly and available from the command line.
74+
Then click on `File > Advanced > Install Plugin From URL` or `File > Advanced > Install Plugin From Local Folder` to begin the installation.
75+
76+
To learn more about using or creating plugins, check the explanations [here](meshroom/plugins/README.md).
6977

7078
## License
7179

meshroom/core/__init__.py

+11-7
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,19 @@
3030
# make a UUID based on the host ID and current time
3131
sessionUid = str(uuid.uuid1())
3232

33-
cacheFolderName = 'MeshroomCache'
34-
defaultCacheFolder = os.environ.get('MESHROOM_CACHE', os.path.join(tempfile.gettempdir(), cacheFolderName))
3533
nodesDesc = {}
3634
submitters = {}
3735
pipelineTemplates = {}
3836

37+
#meshroom paths
38+
meshroomFolder = os.path.dirname(os.path.dirname(__file__))
39+
cacheFolderName = 'MeshroomCache'
40+
defaultCacheFolder = os.environ.get('MESHROOM_CACHE', os.path.join(tempfile.gettempdir(), cacheFolderName))
41+
42+
#plugin paths
43+
pluginsNodesFolder = os.path.join(meshroomFolder, "plugins")
44+
pluginsPipelinesFolder = os.path.join(meshroomFolder, "pipelines")
45+
pluginCatalogFile = os.path.join(meshroomFolder, "plugins", "catalog.json")
3946

4047
def hashValue(value):
4148
""" Hash 'value' using sha1. """
@@ -331,27 +338,24 @@ def loadPipelineTemplates(folder):
331338
pipelineTemplates[os.path.splitext(file)[0]] = os.path.join(folder, file)
332339

333340
def initNodes():
334-
meshroomFolder = os.path.dirname(os.path.dirname(__file__))
335341
additionalNodesPath = os.environ.get("MESHROOM_NODES_PATH", "").split(os.pathsep)
336342
# filter empty strings
337343
additionalNodesPath = [i for i in additionalNodesPath if i]
338-
nodesFolders = [os.path.join(meshroomFolder, 'nodes')] + additionalNodesPath
344+
nodesFolders = [os.path.join(meshroomFolder, 'nodes')] + additionalNodesPath + [pluginsNodesFolder]
339345
for f in nodesFolders:
340346
loadAllNodes(folder=f)
341347

342348
def initSubmitters():
343-
meshroomFolder = os.path.dirname(os.path.dirname(__file__))
344349
subs = loadSubmitters(os.environ.get("MESHROOM_SUBMITTERS_PATH", meshroomFolder), 'submitters')
345350
for sub in subs:
346351
registerSubmitter(sub())
347352

348353
def initPipelines():
349-
meshroomFolder = os.path.dirname(os.path.dirname(__file__))
350354
# Load pipeline templates: check in the default folder and any folder the user might have
351355
# added to the environment variable
352356
additionalPipelinesPath = os.environ.get("MESHROOM_PIPELINE_TEMPLATES_PATH", "").split(os.pathsep)
353357
additionalPipelinesPath = [i for i in additionalPipelinesPath if i]
354-
pipelineTemplatesFolders = [os.path.join(meshroomFolder, 'pipelines')] + additionalPipelinesPath
358+
pipelineTemplatesFolders = [os.path.join(meshroomFolder, 'pipelines')] + additionalPipelinesPath + [pluginsPipelinesFolder]
355359
for f in pipelineTemplatesFolders:
356360
loadPipelineTemplates(f)
357361

meshroom/core/node.py

+51-30
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ class Status(Enum):
5151
KILLED = 5
5252
SUCCESS = 6
5353
INPUT = 7 # special status for input nodes
54+
BUILD = 8
55+
FIRST_RUN = 9
5456

5557

5658
class ExecMode(Enum):
@@ -401,36 +403,55 @@ def process(self, forceCompute=False):
401403
if not forceCompute and self._status.status == Status.SUCCESS:
402404
logging.info("Node chunk already computed: {}".format(self.name))
403405
return
404-
global runningProcesses
405-
runningProcesses[self.name] = self
406-
self._status.initStartCompute()
407-
exceptionStatus = None
408-
startTime = time.time()
409-
self.upgradeStatusTo(Status.RUNNING)
410-
self.statThread = stats.StatisticsThread(self)
411-
self.statThread.start()
412-
try:
413-
self.node.nodeDesc.processChunk(self)
414-
except Exception as e:
415-
if self._status.status != Status.STOPPED:
416-
exceptionStatus = Status.ERROR
417-
raise
418-
except (KeyboardInterrupt, SystemError, GeneratorExit) as e:
419-
exceptionStatus = Status.STOPPED
420-
raise
421-
finally:
422-
self._status.initEndCompute()
423-
self._status.elapsedTime = time.time() - startTime
424-
if exceptionStatus is not None:
425-
self.upgradeStatusTo(exceptionStatus)
426-
logging.info(' - elapsed time: {}'.format(self._status.elapsedTimeStr))
427-
# ask and wait for the stats thread to stop
428-
self.statThread.stopRequest()
429-
self.statThread.join()
430-
self.statistics = stats.Statistics()
431-
del runningProcesses[self.name]
432-
433-
self.upgradeStatusTo(Status.SUCCESS)
406+
407+
#if plugin node and if first call call meshroom_compute inside the env on 'host' so that the processchunk
408+
# of the node will be ran into the env
409+
if hasattr(self.node.nodeDesc, 'envFile') and self._status.status!=Status.FIRST_RUN:
410+
try:
411+
if not self.node.nodeDesc.isBuild():
412+
self.upgradeStatusTo(Status.BUILD)
413+
self.node.nodeDesc.build()
414+
self.upgradeStatusTo(Status.FIRST_RUN)
415+
command = self.node.nodeDesc.getCommandLine(self)
416+
#NOTE: docker returns 0 even if mount fail (it fails on the deamon side)
417+
status = os.system(command)
418+
if status != 0:
419+
raise RuntimeError("Error in node execution")
420+
self.updateStatusFromCache()
421+
except Exception as ex:
422+
self.logger.exception(ex)
423+
self.upgradeStatusTo(Status.ERROR)
424+
else:
425+
global runningProcesses
426+
runningProcesses[self.name] = self
427+
self._status.initStartCompute()
428+
exceptionStatus = None
429+
startTime = time.time()
430+
self.upgradeStatusTo(Status.RUNNING)
431+
self.statThread = stats.StatisticsThread(self)
432+
self.statThread.start()
433+
try:
434+
self.node.nodeDesc.processChunk(self)
435+
except Exception as e:
436+
if self._status.status != Status.STOPPED:
437+
exceptionStatus = Status.ERROR
438+
raise
439+
except (KeyboardInterrupt, SystemError, GeneratorExit) as e:
440+
exceptionStatus = Status.STOPPED
441+
raise
442+
finally:
443+
self._status.initEndCompute()
444+
self._status.elapsedTime = time.time() - startTime
445+
if exceptionStatus is not None:
446+
self.upgradeStatusTo(exceptionStatus)
447+
logging.info(' - elapsed time: {}'.format(self._status.elapsedTimeStr))
448+
# ask and wait for the stats thread to stop
449+
self.statThread.stopRequest()
450+
self.statThread.join()
451+
self.statistics = stats.Statistics()
452+
del runningProcesses[self.name]
453+
454+
self.upgradeStatusTo(Status.SUCCESS)
434455

435456
def stopProcess(self):
436457
if not self.isExtern():

0 commit comments

Comments
 (0)