/**Generates hit data for viewing rays, from the constructed BVH
* @param err Error info
* @return Result of the operation: Success or failure
*/
Result BVHManager::generateContacts(Camera& cam,Errata& err)
{
	//Uploading camera state to GPU
	_deviceCamera.reset(new CLBuffer(_context,sizeof(struct Camera),&cam,CLBufferFlags::ReadOnly));
	
	//Allocating memory for primary contacts
	size_t contactBufSize = cam.resX * cam.resY * sizeof(struct Contact);
	if (_primaryContactsArray)
		_primaryContactsArray->resize(contactBufSize);
	else
		_primaryContactsArray.reset(new CLBuffer(_context,contactBufSize,CLBufferFlags::ReadWrite));
	
	//Getting the launch parameters
	size_t processors, warp;
	if (Success != _context.getMaximalLaunchExecParams(*_contactGenerateKernel,processors,warp,err))
		return Error;

	//Setting kernel args
	try
	{
		SET_KERNEL_ARGS((*_contactGenerateKernel),_deviceCamera->getCLMem(),_bvhNodes->getCLMem(),_bvhLeavesCount,_scene.getDeviceSceneData(),_primaryContactsArray->getCLMem());
	}
	catch (CLInterfaceException e)
	{
		err = Errata(e);
		return Error;
	}

	CLEvent evt;
	evt.reset();
	cl_uint totalWorkItems = closestMultipleTo(cam.resX * cam.resY,warp);
	CLKernelWorkDimension globalDim(1,totalWorkItems);
	CLKernelWorkDimension localDim(1,warp);
	CLKernelExecuteParams contactsKernelExecParams(&globalDim,&localDim,&evt);

	//Execute kernel
	if (Success != _context.enqueueKernel((*_contactGenerateKernel),contactsKernelExecParams,err))
		return Error;

	//Flush and make sure that calculation is over before returning control
	if (Success != _context.flushQueue(err))
		return Error;

	if (Success != evt.wait(err))
		return Error;

	return Success;
}
/**Generates contacts for rays and fills the contacts array
* @param rays The rays to be traced - A device memory buffer object that contains rays as struct Ray
* @param contact The target device memory that will contain the result - For each ray rays[i] the buffer will contain
*                data about its closest intersection with object in the scene as contacts[i]. The contact data will be stored
*                as struct Contact
* @param rayCount The number of rays to trace
* @param err Error info
* @return Result of the operation: Success or failure
*/
Result BVHManager::generateContacts(OpenCLUtils::CLBuffer& rays,OpenCLUtils::CLBuffer& contacts, const unsigned int rayCount, Common::Errata& err)
{
	//Setting kernel args
	try
	{
		SET_KERNEL_ARGS((*_contactGenerateKernel2),rays.getCLMem(),rayCount,_bvhNodes->getCLMem(),_bvhLeavesCount,_scene.getDeviceSceneData(),contacts.getCLMem());
	}
	catch (CLInterfaceException e)
	{
		err = Errata(e);
		return Error;
	}

	//Getting the launch parameters
	size_t processors, warp;
	if (Success != _context.getMaximalLaunchExecParams(*_contactGenerateKernel2,processors,warp,err))
		return Error;

	CLEvent evt;
	evt.reset();
	cl_uint totalWorkItems = closestMultipleTo(rayCount,warp);
	CLKernelWorkDimension globalDim(1,totalWorkItems);
	CLKernelWorkDimension localDim(1,warp);
	CLKernelExecuteParams contactsKernelExecParams(&globalDim,&localDim,&evt);

	//Execute kernel
	if (Success != _context.enqueueKernel((*_contactGenerateKernel2),contactsKernelExecParams,err))
		return Error;

	//Flush and make sure that calculation is over before returning control
	if (Success != _context.flushQueue(err))
		return Error;

	if (Success != evt.wait(err))
		return Error;

	return Success;
}
/**Constructs BVH acceleration structure, according to associated scene state
* @param err Error info
* @return Result of the operation: Success or failure
*/
Result BVHManager::construct(Common::Errata& err)
{

	try
	{
		SET_KERNEL_ARGS((*_mortonCalcKernel),_bvhNodes->getCLMem(),_sortedMortonCodes->getCLMem(),_scene.getDeviceSceneData());
		SET_KERNEL_ARGS((*_radixTreeBuildKernel),_bvhNodes->getCLMem(),_sortedMortonCodes->getCLMem(),_bvhLeavesCount);
		SET_KERNEL_ARGS((*_bbCalcKernel),_bvhNodes->getCLMem(),_nodeVisitCounters->getCLMem(), _bvhLeavesCount);
	}
	catch (CLInterfaceException e)
	{
		err = Errata(e);
		return Error;
	}

	CLEvent evt;
	evt.reset();
	size_t processors, warp;
	if (Success != _context.getMaximalLaunchExecParams(*_mortonCalcKernel,processors,warp,err))
		return Error;

	CL_UINT worksize = closestMultipleTo(_bvhLeavesCount,warp);
	CLKernelWorkDimension globalDim(1,worksize);
	CLKernelWorkDimension localDim(1,warp);
	CLKernelExecuteParams mortonKernelExecParams(&globalDim,&localDim,&evt);

	//1. Morton Codes
	//Execute kernel
	if (Success != _context.enqueueKernel((*_mortonCalcKernel),mortonKernelExecParams,err))
		return Error;

	//Flush and make sure that calculation is over before returning control
	if (Success != _context.flushQueue(err))
		return Error;

	if (Success != evt.wait(err))
		return Error;

	//2.Sort the leaves by Morton Codes
	if (Success != _bitonicSorter->sort(_sortedMortonCodes->getCLMem(),_mortonBufferItems,err))
		return Error;

	//3. Build Radix Tree
	worksize = closestMultipleTo(_bvhLeavesCount-1,warp);
	CLKernelWorkDimension globalDim_Radix(1,worksize);
	CLKernelWorkDimension localDim_Radix(1,warp);
	CLKernelExecuteParams radixKernelExecParams(&globalDim_Radix,&localDim_Radix,&evt);
	//Execute kernel
	if (Success != _context.enqueueKernel((*_radixTreeBuildKernel),radixKernelExecParams,err))
		return Error;

	//Flush and make sure that calculation is over before returning control
	if (Success != _context.flushQueue(err))
		return Error;

	if (Success != evt.wait(err))
		return Error;

	worksize = closestMultipleTo(_bvhLeavesCount,warp);
	CLKernelExecuteParams boundingBoxKernelExecParams(new CLKernelWorkDimension(1,worksize),new CLKernelWorkDimension(1,warp,&evt));
	//Execute kernel
	if (Success != _context.enqueueKernel((*_bbCalcKernel),boundingBoxKernelExecParams,err))
		return Error;

	//Flush and make sure that calculation is over before returning control
	if (Success != _context.flushQueue(err))
		return Error;

	if (Success != evt.wait(err))
		return Error;

	return Success;
}