Skip to content

Commit

Permalink
ENH: Support integrating backend-specific Autoscoper executable
Browse files Browse the repository at this point in the history
Build-system updates:

- Removed support for setting Autoscoper_RENDERING_BACKEND option.
- Introduced an explicit AutoscoperM-OpenCL external project.
- Associated Autoscoper "_DIR" variables with the label "Autoscoper_DIRS"
  allowing to update install and fix-up rules based on the different
  Autoscoper builds.
- Updated external project variables depending on the project name to
  use "${proj}_" prefix instead of the hard-coded "Autoscoper_" one.

AutoscoperM module updates:
- Updated AutoscoperM module to include a combobox for selecting the rendering backend.
  The combobox is dynamically populated based on the available Autoscoper executables
  found in the environment (e.g., autoscoper-CUDA or autoscoper-OpenCL).

Note: The distribution of the CUDA variant of Autoscoper ("autoscoper-CUDA") will be
integrated in subsequent commits.

List of Autoscoper updates:

```
$ git shortlog e14b2da..d476e2c --no-merges
Jean-Christophe Fillion-Robin (1):
      COMP: Add support for appending a suffix to Autoscoper artifacts
```
  • Loading branch information
jcfr committed Dec 17, 2023
1 parent 0aba42e commit 566a540
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 56 deletions.
47 changes: 40 additions & 7 deletions AutoscoperM/AutoscoperM.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ def __init__(self, parent=None):
self.logic = None
self._parameterNode = None
self._updatingGUIFromParameterNode = False
self.autoscoperExecutables = {}

def setup(self):
"""
Expand Down Expand Up @@ -186,10 +187,27 @@ def setup(self):
# NA

# Buttons
self.ui.startAutoscoper.connect("clicked(bool)", self.lookupAndStartAutoscoper)
self.ui.startAutoscoper.connect("clicked(bool)", self.startAutoscoper)
self.ui.closeAutoscoper.connect("clicked(bool)", self.logic.stopAutoscoper)
self.ui.loadConfig.connect("clicked(bool)", self.onLoadConfig)

# Lookup Autoscoper executables
for backend in ["CUDA", "OpenCL"]:
executableName = AutoscoperMWidget.autoscoperExecutableName(backend)
logging.info(f"Looking up '{executableName}' executable")
path = shutil.which(executableName)
if path:
self.autoscoperExecutables[backend] = path
logging.info(f"Found '{path}'")
else:
logging.info("No executable found")

if not self.autoscoperExecutables:
logging.error("Failed to lookup autoscoper executables")

# Available Autoscoper backends
self.ui.autoscoperRenderingBackendComboBox.addItems(list(self.autoscoperExecutables.keys()))

# Sample Data Buttons
self.ui.wristSampleButton.connect("clicked(bool)", lambda: self.onSampleDataButtonClicked("2023-08-01-Wrist"))
self.ui.kneeSampleButton.connect("clicked(bool)", lambda: self.onSampleDataButtonClicked("2023-08-01-Knee"))
Expand Down Expand Up @@ -323,14 +341,29 @@ def updateParameterNodeFromGUI(self, _caller=None, _event=None):

self._parameterNode.EndModify(wasModified)

def lookupAndStartAutoscoper(self):
"""Lookup autoscoper executable and start a new process
@property
def autoscoperExecutableToLaunchBackend(self):
return self.ui.autoscoperRenderingBackendComboBox.currentText

@autoscoperExecutableToLaunchBackend.setter
def autoscoperExecutableToLaunchBackend(self, value):
self.ui.autoscoperRenderingBackendComboBox.currentText = value

@staticmethod
def autoscoperExecutableName(backend=None):
"""Returns the Autoscoper executable name to lookup given a backend name."""
suffix = f"-{backend}" if backend else ""
return f"autoscoper{suffix}"

def startAutoscoper(self):
"""Start a new process using the Autoscoper executable corresponding to the selected backend.
This call waits that the process has been started and returns.
"""
executablePath = shutil.which("autoscoper")
if not executablePath:
logging.error("autoscoper executable not found")
try:
executablePath = self.autoscoperExecutables[self.autoscoperExecutableToLaunchBackend]
except KeyError:
logging.error("Autoscoper executable not found")
return
self.logic.startAutoscoper(executablePath)

