void ExecutionGroup::initExecution()
{
	if (this->m_chunkExecutionStates != NULL) {
		MEM_freeN(this->m_chunkExecutionStates);
	}
	unsigned int index;
	determineNumberOfChunks();

	this->m_chunkExecutionStates = NULL;
	if (this->m_numberOfChunks != 0) {
		this->m_chunkExecutionStates = (ChunkExecutionState *)MEM_mallocN(sizeof(ChunkExecutionState) * this->m_numberOfChunks, __func__);
		for (index = 0; index < this->m_numberOfChunks; index++) {
			this->m_chunkExecutionStates[index] = COM_ES_NOT_SCHEDULED;
		}
	}


	unsigned int maxNumber = 0;

	for (index = 0; index < this->m_operations.size(); index++) {
		NodeOperation *operation = this->m_operations[index];
		if (operation->isReadBufferOperation()) {
			ReadBufferOperation *readOperation = (ReadBufferOperation *)operation;
			this->m_cachedReadOperations.push_back(readOperation);
			maxNumber = max(maxNumber, readOperation->getOffset());
		}
	}
	maxNumber++;
	this->m_cachedMaxReadBufferOffset = maxNumber;

}
void ExecutionGroup::determineDependingMemoryProxies(vector<MemoryProxy *> *memoryProxies)
{
	unsigned int index;
	for (index = 0; index < this->m_cachedReadOperations.size(); index++) {
		ReadBufferOperation *readOperation = (ReadBufferOperation *) this->m_cachedReadOperations[index];
		memoryProxies->push_back(readOperation->getMemoryProxy());
	}
}
void SocketBufferNode::convertToOperations(NodeConverter &converter, const CompositorContext &context) const
{
	NodeOutput *output = this->getOutputSocket(0);
	NodeInput *input = this->getInputSocket(0);
	
	DataType datatype = output->getDataType();
	WriteBufferOperation *writeOperation = new WriteBufferOperation(datatype);
	ReadBufferOperation *readOperation = new ReadBufferOperation(datatype);
	readOperation->setMemoryProxy(writeOperation->getMemoryProxy());
	converter.addOperation(writeOperation);
	converter.addOperation(readOperation);
	
	converter.mapInputSocket(input, writeOperation->getInputSocket(0));
	converter.mapOutputSocket(output, readOperation->getOutputSocket());
}
void NodeOperationBuilder::add_output_buffers(NodeOperation *operation, NodeOperationOutput *output)
{
	/* cache connected sockets, so we can safely remove links first before replacing them */
	OpInputs targets = cache_output_links(output);
	if (targets.empty())
		return;
	
	WriteBufferOperation *writeOperation = NULL;
	for (OpInputs::const_iterator it = targets.begin(); it != targets.end(); ++it) {
		NodeOperationInput *target = *it;
		
		/* try to find existing write buffer operation */
		if (target->getOperation().isWriteBufferOperation()) {
			BLI_assert(writeOperation == NULL); /* there should only be one write op connected */
			writeOperation = (WriteBufferOperation *)(&target->getOperation());
		}
		else {
			/* remove all links to other nodes */
			removeInputLink(target);
		}
	}
	
	/* if no write buffer operation exists yet, create a new one */
	if (!writeOperation) {
		writeOperation = new WriteBufferOperation(operation->getOutputSocket()->getDataType());
		writeOperation->setbNodeTree(m_context->getbNodeTree());
		addOperation(writeOperation);
		
		addLink(output, writeOperation->getInputSocket(0));
	}
	
	writeOperation->readResolutionFromInputSocket();
	
	/* add readbuffer op for every former connected input */
	for (OpInputs::const_iterator it = targets.begin(); it != targets.end(); ++it) {
		NodeOperationInput *target = *it;
		if (&target->getOperation() == writeOperation)
			continue; /* skip existing write op links */
		
		ReadBufferOperation *readoperation = new ReadBufferOperation(operation->getOutputSocket()->getDataType());
		readoperation->setMemoryProxy(writeOperation->getMemoryProxy());
		addOperation(readoperation);
		
		addLink(readoperation->getOutputSocket(), target);
	
		readoperation->readResolutionFromWriteBuffer();
	}
}
void NodeOperationBuilder::add_input_buffers(NodeOperation * /*operation*/,
                                             NodeOperationInput *input)
{
	if (!input->isConnected())
		return;
	
	NodeOperationOutput *output = input->getLink();
	if (output->getOperation().isReadBufferOperation()) {
		/* input is already buffered, no need to add another */
		return;
	}
	
	/* this link will be replaced below */
	removeInputLink(input);
	
	/* check of other end already has write operation, otherwise add a new one */
	WriteBufferOperation *writeoperation = find_attached_write_buffer_operation(output);
	if (!writeoperation) {
		writeoperation = new WriteBufferOperation(output->getDataType());
		writeoperation->setbNodeTree(m_context->getbNodeTree());
		addOperation(writeoperation);
		
		addLink(output, writeoperation->getInputSocket(0));
		
		writeoperation->readResolutionFromInputSocket();
	}
	
	/* add readbuffer op for the input */
	ReadBufferOperation *readoperation = new ReadBufferOperation(output->getDataType());
	readoperation->setMemoryProxy(writeoperation->getMemoryProxy());
	this->addOperation(readoperation);
	
	addLink(readoperation->getOutputSocket(), input);
	
	readoperation->readResolutionFromWriteBuffer();
}
void ExecutionSystem::execute()
{
	const bNodeTree *editingtree = this->m_context.getbNodeTree();
	editingtree->stats_draw(editingtree->sdh, (char *)"Compositing | Initializing execution");

	DebugInfo::execute_started(this);
	
	unsigned int order = 0;
	for (vector<NodeOperation *>::iterator iter = this->m_operations.begin(); iter != this->m_operations.end(); ++iter) {
		NodeOperation *operation = *iter;
		if (operation->isReadBufferOperation()) {
			ReadBufferOperation *readOperation = (ReadBufferOperation *)operation;
			readOperation->setOffset(order);
			order++;
		}
	}
	unsigned int index;

	// First allocale all write buffer
	for (index = 0; index < this->m_operations.size(); index++) {
		NodeOperation *operation = this->m_operations[index];
		if (operation->isWriteBufferOperation()) {
			operation->setbNodeTree(this->m_context.getbNodeTree());
			operation->initExecution();
		}
	}
	// Connect read buffers to their write buffers
	for (index = 0; index < this->m_operations.size(); index++) {
		NodeOperation *operation = this->m_operations[index];
		if (operation->isReadBufferOperation()) {
			ReadBufferOperation *readOperation = (ReadBufferOperation *)operation;
			readOperation->updateMemoryBuffer();
		}
	}
	// initialize other operations
	for (index = 0; index < this->m_operations.size(); index++) {
		NodeOperation *operation = this->m_operations[index];
		if (!operation->isWriteBufferOperation()) {
			operation->setbNodeTree(this->m_context.getbNodeTree());
			operation->initExecution();
		}
	}
	for (index = 0; index < this->m_groups.size(); index++) {
		ExecutionGroup *executionGroup = this->m_groups[index];
		executionGroup->setChunksize(this->m_context.getChunksize());
		executionGroup->initExecution();
	}

	WorkScheduler::start(this->m_context);

	executeGroups(COM_PRIORITY_HIGH);
	if (!this->getContext().isFastCalculation()) {
		executeGroups(COM_PRIORITY_MEDIUM);
		executeGroups(COM_PRIORITY_LOW);
	}

	WorkScheduler::finish();
	WorkScheduler::stop();

	editingtree->stats_draw(editingtree->sdh, (char *)"Compositing | Deinitializing execution");
	for (index = 0; index < this->m_operations.size(); index++) {
		NodeOperation *operation = this->m_operations[index];
		operation->deinitExecution();
	}
	for (index = 0; index < this->m_groups.size(); index++) {
		ExecutionGroup *executionGroup = this->m_groups[index];
		executionGroup->deinitExecution();
	}
}