/*======================================================================================

	Program:		Range Imaging Toolkit (RITK)
	Module:			ritkCudaImportImageContainer.h
	Version:		$Revision: 1761 $
	Date:			$Date: 2011-09-23 17:25:41 +0200 (Fri, 23 Sep 2011) $

	Copyright (c) 2010-2011
	Pattern Recognition Lab, 
    University Erlangen-Nuremberg, Germany
    All rights reserved.

	This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
	See RITKCopyright.txt for more information.

	http://www5.informatik.uni-erlangen.de/research/software/ritk

========================================================================================*/

#ifndef CUDAIMPORTIMAGECONTAINER_H__
#define CUDAIMPORTIMAGECONTAINER_H__

#include "ritkCuda.h"


#include "ritkGenericImportImageContainer.h"

#include "ritkCudaContext.h"


namespace ritk
{

/**	@ingroup	Cuda
 *	@class		CudaImportImageContainer
 *	@author		Jakob Wasza
 *	@brief		Class extending the GenericImportImageContainer by CUDA GPU memory
 *
 *	@details
 *	This class provides basic interfaces for CUDA GPU memory management in compliance with
 *	the GenericImportImageContainer concept.  As there is no "single" CUDA memory type
 *	(there exists regular, pitched and array memory) this class does not provide any
 *	direct methods for allocating/deallocating or synchronization. This is the task
 *	of subclasses.
 */
template<typename TElementIdentifier, typename TElement>
class CudaImportImageContainer : public GenericImportImageContainer<TElementIdentifier,TElement>
{
public:
	/**	@name	Std typedefs */
	//@{
	typedef CudaImportImageContainer									Self;
	typedef GenericImportImageContainer<TElementIdentifier,TElement>	Superclass;
	typedef itk::SmartPointer<Self>										Pointer;
	typedef itk::SmartPointer<const Self>								ConstPointer;
	//@}

	/// For convenience
	//@{
	typedef Superclass										GenericImportImageContainerType;
	typedef typename Superclass::ImportImageContainerType	ImportImageContainerType;
	//@}

	/**	@name	Template parameters */
	//@{
	typedef TElementIdentifier  ElementIdentifier;
	typedef TElement            Element;
	//@}

	//@}

	/// Object creation
	itkNewMacro(Self);

	/// RTTI
	itkTypeMacro(CudaImportImageContainer, GenericImportImageContainer);

	/**	@name 2-D size of the container */
	//@{

	/**	@brief	Set the 2-D size of the container
	 *	@param	width	the width of the container in pixel
	 *	@param	height	the height of the container in pixel
	 *
	 *	@details
	 *	In contrast to a regular itk::ImportImageContainer, exact knowledge of the 2-D size of a container is important
	 *	for CUDA memory. This is due to the different memory types available in CUDA (e.g. pitched memory or arrays).
	 */
	virtual void SetContainerSize(unsigned int width, unsigned int height);

	/// Get the size of the container
	virtual void GetContainerSize(unsigned int *width, unsigned int *height) const;
	/// Get the size of the container
	virtual const unsigned int *GetContainerSize() const;
	
	//@}

	/**	@name	Host/device synchronization. Is delegated to the superclass */
	//@{
	virtual void SynchronizeDevice();
	virtual void SynchronizeHost();
	//@}

	/**	@name	Deep copy */
	//@{

	/**	@brief	Deep copy from a container
	 *	@param	Container	The container providing the source
	 *
	 *	@details
	 *	Copy data from an itk::ImportImageContainer, however, this class' implementation only addresses copying host memory.
	 *	If the argument can be casted to a CudaImportImageContainer copying is tailored to page-locked memory and copying will
	 *	performed if and only if the host time stamp is later or equal than the device time stamp. Copying device memory has to be provided
	 *	by subclasses. If the argument cannot be casted to a CudaImportImageContainer, this method will simply call the Superclass'
	 *	implementation. In either case, memory allocation will be performed. \n
	 *	Note that in case of equal timestamps subclasses have to set them properly as this is NOT supported in the baseclass' implementation.
	 */
	virtual void DeepCopy(ImportImageContainerType *Container);

	/**	@brief	Deep copy from a container
	 *	@param	Container	The container providing the source
	 *
	 *	@details
	 *	This method will simply remove the const qualifier (const_cast) of the input argument and pass it to the non-const DeepCopy(ImportImageContainerType *) method.
	 *	Note that a non-const input is necessary due to the synchronization mechanism. However, it is granted, that the memory (host or device) that is up-to-data
	 *	will NOT be modified!
	 */
	virtual void DeepCopy(const ImportImageContainerType *Container);

	/**	@name Get elapsed times (in ms) for the last call*/
	//@{
	void EnableTiming(bool b);
	float GetDeepCopyTime();
	float GetHostToDeviceTime();
	float GetDeviceToHostTime();
	//@}

	//@}


protected:
	/// Constructor
	CudaImportImageContainer();
	/// Destructor
	virtual ~CudaImportImageContainer();

	/// Print
	virtual void PrintSelf(std::ostream& os, itk::Indent indent) const;

	/**	@brief	Allocate the buffer
	 *	@param	size	The size in elements to allocate
	 *
	 *	@details
	 *	This method will allocate the buffer. Calling this method allocates
	 *	the host memory using cudaHostAlloc() and not using an ordinary
	 *	new or malloc call. This is due to the page-locked performance gain
	 *	when transfering from host to device and vice versa. \n
	 *	Note that this method completely overrides any Superclass' implementation.
	 *	However, the host memory is accessible as usual. \n
	 *	Note that calling this method will NOT perform any device allocation: this is
	 *	the task of a subclass' implementation.
	 *	@sa DeallocateManagedMemory
	 */
	virtual Element* AllocateElements(ElementIdentifier size) const;

	/**	@brief	Free the buffer
	 *
	 *	@details
	 *	This method will free the buffer previously allocated using 
	 *	CudaImportImageContainer::AllocateElements(). In fact, calling this method
	 *	performs a call to cudaFreeHost() to  m_ImportPointer declared in itk::ImportImageContainer.
	 *	This is in compliance with the standard implementation as can be found in itk::ImportImageContainer. \n
	 *	Note that calling this method won't perform any device de-allocation at all: this is the task
	 *	of subclasses.
	 *	@sa AllocateElements
	 */
	virtual void DeallocateManagedMemory();

	/**	@name	Methods that have to be executed in the global CUDA context */
	//@{
	mutable ElementIdentifier m_RequestedAllocSize;
	Element *m_HostMem;
	CudaConstMethodMacro(CudaImportImageContainer, AllocateCudaMemory);
	CudaMethodMacro(CudaImportImageContainer, DeallocateCudaMemory);
	Self *m_TmpCudaContainer;
	CudaMethodMacro(CudaImportImageContainer, DeepCopyCudaMemory);
	//@}

protected:
	/**	@name Events for timing. 0=DeepCopy 1=HostToDevice 2=DeviceToHost */
	//@{
	cudaEvent_t m_StartEvents[3];
	cudaEvent_t m_StopEvents[3];
	//@}

	/// The 2-D size of the container
	unsigned int m_ContainerSize[2];

private:
	/// purposely not implemented
	CudaImportImageContainer(const Self&); 
	/// purposely not implemented
	void operator=(const Self&);
};

/** @name Template instantiations */
//@{
typedef CudaImportImageContainer<unsigned long,float> CudaImportImageContainerF;
typedef CudaImportImageContainer<unsigned long, itk::Vector<float,3> > CudaImportImageContainerF3;
//@}

} // ritk


#endif // CUDAIMPORTIMAGECONTAINER_H__