Expand All @@ -350,7 +383,7 @@ def loadConfig(self, configPath):
if self.logic.AutoscoperProcess.state() != qt.QProcess.Running and slicer.util.confirmYesNoDisplay(
"Autoscoper is not running. Do you want to start Autoscoper?"
):
self.lookupAndStartAutoscoper()
self.startAutoscoper()

if self.logic.AutoscoperProcess.state() != qt.QProcess.Running:
logging.error("failed to load the Sample Data: Autoscoper is not running. ")
Expand Down
35 changes: 24 additions & 11 deletions AutoscoperM/Resources/UI/AutoscoperM.ui
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,30 @@
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="startAutoscoper">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>Start the Autoscoper executable bundled in the extension.</string>
</property>
<property name="text">
<string>Launch Autoscoper</string>
</property>
</widget>
<layout class="QHBoxLayout" name="launcherAutoscoperHorizontalLayout">
<item>
<widget class="QPushButton" name="startAutoscoper">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Start the Autoscoper executable bundled in the extension.</string>
</property>
<property name="text">
<string>Launch Autoscoper</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="autoscoperRenderingBackendComboBox"/>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="closeAutoscoper">
Expand Down
34 changes: 14 additions & 20 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,7 @@ mark_as_superbuild(GIT_EXECUTABLE)
#-----------------------------------------------------------------------------
# Options

set(_reason "")
if(DEFINED ENV{Autoscoper_RENDERING_BACKEND})
set(_reason " (initialized from env. variable)")
set(_default_backend $ENV{Autoscoper_RENDERING_BACKEND})
elseif(NOT DEFINED Autoscoper_RENDERING_BACKEND)
set(_reason " (initialized from default value)")
set(_default_backend "OpenCL")
endif()

set(Autoscoper_RENDERING_BACKEND "${_default_backend}" CACHE STRING "Backend to use for DRR and radiograph rendering")
set_property(CACHE Autoscoper_RENDERING_BACKEND PROPERTY STRINGS "CUDA" "OpenCL")
message(STATUS "Setting Autoscoper_RENDERING_BACKEND to ${Autoscoper_RENDERING_BACKEND}${_reason}")
# NA

#-----------------------------------------------------------------------------
# SuperBuild setup
Expand All @@ -59,17 +48,22 @@ add_subdirectory(VirtualRadiographGeneration)

#-----------------------------------------------------------------------------
set(EXTENSION_CPACK_INSTALL_CMAKE_PROJECTS)
if(DEFINED Autoscoper_DIR)
list(APPEND EXTENSION_CPACK_INSTALL_CMAKE_PROJECTS "${Autoscoper_DIR};Autoscoper;ALL;/")
endif()
foreach(varname IN LISTS ${SUPERBUILD_TOPLEVEL_PROJECT}_EP_LABEL_Autoscoper_DIRS)
if(DEFINED ${varname})
message(STATUS "Adding install rules for Autoscoper in ${varname}")
list(APPEND EXTENSION_CPACK_INSTALL_CMAKE_PROJECTS "${${varname}};Autoscoper;ALL;/")
endif()
endforeach()
set(${EXTENSION_NAME}_CPACK_INSTALL_CMAKE_PROJECTS "${EXTENSION_CPACK_INSTALL_CMAKE_PROJECTS}" CACHE STRING "List of external projects to install" FORCE)

if(APPLE)
if(APPLE OR TRUE)
set(EXTENSION_FIXUP_BUNDLE_LIBRARY_DIRECTORIES)
set(GLEW_RUNTIME_LIBRARY_DIR "${Autoscoper_DIR}/../Autoscoper-build/GLEW-install/${Slicer_INSTALL_THIRDPARTY_LIB_DIR}")
list(APPEND EXTENSION_FIXUP_BUNDLE_LIBRARY_DIRECTORIES ${GLEW_RUNTIME_LIBRARY_DIR})
set(TIFF_RUNTIME_LIBRARY_DIR "${Autoscoper_DIR}/../Autoscoper-build/TIFF-install/${Slicer_INSTALL_THIRDPARTY_LIB_DIR}")
list(APPEND EXTENSION_FIXUP_BUNDLE_LIBRARY_DIRECTORIES ${TIFF_RUNTIME_LIBRARY_DIR})
foreach(varname IN LISTS ${SUPERBUILD_TOPLEVEL_PROJECT}_EP_LABEL_Autoscoper_DIRS)
set(GLEW_RUNTIME_LIBRARY_DIR "${${varname}}/../GLEW-install/${Slicer_INSTALL_THIRDPARTY_LIB_DIR}")
list(APPEND EXTENSION_FIXUP_BUNDLE_LIBRARY_DIRECTORIES ${GLEW_RUNTIME_LIBRARY_DIR})
set(TIFF_RUNTIME_LIBRARY_DIR "${${varname}}/../TIFF-install/${Slicer_INSTALL_THIRDPARTY_LIB_DIR}")
list(APPEND EXTENSION_FIXUP_BUNDLE_LIBRARY_DIRECTORIES ${TIFF_RUNTIME_LIBRARY_DIR})
endforeach()
set(${EXTENSION_NAME}_FIXUP_BUNDLE_LIBRARY_DIRECTORIES "${EXTENSION_FIXUP_BUNDLE_LIBRARY_DIRECTORIES}" CACHE STRING "List of directories to look up libraries to copy into the application package" FORCE)
endif()

