Skip to content
Snippets Groups Projects
Select Git revision
  • ij-rand-ellipses
  • master default
  • ij-opencv
  • ij-replicates
  • gmm-thread
5 results

readme.txt

Blame
  • ScriptCommandImpl.h 10.63 KiB
    #pragma once
    
    #include <cstddef>
    #include <tuple>
    
    #include "ScriptCommand.h"
    #include "ScopedProcessMutex.h"
    
    #include "HydraConfig.h"
    
    #include "mph/tuple_helpers.h"
    
    template <typename Derived, typename ConverterType>
    class ScriptCommandImpl: public ScriptCommand
    {
    	template <typename T>
    	struct AssertProcessFunc
    	{
    		template <typename... Args> static void run(Args&&...) {}
    
    		// This condition (!is_same<T,T> is always false) forces waiting for dependent type instantiation
    		static_assert(!std::is_same<T, T>::value, "HIP_COMPILE: Overload ::execute or ::process<OutT,InT> method in script command subclass, or define script command using SCR_CMD instead of SCR_CMD_NOPROC.");
    	};
    
    	using ProcessFunc = AssertProcessFunc<Derived>;
    public:
    	// Argument load/conversion access types
    	using ArgConverter = ConverterType;
    	using ArgError = typename ArgConverter::ArgError;
    
    	// Script/converted argument types from arg parser
    	using ScriptTypes = typename ArgConverter::ScriptTypes;
    	using ArgTypes = typename ArgConverter::ArgTypes;
    
    	// Pointers to arguments
    	using ScriptPtrs = typename ArgConverter::ScriptPtrs;
    	using ArgPtrs = typename ArgConverter::ArgPtrs;
    
    	// Selectors
    	using OptionalSel = typename ArgConverter::OptionalSel;
    	using DeferredSel = typename ArgConverter::DeferredSel;
    	using DeferredOutSel = typename ArgConverter::DeferredOutSel;
    	using DeferredOutImSel = typename ArgConverter::DeferredOutImSel;
    	using DeferredInOptSel = typename ArgConverter::DeferredInOptSel;
    	using NondeferredSel = typename ArgConverter::NondeferredSel;
    	using NondeferOutSel = typename ArgConverter::NondeferOutSel;
    	using NondeferInOptSel = typename ArgConverter::NondeferInOptSel;
    
    	// Deferred concrete-types
    	template <typename OutT, typename InT>
    	using ConcreteArgTypes = typename ArgConverter::template ConcreteArgTypes<OutT, InT>;
    
    	template <typename InT>
    	using OutMap = typename ArgConverter::template OutMap<InT>;
    
    public:
    	/////////
    	// These are the four interface functions that are registered in the m_commands FuncPtrs list
    	// (dispatch, usage, help, info)
    
    	// Script engine-dependent function to dispatch parameters
    	inline static SCR_DISPATCH_FUNC_DEF(dispatch)
    
    		inline static SCR_HELP_FUNC_DECL(help)
    	{
    		if ( Derived::helpStr.size() > 0 )
    			return Derived::usage() + "\n\n" + std::string(Derived::helpStr);
    		else
    			return Derived::usage();
    	}
    
    	inline static SCR_USAGE_FUNC_DECL(usage)
    	{
    		// TODO: Fix the output wrapping here
    		return usageOutput(ArgConverter::outargstr())
    			+ moduleName() + "." + Derived::commandName()
    			+ "("+ ArgConverter::inoptargstr() + ")";
    	}
    
    	inline static SCR_INFO_FUNC_DECL(info)
    	{
    		command = Derived::commandName();
    		help = Derived::help();
    		outArgs = ArgConverter::outargstr();
    		inArgs = ArgConverter::inoptargstr();
    	}
    
    private:
    	inline static std::string usageOutput(const std::string& outStr)
    	{
    		if ( outStr.empty() )
    			return outStr;
    		else
    			return std::string("[") + outStr + "] = ";
    	}
    
    public:
    
    	// Non-overloadable - Handles argument conversion and dispatches to execute command
    	// NOTE: default execute command checks for deferred types and passes to
    	//   the templated process function.
    	template <typename... ScriptInterface>
    	static void convert_dispatch(ScriptInterface&&... scriptioArgs)
    	{
    		try
    		{
    			// Subset of arguments that have concrete type (non-deferred)
    			using ConcreteArgs = typename NondeferredSel::template type<ArgTypes>;
    
    			// TODO: Specialize output types for python to make sure memory is cleaned up
    			// Points to the C++ arguments converted from script types
    			//  NOTE: These are partially converted as the deferred arguments are converted in ::process<>
    			ArgPtrs convertedPtrs;
    
    			// Memory for script objects corresponding to arguments
    			// NOTE: These local objects are necessary for temporary ownership of non-deferred Python outputs
    			ScriptTypes scriptObjects;
    			ScriptPtrs scriptPtrs = mph::tuple_addr_of(scriptObjects);
    
    			// Load script pointers from script engine inputs
    			ArgConverter::load(scriptPtrs, std::forward<ScriptInterface>(scriptioArgs)...);
    
    			// NOTE: Requires that args are default-constructible types
    			ConcreteArgs convertedArgs;
    
    			// Hook up convertedPtrs (non-deferred point to concreteArgs, deferred same as scriptPtrs)
    			DeferredSel::select(convertedPtrs) = DeferredSel::select(scriptPtrs);
    			NondeferredSel::select(convertedPtrs) = mph::tuple_addr_of(convertedArgs);
    
    			// Load default values for optional arguments (can't be deferred)
    			ArgConverter::setOptionalDefaults(convertedPtrs);
    
    			// Convert non-deferred inputs to appropriate arg types
    			ArgConverter::convertSelected(convertedPtrs, scriptPtrs, NondeferInOptSel{});
    
    			// TODO: Figure out how to solve the dims inference problem
    			// TODO: Currently no support for creating non-deferred output images
    			// Create non-deferred outputs
    			//ArgConverter::createOutIm(convertedPtrs, scriptPtrs, dims, NondeferOutImSel{});
    
    			// Run backend cuda filter using default execute() -> process<OutT,InT>()
    			// or run overloaded ::execute or ::process<OutT,InT> functions
    			exec_dispatch(mph::tuple_deref(convertedPtrs));
    
    			// Convert outputs to script types
    			ArgConverter::convertSelected(scriptPtrs, convertedPtrs, NondeferOutSel{});
    
    			// Load all outputs into script output structure (Necessary for Python)
    			ArgConverter::store(scriptPtrs, std::forward<ScriptInterface>(scriptioArgs)...);
    		}
    		catch ( ArgError& ae )
    		{
    			Script::errorMsg(ae.what());
    		}
    		catch ( std::exception& e )
    		{
    			std::string msg("Internal error: ");
    			msg += e.what();
    
    			Script::errorMsg(msg.c_str());
    		}
    	}
    
    private:
    	template <typename... Args>
    	static void exec_dispatch(std::tuple<Args...> ioArgs)
    	{
    		Derived::exec_dispatch_impl(ioArgs, mph::make_index_sequence<sizeof... (Args)>());
    	}
    
    	template <typename... Args, size_t... Is>
    	static void exec_dispatch_impl(std::tuple<Args...> ioArgs, mph::index_sequence<Is...>)
    	{
    		// TODO: MRW - Should all execute() routines dispatch through SCOPED_PROCESS_MUTEX since it can be disabled now?
    		Derived::execute(std::forward<Args>(std::get<Is>(ioArgs))...);
    	}
    
    private:
    	/////////////////////////
    	// execute - (Static-overloadable)
    	//   Default execute function dispatches to image-type templated
    	//   process<OutT,InT>(Args...) function
    	//
    	//   NOTE: Main purpose of default execute() is to check config and
    	//     dispatch to exec_internal()
    	/////////////////////////
    	template <typename... Args>
    	static void execute(Args&&... args)
    	{
    		if ( HydraConfig::useProcessMutex() )
    		{
    			// Use a scoped process-level mutex to run only a single GPU kernel at a time
    			// TODO: Figure out a scheduling system for multi-process HIP calls
    			SCOPED_PROCESS_MUTEX(hip_cmd_gpu_);
    			Derived::exec_internal(std::forward<Args>(args)...);
    		}
    		else
    		{
    			Derived::exec_internal(std::forward<Args>(args)...);
    		}
    	}
    
    	/////////////////////////
    	// process - (Static-overloadable)
    	//   Default process<OutT,InT> function creates deferred io and dispatches
    	//   to the class-specified cuda processing function
    	/////////////////////////
    	template <typename OutType, typename InType, typename... Args>
    	static void process(Args&&... args)
    	{
    		// Deferred types (Can now be fully defined)
    		using DeferredArgs = ConcreteArgTypes<OutType, InType>;
    		using DeferredPtrs = typename mph::tuple_ptr_t<DeferredArgs>;
    
    		using DeferredInArgs = typename DeferredInOptSel::template type<DeferredArgs>;
    		using DeferredOutArgs = typename DeferredOutSel::template type<DeferredArgs>;
    
    		auto argRefs = std::tie(args...);
    		ArgPtrs argPtrs = mph::tuple_addr_of(argRefs);
    
    		// Pointers to fully-converted arguments to be passed (by reference) to the cuda processing function
    		DeferredPtrs convertedPtrs;
    
    		// Storage for deferred in/out types
    		DeferredInArgs concreteInArgs;
    		DeferredOutArgs concreteOutArgs;
    
    		// Hook up convertedPtrs all non-deferred are already converted, deferred get hooked locals
    		NondeferredSel::select(convertedPtrs) = NondeferredSel::select(argPtrs);
    		DeferredInOptSel::select(convertedPtrs) = mph::tuple_addr_of(concreteInArgs);
    		DeferredOutSel::select(convertedPtrs) = mph::tuple_addr_of(concreteOutArgs);
    
    		// Convert deferred inputs to appropriate arg types
    		ArgConverter::convertSelected(convertedPtrs, argPtrs, DeferredInOptSel{});
    
    		// Create imageref outputs
    		// TODO: Better image dims inference
    		Script::DimInfo dimInfo = ArgConverter::getInputDimInfo(argRefs);
    		ArgConverter::createOutImRefs(convertedPtrs, argPtrs, dimInfo);
    
    		// Run backend cuda filter
    		run_dispatch(mph::tuple_deref(convertedPtrs));
    
    		// Convert deferred outputs to script types
    		ArgConverter::convertSelected(argPtrs, convertedPtrs, DeferredOutSel{});
    	}
    
    	/////////////////////////
    	// exec_internal - (Not overloadable)
    	//   Dispatch to processing function:
    	//   process<OutT,inT>()
    	/////////////////////////
    	template <typename... Args>
    	static void exec_internal(Args&&... args)
    	{
    		static_assert(ArgConverter::has_deferred_image_inputs(), "HIP_COMPILE: Argument layout has no dynamic image inputs. Please overload default ::execute() function!");
    
    		Script::IdType type = ArgConverter::getInputType(std::forward<Args>(args)...);
    
    		if (type == Script::TypeToIdMap<bool>::typeId)
    		{
    			Derived::template process<OutMap<bool>, bool>(std::forward<Args>(args)...);
    		}
    		else if (type == Script::TypeToIdMap<uint8_t>::typeId)
    		{
    			Derived::template process<OutMap<uint8_t>, uint8_t>(std::forward<Args>(args)...);
    		}
    		else if (type == Script::TypeToIdMap<uint16_t>::typeId)
    		{
    			Derived::template process<OutMap<uint16_t>, uint16_t>(std::forward<Args>(args)...);
    		}
    		else if (type == Script::TypeToIdMap<int16_t>::typeId)
    		{
    			Derived::template process<OutMap<int16_t>, int16_t>(std::forward<Args>(args)...);
    		}
    		else if (type == Script::TypeToIdMap<uint32_t>::typeId)
    		{
    			Derived::template process<OutMap<uint32_t>, uint32_t>(std::forward<Args>(args)...);
    		}
    		else if (type == Script::TypeToIdMap<int32_t>::typeId)
    		{
    			Derived::template process<OutMap<int32_t>, int32_t>(std::forward<Args>(args)...);
    		}
    		else if (type == Script::TypeToIdMap<float>::typeId)
    		{
    			Derived::template process<OutMap<float>, float>(std::forward<Args>(args)...);
    		}
    		else if (type == Script::TypeToIdMap<double>::typeId)
    		{
    			Derived::template process<OutMap<double>, double>(std::forward<Args>(args)...);
    		}
    		else
    		{
    			throw ArgError("Image type unsupported (%x)", type);
    		}
    	}
    
    
    	template <typename... Args>
    	static void run_dispatch(std::tuple<Args...> runArgs)
    	{
    		Derived::run_dispatch_impl(runArgs, mph::make_index_sequence<sizeof... (Args)>());
    	}
    
    	template <typename... Args, size_t... Is>
    	static void run_dispatch_impl(std::tuple<Args...> runArgs, mph::index_sequence<Is...>)
    	{
    		Derived::ProcessFunc::run(std::forward<Args>(std::get<Is>(runArgs))...);
    	}
    };