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

	Program:		Range Imaging Toolkit (RITK)
	Module:			ritkCudaContext.cxx
	Version:		$Revision: 2883 $
	Date:			$Date: 2012-04-02 17:01:22 +0200 (Mo, 02 Apr 2012) $

	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

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

#include "ritkCudaContext.h"
#include "ritkManager.h"
#include "ritkOpenGLVisualizationWidget.h"

#include <cuda_gl_interop.h>

#include <iostream>


namespace ritk
{


//----------------------------------------------------------------------------
CudaContext::CudaContext()
{
	/*m_ThreadedWorker = new ThreadedWorker();
	m_ThreadedWorker->start();
	m_ThreadedWorker->setPriority(QThread::TimeCriticalPriority);*/

#if 0

	std::cout << "Initializing CUDA context with threadId=" << QThread::currentThreadId() << std::endl;
	std::cout << "	Context@" << OpenGLVisualizationWidget::GetGLContext() << std::endl;

	// This function returns the best GPU (with maximum GFLOPS)
	int max_gflops_device = 0;
	{
		int device_count = 0;
		cudaGetDeviceCount( &device_count );

		cudaDeviceProp device_properties;
		int max_gflops_device = 0;
		int max_gflops = 0;
		
		int current_device = 0;
		cudaGetDeviceProperties( &device_properties, current_device );
		max_gflops = device_properties.multiProcessorCount * device_properties.clockRate;
		++current_device;

		while( current_device < device_count )
		{
			cudaGetDeviceProperties( &device_properties, current_device );
			int gflops = device_properties.multiProcessorCount * device_properties.clockRate;
			if( gflops > max_gflops )
			{
				max_gflops        = gflops;
				max_gflops_device = current_device;
			}
			++current_device;
		}
	}

	ritkCudaSafeCall(cudaGLSetGLDevice(max_gflops_device));
	ritkCudaCheckError();
	float *dummy;
	ritkCudaSafeCall(cudaMalloc(&dummy,1));
	//cudaError_t test = cudaMalloc(&dummy,1);
	ritkCudaSafeCall(cudaDeviceSynchronize());
	ritkCudaCheckError();
	ritkCudaSafeCall(cudaSetDevice(max_gflops_device));
#endif
}


//----------------------------------------------------------------------------
CudaContext::~CudaContext()
{
	cudaThreadExit();
	if ( m_ThreadedWorker )
	{
		m_ThreadedWorker->Shutdown();
		delete m_ThreadedWorker;
	}
}


//----------------------------------------------------------------------------
void 
CudaContext::ExecuteCudaJob(CudaJob *Job, bool Block)
{
	//std::cout << "CudaContext: Executing job from thread@" << QThread::currentThreadId() << " Context@" << m_ThreadedWorker.GetThreadHandle() << std::endl;

	// Get the acquisition pipeline
	AcquisitionPipeline *AP = Manager::GetInstance().GetAcquisitionPipeline();

	// To avoid recursive block calls
	if ( QThread::currentThreadId() == AP->GetThreadHandle() )
	{
		// Direct execution
		Job->Execute();
	}
	else
	{
		// Lock the job to prevent race conditions
		QMutexLocker locker(Job);

		/// Add the job to the worker
		AP->AddNewJob(Job);

		// Wait for the job to finish if requested
		if ( Block )
			Job->wait(Job);
	}
}


} // end namespace ritk