Expand Down
2 changes: 1 addition & 1 deletion SuperBuild.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ set(proj ${SUPERBUILD_TOPLEVEL_PROJECT})

# Project dependencies
set(${proj}_DEPENDS
Autoscoper
Autoscoper-OpenCL
)

ExternalProject_Include_Dependencies(${proj}
Expand Down
3 changes: 3 additions & 0 deletions SuperBuild/External_Autoscoper-OpenCL.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
set(proj Autoscoper-OpenCL)
set(${proj}_RENDERING_BACKEND OpenCL)
include(${CMAKE_CURRENT_LIST_DIR}/External_Autoscoper.cmake)
47 changes: 30 additions & 17 deletions SuperBuild/External_Autoscoper.cmake
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@

set(proj Autoscoper)
# Support redefining "proj" variable to allow reusing this project while
# specifying a different value for ${proj}_RENDERING_BACKEND.
if(NOT DEFINED proj)
set(proj Autoscoper)
endif()

# Set dependency list
set(${proj}_DEPENDS
Expand All @@ -14,8 +18,8 @@ if(${SUPERBUILD_TOPLEVEL_PROJECT}_USE_SYSTEM_${proj})
endif()

# Sanity checks
if(DEFINED Autoscoper_DIR AND NOT EXISTS ${Autoscoper_DIR})
message(FATAL_ERROR "Autoscoper_DIR [${Autoscoper_DIR}] variable is defined but corresponds to nonexistent directory")
if(DEFINED ${proj}_DIR AND NOT EXISTS ${${proj}_DIR})
message(FATAL_ERROR "${proj}_DIR [${${proj}_DIR}] variable is defined but corresponds to nonexistent directory")
endif()

if(NOT DEFINED ${proj}_DIR AND NOT ${SUPERBUILD_TOPLEVEL_PROJECT}_USE_SYSTEM_${proj})
Expand All @@ -31,7 +35,7 @@ if(NOT DEFINED ${proj}_DIR AND NOT ${SUPERBUILD_TOPLEVEL_PROJECT}_USE_SYSTEM_${p

ExternalProject_SetIfNotDefined(
Slicer_${proj}_GIT_TAG
"e14b2da04dd7786c288613cafd11215bbf394027"
"d476e2cbb4fc72a4dea5f4d467676bf9b978d8ce"
QUIET
)

Expand All @@ -43,11 +47,11 @@ if(NOT DEFINED ${proj}_DIR AND NOT ${SUPERBUILD_TOPLEVEL_PROJECT}_USE_SYSTEM_${p
set(Slicer_INSTALL_THIRDPARTY_LIB_DIR ${Slicer_THIRDPARTY_LIB_DIR})
endif()

set(Autoscoper_INSTALL_DEPENDENCIES TRUE)
set(${proj}_INSTALL_DEPENDENCIES TRUE)
if(APPLE)
# Dependency libraries (e.g Glew) will be installed leveraging
# the macOS "fix-up" script.
set(Autoscoper_INSTALL_DEPENDENCIES FALSE)
set(${proj}_INSTALL_DEPENDENCIES FALSE)
endif()

if(APPLE)
Expand All @@ -57,17 +61,17 @@ if(NOT DEFINED ${proj}_DIR AND NOT ${SUPERBUILD_TOPLEVEL_PROJECT}_USE_SYSTEM_${p
)
endif()

if(NOT "${Autoscoper_RENDERING_BACKEND}" MATCHES "^(CUDA|OpenCL)$")
message(FATAL_ERROR "Autoscoper_RENDERING_BACKEND must be set to CUDA or OpenCL")
if(NOT "${${proj}_RENDERING_BACKEND}" MATCHES "^(CUDA|OpenCL)$")
message(FATAL_ERROR "${proj}RENDERING_BACKEND must be set to CUDA or OpenCL")
endif()

if(Autoscoper_RENDERING_BACKEND STREQUAL "OpenCL")
set(Autoscoper_OPENCL_USE_ICD_LOADER TRUE)
if(${proj}_RENDERING_BACKEND STREQUAL "OpenCL")
set(${proj}_OPENCL_USE_ICD_LOADER TRUE)
if(APPLE)
set(Autoscoper_OPENCL_USE_ICD_LOADER FALSE)
set(${proj}_OPENCL_USE_ICD_LOADER FALSE)
endif()
else()
set(Autoscoper_OPENCL_USE_ICD_LOADER FALSE)
set(${proj}_OPENCL_USE_ICD_LOADER FALSE)
endif()

if(UNIX AND NOT APPLE)
Expand All @@ -82,6 +86,11 @@ if(NOT DEFINED ${proj}_DIR AND NOT ${SUPERBUILD_TOPLEVEL_PROJECT}_USE_SYSTEM_${p
)
endif()

if(NOT DEFINED ${proj}_ARTIFACT_SUFFIX)
set(${proj}_ARTIFACT_SUFFIX "-${${proj}_RENDERING_BACKEND}")
endif()
ExternalProject_Message(${proj} "${proj}_ARTIFACT_SUFFIX:${${proj}_ARTIFACT_SUFFIX}")

ExternalProject_Add(${proj}
${${proj}_EP_ARGS}
GIT_REPOSITORY "${Slicer_${proj}_GIT_REPOSITORY}"
Expand All @@ -107,11 +116,12 @@ if(NOT DEFINED ${proj}_DIR AND NOT ${SUPERBUILD_TOPLEVEL_PROJECT}_USE_SYSTEM_${p
# Options
-DAutoscoper_SUPERBUILD:BOOL=ON
-DAutoscoper_CONFIGURE_LAUCHER_SCRIPT:BOOL=OFF
-DAutoscoper_OPENCL_USE_ICD_LOADER:BOOL=${Autoscoper_OPENCL_USE_ICD_LOADER}
-DAutoscoper_INSTALL_DEPENDENCIES:BOOL=${Autoscoper_INSTALL_DEPENDENCIES}
-DAutoscoper_OPENCL_USE_ICD_LOADER:BOOL=${${proj}_OPENCL_USE_ICD_LOADER}
-DAutoscoper_INSTALL_DEPENDENCIES:BOOL=${${proj}_INSTALL_DEPENDENCIES}
-DAutoscoper_INSTALL_Qt_LIBRARIES:BOOL=OFF
-DAutoscoper_INSTALL_SAMPLE_DATA:BOOL=OFF
-DAutoscoper_RENDERING_BACKEND:STRING=${Autoscoper_RENDERING_BACKEND}
-DAutoscoper_RENDERING_BACKEND:STRING=${${proj}_RENDERING_BACKEND}
-DAutoscoper_ARTIFACT_SUFFIX:STRING=${${proj}_ARTIFACT_SUFFIX}
-DQt5_DIR:PATH=${Qt5_DIR}
# Dependencies
# NA
Expand Down Expand Up @@ -157,7 +167,7 @@ if(NOT DEFINED ${proj}_DIR AND NOT ${SUPERBUILD_TOPLEVEL_PROJECT}_USE_SYSTEM_${p
${EP_BINARY_DIR}/GLEW-install/${_lib_subdir} # Glew library
${EP_BINARY_DIR}/TIFF-install/${_lib_subdir} # TIFF library
)
if(Autoscoper_OPENCL_USE_ICD_LOADER)
if(${proj}_OPENCL_USE_ICD_LOADER)
list(APPEND ${proj}_LIBRARY_PATHS_LAUNCHER_BUILD
${EP_BINARY_DIR}/OpenCL-ICD-Loader-build/${_lib_subdir}/${CMAKE_CFG_INTDIR} # OpenCL library
)
Expand Down Expand Up @@ -185,4 +195,7 @@ else()
ExternalProject_Add_Empty(${proj} DEPENDS ${${proj}_DEPENDS})
endif()

mark_as_superbuild(${proj}_DIR:PATH)
mark_as_superbuild(
VARS ${proj}_DIR:PATH
LABELS "Autoscoper_DIRS"
)

0 comments on commit 566a540

Please sign in to comment.