Watermark doesn't work with TR & 3rd Party Render

On 15/03/2016 at 05:48, xxxxxxxx wrote:

User Information:
Cinema 4D Version:   R16 
Platform:      
Language(s) :     C++  ;

---------
Hi!

The built-in Watermark videopost has the same problem. 😞 I have a videopost that adds a watermark
in VIDEOPOSTCALL_FRAME with !vps->open. This works fine in normal renders, but it does not when
rendering a single frame  via Team Render.

This is the code from VideoPostData::Execute()

    RENDERRESULT Execute(BaseVideoPost* node, VideoPostStruct* vps) override {
        GePrint("Execute() " + String::IntToString(vps->vp));  // DEBUG
        if (vps->vp == VIDEOPOSTCALL_FRAME && vps->open) {
            _executeLineCalled = false;
            _applyWatermark = HasFixtureContainer(vps->doc) && !StageHook_TestLicense(vps->doc);
        }
        else if (vps->vp == VIDEOPOSTCALL_FRAME && !vps->open
                 && _applyWatermark && !_executeLineCalled)
        {
            // If ExecuteLine() should have been called but was not, we are
            // dealing with a third-party renderer that doesn't support it.
            VPBuffer* rgba = vps->render->GetBuffer(VPBUFFER_RGBA, 0);
            Int32 const size = (rgba ? rgba->GetBw() * rgba->GetCpp() : 0);
            AutoGeFree<Float32> line(NewMemClear(Float32, size));
            if (!rgba) GePrint("No RGBA Buffer!");  // DEBUG
            if (rgba && line) {
                Int32 const width = rgba->GetBw();
                for (Int32 y = 0; y < rgba->GetBh(); ++y) {
                    rgba->GetLine(0, y, width, line, 32, true);
                    PixelPost pp;
                    pp.frag = nullptr;
                    pp.frag_nx = nullptr;
                    pp.vd = vps->vd;
                    pp.col = line;
                    pp.mp = nullptr;
                    pp.aa = false;
                    pp.valid_line = true;
                    pp.comp = rgba->GetCpp();
                    pp.cpu_num = 0;
                    pp.xmin = 0;
                    pp.xmax = width - 1;
                    pp.line = y;
                    this->ExecuteLine(node, &pp);
                    rgba->SetLine(0, y, width, line, 32, true);
                }
            }
        }
        return RENDERRESULT_OK;
    }

Is it a limition of Team Render?

On 16/03/2016 at 04:57, xxxxxxxx wrote:

Hello,

I'm testing this with 17.048. It seems that "Watermark" works fine with Team Render single frame rendering. Please notice that if "Multi-Pass" is enabled, the post effect will not render it's result into the RGBA buffer but into separate passes.

It looks like that if a post effect should be executed on !vps->open this has to be done not on a single client but on the server. To make sure your post effect is executed on the server you must return VIDEOPOSTINFO_NETRUNONSERVER in your GetRenderInfo().

Best wishes,
Sebastian

On 16/03/2016 at 05:34, xxxxxxxx wrote:

Hi Sebastian, thanks a lot for your reply.

Unfortunately I can't confirm the "Watermark" video post working with Team Render single frame rendering,
neither in R16.050 nor R17.048. ** It does not work with third-party renderers , just to be clear.

I use the sdk_teamrender.cpp example to test it. I've added a GIF that shows rendering using that
Team Render Example renderer locally, then using Team Render single frame.

Note that it does work  rendering an animation. I guess this is due to the fact that Team Render 
assigns a full frame per node, thus its the same as doing a single frame render without TR.

Thanks!
Niklas

PS: Attached is the source of that teamrender example, just in case.

#include "c4d.h"
// #include "main.h"
#include "lib_clipmap.h"
#include "lib_net.h"
  
/**A unique plugin ID. You must obtain this from http://www.plugincafe.com. Use this ID to create new instances of this material.*/
#define ID_SDK_TEAMRENDER_EXAMPLE 1033999 // NOT A REAL PLUGIN ID !!
  
  
#define BUCKET_SIZE 64	// hardcoded bucket size
  
//----------------------------------------------------------------------------------------
/// Data structure relevant for the Team Render Client
//----------------------------------------------------------------------------------------
struct TeamRenderClientData
{
	VideoPostStruct* _vps;
	Int32 _bucketSize;
	Int32 _currentBucket;
	Int32 _x1;
	Int32 _x2;
	Int32 _y1;
	Int32 _y2;
};
  
//----------------------------------------------------------------------------------------
/// Data structure relevant for the Team Render Server
//----------------------------------------------------------------------------------------
struct TeamRenderServerData
{
	RenderJob* _renderJob;
	MultipassBitmap* _targetBitmap;
	Int32 _lastBucketIndex;
	Int32 _totalBucketCount;
	Int32 _finishedBucketCount;
	Int32 _startTime;
};
  
//----------------------------------------------------------------------------------------
/// Simple VideoPostData renderer using Team Render funtionality
//----------------------------------------------------------------------------------------
class TeamRenderVideoPost : public VideoPostData
{
		INSTANCEOF(TeamRenderVideoPost, VideoPostData)
  
public:
  
	static NodeData* Alloc() { return NewObjClear(TeamRenderVideoPost);}
  
	virtual VIDEOPOSTINFO GetRenderInfo(BaseVideoPost* node);
  
  virtual RENDERRESULT Execute(BaseVideoPost* node, VideoPostStruct* vps);
  
  
  virtual Bool RenderEngineCheck(BaseVideoPost* node, Int32 id);
  
  virtual Bool NetFrameInit(BaseVideoPost *node, BaseDocument* doc, RenderJob* job, Int32 initialclients, const NetRenderData* renderinfo, MultipassBitmap* frameBmp, BaseThread* bt, Int32 &realdepth);
  virtual void NetFrameFree(BaseVideoPost* node);
  virtual Bool NetFrameMessage(BaseVideoPost *node, const C4DUuid& remoteUuid, const NetRenderBuffer& data, NetRenderBuffer *result);
  
  
private:
  
	void FillBuffer(VideoPostStruct* vps, String text, Vector color);
  
  
private: // client methods
  
	void HandleTeamRenderBucket(VideoPostStruct* vps);
	Bool GetBucketSize(TeamRenderClientData* data);
	Bool GetBucketIndex(TeamRenderClientData* data);
	void GetBucketDimensions(TeamRenderClientData* data);
	void SendResult(TeamRenderClientData* data);
  
private: // server data
  
	TeamRenderServerData _serverData;
  
private: // server methods
  
	void FillRegion(Int32 x1, Int32 x2, Int32 y1, Int32 y2);
	void FillRegion(NetRenderMsgBucketFinished* result);
  
  
  
};
  
  
VIDEOPOSTINFO TeamRenderVideoPost::GetRenderInfo(BaseVideoPost* node)
{
	return VIDEOPOSTINFO_NETFRAME;
	//return VIDEOPOSTINFO_0;
}
  
  
RENDERRESULT TeamRenderVideoPost::Execute(BaseVideoPost* node, VideoPostStruct* vps)
{
	// check if break
	if(vps->thread && vps->thread->TestBreak())
	{
		return RENDERRESULT_OK;
	}
  
	if (vps->open)
	{
		switch (vps->vp)
		{
			case(VIDEOPOSTCALL_RENDER) :
			{
				// skip default Cinema Render process
				if(vps->vd)
					vps->vd->SkipRenderProcess();
  
				break;
			}
			case(VIDEOPOSTCALL_INNER) :
			{
  
				// check if Team Render situation
				if(vps->net && vps->net_server == false)
				{
  
					if(vps->net->_singleImageDistribution)
					{
						// distrubuted rendering (single frame render buckets)
						this->HandleTeamRenderBucket(vps);
					}
					else
					{
						// animation rendering (render a whole frame)
						this->FillBuffer(vps,"Render with Team Render",Vector(1,1,1));
					}
				}
				else
				{
					// no Team Render situation
					this->FillBuffer(vps,"Render locally",Vector(1,1,1));
				}
  
				break;
			}
		}
	}
  
  
	return RENDERRESULT_OK;
}
  
  
Bool TeamRenderVideoPost::RenderEngineCheck(BaseVideoPost* node, Int32 id)
{
	switch (id)
	{
		case RENDERSETTING_STATICTAB_ANTIALIASING:
		case RENDERSETTING_STATICTAB_OPTIONS:
		case RENDERSETTING_STATICTAB_STEREO:
		return false;
	}
  
	return true;
}
  
//----------------------------------------------------------------------------------------
/// Function is called when the Team Render server process starts. Access and save all relevant information.
//----------------------------------------------------------------------------------------
Bool TeamRenderVideoPost::NetFrameInit(BaseVideoPost *node, BaseDocument* doc, RenderJob* job, Int32 initialclients, const NetRenderData* renderinfo, MultipassBitmap* frameBmp, BaseThread* bt, Int32 &realdepth)
{
	GePrint("TeamRenderVideoPost::NetFrameInit");
  
	// calculating the total number of render buckets
	VPBuffer* rgba = (VPBuffer* )frameBmp;
  
	if(!rgba)
		return false;
  
	const Int32 w = rgba->GetBw();
	const Int32 h = rgba->GetBh();
  
	const Int32 subdivisionX = (Int32)maxon::Ceil((Float)w / (Float)BUCKET_SIZE);
	const Int32 subdivisionY = (Int32)maxon::Ceil((Float)h / (Float)BUCKET_SIZE);
  
	_serverData._totalBucketCount = subdivisionX * subdivisionY;
  
	// number of finished buckets
	_serverData._finishedBucketCount = 0;
  
		// default first bucket index
	_serverData._lastBucketIndex = 0;
  
		// save target framebuffer
	_serverData._targetBitmap = frameBmp;
  
	// save RenderJob
	_serverData._renderJob = job;
  
	// save start time
	_serverData._startTime = GeGetMilliSeconds();
  
  
  
	frameBmp->SetData(BASEBITMAP_DATA_PROGRESS_PHASE,	GeData(RENDERPROGRESSTYPE_DURINGRENDERING));
	frameBmp->SetData(BASEBITMAP_DATA_PROGRESS_FRAME,	GeData(0.0));
	frameBmp->SetData(BASEBITMAP_DATA_STARTTIME,			GeData(GeGetTimer()));
	frameBmp->SetSave(true);
  
	// add passes
  
	MultipassBitmap* layer = frameBmp->AddLayer(nullptr ,COLORMODE_RGB,false);
	if(layer)
	{
		layer->SetUserID(2000);
		layer->SetName("Multipass A");
		layer->SetSave(true);
	}
  
	layer = frameBmp->AddLayer(nullptr ,COLORMODE_RGB,false);
	if(layer)
	{
		layer->SetUserID(2001);
		layer->SetName("Multipass B");
		layer->SetSave(true);
	}
  
	return true;
}
  
//----------------------------------------------------------------------------------------
/// Function is called when the Team Render server process ends.
//----------------------------------------------------------------------------------------
void TeamRenderVideoPost::NetFrameFree(BaseVideoPost* node)
{
	GePrint("RendererVideoPost::NetFrameFree");
  
	if(!node)
		return;
  
	BaseDocument* doc = node->GetDocument();
  
	if(!doc)
		return;
  
	const RenderData* const renderData = doc->GetActiveRenderData();
  
	if(_serverData._targetBitmap == nullptr)
		return;
  
	_serverData._targetBitmap->SetData(BASEBITMAP_DATA_PROGRESS_PHASE,	GeData(RENDERPROGRESSTYPE_AFTERRENDERING));
  
	// set Picture Viewer status bar text
  
	const Int32 diff = GeGetMilliSeconds() - _serverData._startTime;
	const Float32 seconds = Float32(diff) / 1000.0f;
	const String text = "Render Time: "+String::FloatToString(seconds,-1,1)+ " s.";
  
	_serverData._targetBitmap->SetData(BASEBITMAP_DATA_PROGRESS_TIME, GeData(text));
  
  
  
	//
	// make saving the multipasses depend on whatever your multipasses depend on
	//
  
	// save image data
	const Int32 layerCount = _serverData._targetBitmap->GetLayerCount();
  
	for(Int32 i = 0; i < layerCount; ++i)
	{
		MultipassBitmap* bmp = _serverData._targetBitmap->GetLayerNum(i);
  
		if(bmp)
		{
			GeData name = bmp->GetParameter(MPBTYPE_NAME);
			GeData userID = bmp->GetParameter(MPBTYPE_USERID);
			GeData save = bmp->GetParameter(MPBTYPE_SAVE);
  
			if(save.GetBool())
			{
				Filename imagePath = renderData->GetDataInstance()->GetFilename(RDATA_PATH);
				String suffix = imagePath.GetSuffix();
  
				imagePath.SetFile("mp_"+String::IntToString(i)+"."+suffix);
  
				GePrint("write file :"+imagePath.GetString());
  
				bmp->Save(imagePath,FILTER_TIF,nullptr,SAVEBIT_0);
			}
		}
	}
  
  
	// saving the rgba image
	if(renderData->GetDataInstance()->GetBool(RDATA_SAVEIMAGE))
	{
		Int32 format = renderData->GetDataInstance()->GetInt32(RDATA_FORMAT);
		Filename imagePath = renderData->GetDataInstance()->GetFilename(RDATA_PATH);
  
		GePrint("write file :"+imagePath.GetString());
  
		_serverData._targetBitmap->Save(imagePath,format,nullptr,SAVEBIT_0);
	}
  
  
  
}
  
  
//----------------------------------------------------------------------------------------
/// Function is called when the Team Render server receives a message from a client.
//----------------------------------------------------------------------------------------
Bool TeamRenderVideoPost::NetFrameMessage(BaseVideoPost* node, const C4DUuid& remoteUuid, const NetRenderBuffer& data, NetRenderBuffer* result)
{
	GePrint("RendererVideoPost::NetFrameMessage");
  
	switch(data.id)
	{
  
		// Client requests the bucket size
		case(MSG_NETRENDER_BUCKET_INFO) :
		{
			GePrint("- MSG_NETRENDER_BUCKET_INFO");
  
			// prepare answer
			NetRenderMsgBucketInfo* answer = NewMemClear(NetRenderMsgBucketInfo, 1);
  
			if ((answer == nullptr) || (result == nullptr))
				return false;
  
			// set data
			answer->bucketSize = BUCKET_SIZE;
  
			// set result
			result->size = sizeof(NetRenderMsgBucketInfo);
			result->data = answer;
  
			break;
		}
  
		// client requests the index of a bucket to render
		case MSG_NETRENDER_BUCKET_INDEX:
		{
			GePrint("- MSG_NETRENDER_BUCKET_INDEX");
  
			// return -1 if the client should no longer do anything
			Int32 bucketIndex = -1;
  
			// only send a new bucket index if there are still bucket to distribute
			if(_serverData._lastBucketIndex < _serverData._totalBucketCount)
				bucketIndex = _serverData._lastBucketIndex;
  
			// prepare answer
			NetRenderMsgBucketIndex* answer = NewMemClear(NetRenderMsgBucketIndex, 1);
  
			if ((answer == nullptr) || (result == nullptr))
				return false;
  
			// set data
			answer->bucketID			= bucketIndex;
  
			result->size							= sizeof(NetRenderMsgBucketIndex);
			result->data							= answer;
  
			// increment
			_serverData._lastBucketIndex++;
  
			break;
		}
  
		// client sends the dimensions of the bucket he is about to render
		// this is used to highlight the bucket in the framebuffer
		case MSG_NETRENDER_BUCKET_REQUEST:
		{
			GePrint("- MSG_NETRENDER_BUCKET_REQUEST");
  
			NetRenderMsgBucketStarted* message = (NetRenderMsgBucketStarted* )data.data;
  
			this->FillRegion(	message->regionX1,
												message->regionX2,
												message->regionY1,
												message->regionY2);
  
			break;
		}
  
		// client sends the results of the rendering process
		case MSG_NETRENDER_BUCKET_FINISHED:
		{
			GePrint("- MSG_NETRENDER_BUCKET_FINISHED");
  
			NetRenderMsgBucketFinished* message = (NetRenderMsgBucketFinished* )data.data;
  
			message->buffer				= (Float32* )(((UChar* )message) + sizeof(NetRenderMsgBucketFinished));
  
			// write the data to the framebuffer
			this->FillRegion(message);
  
			// increment finished buckets
			_serverData._finishedBucketCount++;
  
  
			if(_serverData._finishedBucketCount == _serverData._totalBucketCount)
			{
				// end the render process
				_serverData._renderJob->UpdateProgress(1.0, true);
			}
			else
			{
				const Float progress = (Float)_serverData._finishedBucketCount / (Float)_serverData._totalBucketCount;
  
				_serverData._renderJob->UpdateProgress(progress, false);
  
				// define progress bar in picture viewer
  
				String showProgress = String::FloatToString(progress*100.0);
				showProgress += " %";
  
				_serverData._targetBitmap->SetData(BASEBITMAP_DATA_PROGRESS_FRAME,GeData(progress));
				_serverData._targetBitmap->SetData(BASEBITMAP_DATA_PROGRESS_ACTION,	GeData(showProgress));
			}
  
			break;
		}
	}
  
	return true;
}
  
//----------------------------------------------------------------------------------------
/// Function will fill the framebuffer with the given color and will write the given text.
//----------------------------------------------------------------------------------------
void TeamRenderVideoPost::FillBuffer(VideoPostStruct* vps, String text, Vector color)
{
	if(!vps || !vps->render)
		return;
  
	VPBuffer* rgba = vps->render->GetBuffer(VPBUFFER_RGBA, NOTOK);
  
	if(!rgba)
		return;
  
	AutoAlloc<GeClipMap> clipMap;
  
  
	if(clipMap->Init(rgba->GetBw(),rgba->GetBh(),32) != IMAGERESULT_OK)
		return;
  
	clipMap->BeginDraw();
  
	const Int32 col_r = SAFEINT32(color.x * 255.0);
	const Int32 col_g = SAFEINT32(color.y * 255.0);
	const Int32 col_b = SAFEINT32(color.z * 255.0);
  
	clipMap->SetColor(col_r,col_g,col_b);
	clipMap->FillRect(0,0,clipMap->GetBw()-1,clipMap->GetBh()-1);
  
  
	clipMap->SetColor(0,0,0);
  
	String output = "Frame: ";
	output += String::IntToString(vps->time.GetFrame(vps->fps));
	output += ": " + text;
  
	clipMap->TextAt(10, 10,output);
  
	clipMap->EndDraw();
  
	BaseBitmap* res = clipMap->GetBitmap();
  
	// copy
	Int32 x1, y1, x2, y2, x, y, cnt;
  
	// example functions
	Int32 cpp = rgba->GetInfo(VPGETINFO_CPP);
  
	x1	= 0;
	y1	= 0;
	x2	= rgba->GetBw();
	y2	= rgba->GetBh();
	cnt = rgba->GetBw();
  
	Int			 bufferSize = cpp * cnt;
	Float32* b, *buffer = nullptr;
  
	if (bufferSize > 0)
		buffer = NewMemClear(Float32, bufferSize);
  
	if (!buffer)
		return;
  
	for (y = 0; y < y2; y++)
	{
		rgba->GetLine(x1, y, cnt, buffer, 32, true);
  
		for (b = buffer, x = 0; x < x2; x++, b += cpp)
		{
			UInt16 pr,pg,pb;
			res->GetPixel(x, y, &pr, &pg, &pb);
  
			b[0] = (Float32)pr / 255.0f;
			b[1] = (Float32)pg / 255.0f;
			b[2] = (Float32)pb / 255.0f;
		}
  
		rgba->SetLine(x1, y, cnt, buffer, 32, true);
	}
	DeleteMem(buffer);
}
  
  
//----------------------------------------------------------------------------------------
/// The client requests buckets and will send render results to the server.
//----------------------------------------------------------------------------------------
void TeamRenderVideoPost::HandleTeamRenderBucket(VideoPostStruct* vps)
{
	// prepare data
	TeamRenderClientData data;
	data._vps = vps;
	data._currentBucket = -1;
  
	Bool res = false;
  
	// request bucket size
	res = this->GetBucketSize(&data);
  
	if(res == false)
		return;
  
	// loop until there are no more buckets to render
	while(true)
	{
		// request bucket index
		res = this->GetBucketIndex(&data);
  
		if(res == false)
			return;
  
		// simulate activity
		GeSleep(50);
  
		// send results to the server
		this->SendResult(&data);
  
  
		if(vps->thread->TestBreak())
			return;
  
	}
}
  
//----------------------------------------------------------------------------------------
/// The client requests the bucket size.
//----------------------------------------------------------------------------------------
Bool TeamRenderVideoPost::GetBucketSize(TeamRenderClientData* data)
{
	// setup message
	NetRenderBuffer requestMessage;
	NetRenderMsgBucketInfo* messageData = NewObjClear(NetRenderMsgBucketInfo);
  
	requestMessage.id = MSG_NETRENDER_BUCKET_INFO;
	requestMessage.job = data->_vps->net->_renderJob->GetUuid();
	requestMessage.size = sizeof(NetRenderMsgBucketInfo);
	requestMessage.data = messageData;
  
	// setup Result
	NetRenderBuffer requestResult;
	requestResult.id = MSG_NETRENDER_BUCKET_INFO;
	requestResult.job = data->_vps->net->_renderJob->GetUuid();
	requestResult.size = 0;
	requestResult.data = nullptr;
  
	// send
	GePrint("Send MSG_NETRENDER_BUCKET_INFO");
	MESSAGERESULT res = NetSendData(data->_vps->net->_service, data->_vps->net->_renderJob->GetServerUuid(), &requestMessage, &requestResult);
  
	if(res != MESSAGERESULT_OK)
		return false;
  
	if (requestResult.data == nullptr)
		return false;
  
	// get data
	NetRenderMsgBucketInfo* bucketInfo = (NetRenderMsgBucketInfo* )requestResult.data;
	data->_bucketSize = bucketInfo->bucketSize;
  
	return true;
}
  
//----------------------------------------------------------------------------------------
/// The client requests the bucket index and will send the bucket dimensions to the server.
//----------------------------------------------------------------------------------------
Bool TeamRenderVideoPost::GetBucketIndex(TeamRenderClientData* data)
{
	data->_currentBucket = -1;
  
  
	NetRenderMsgBucketInfo* pNetMsgInfo	= NewMemClear(NetRenderMsgBucketInfo, 1);
  
	if (pNetMsgInfo == nullptr)
		return false;
  
	// prepare message
	NetRenderBuffer requestMessage;
	requestMessage.id						= MSG_NETRENDER_BUCKET_INDEX;
	requestMessage.job					= data->_vps->net->_renderJob->GetUuid();
	requestMessage.size					= sizeof(NetRenderMsgBucketIndex);
	requestMessage.data					= pNetMsgInfo;
  
	NetRenderBuffer requestResult;
	requestResult.id					= MSG_NETRENDER_BUCKET_INDEX;
	requestResult.job					= data->_vps->net->_renderJob->GetUuid();
	requestResult.size				= 0;
	requestResult.data				= nullptr;
  
	GePrint("Send MSG_NETRENDER_BUCKET_INDEX");
	NetSendData(data->_vps->net->_service,data->_vps->net->_renderJob->GetServerUuid(), &requestMessage, &requestResult);
  
	if(requestResult.data == nullptr)
		return false;
  
	data->_currentBucket	= ((NetRenderMsgBucketIndex* )requestResult.data)->bucketID;
  
	if(data->_currentBucket == -1)
		return false;
  
	// get dimensions
	this->GetBucketDimensions(data);
  
  
	//----------------------
	// answer server
	//----------------------
  
	// setup answer data
	NetRenderMsgBucketStarted* answerData = NewMemClear(NetRenderMsgBucketStarted, 1);
	answerData->bucketID	= data->_currentBucket;
	answerData->regionX1	= data->_x1;
	answerData->regionX2	= data->_x2;
	answerData->regionY1	= data->_y1;
	answerData->regionY2	= data->_y2;
  
	// setup answer message
	NetRenderBuffer* answerMessage					 = NewObj(NetRenderBuffer);
	answerMessage->id				= MSG_NETRENDER_BUCKET_REQUEST;
	answerMessage->job			= data->_vps->net->_renderJob->GetUuid();
	answerMessage->size			= sizeof(NetRenderMsgBucketStarted);
	answerMessage->data			= answerData;
  
	GePrint("Send MSG_NETRENDER_BUCKET_REQUEST");
	NetSendData(data->_vps->net->_service, data->_vps->net->_renderJob->GetServerUuid(), answerMessage, nullptr);
  
	return true;
}
  
//----------------------------------------------------------------------------------------
/// Used to calculate the position and the dimensions of the bucked defined by the current bucket index.
//----------------------------------------------------------------------------------------
void TeamRenderVideoPost::GetBucketDimensions(TeamRenderClientData* data)
{
		VPBuffer* rgba = data->_vps->render->GetBuffer(VPBUFFER_RGBA, NOTOK);
  
		if(!rgba)
			return;
  
		const Int32 w = rgba->GetBw();
		const Int32 h = rgba->GetBh();
  
		const Int32 subdivisionX = (Int32)maxon::Ceil((Float)w / (Float)data->_bucketSize);
  
		const Int32 xBucket = data->_currentBucket % subdivisionX;
		const Int32 yBucket = (Int32)maxon::Floor((Float)data->_currentBucket / (Float)subdivisionX);
  
		data->_x1 = xBucket * data->_bucketSize;
		data->_x2 = data->_x1 + data->_bucketSize;
  
		data->_y1 = yBucket * data->_bucketSize;
		data->_y2 = data->_y1 + data->_bucketSize;
  
		if(data->_x2 > w)
			data->_x2 = w;
  
		if(data->_y2 > h)
			data->_y2 = h;
  
}
  
//----------------------------------------------------------------------------------------
/// The client sends the final image data to the server.
//----------------------------------------------------------------------------------------
void TeamRenderVideoPost::SendResult(TeamRenderClientData* data)
{
	//----------------
	// First I fill some memory with a random color to simulate "rendering"
	//----------------
  
	const Int32 width		= data->_x2 - data->_x1;
	const Int32 height	= data->_y2 - data->_y1;
  
	const Int32 payloadPixels = width * height * 4; //RGBA
	const Int32 payloadSize = payloadPixels * sizeof(Float32);
  
	Float32* payload = NewMemClear(Float32,payloadPixels);
  
	// fill payload with random color
	Random random;
	random.Init(GeGetTimer());
	Float32 r = (Float32)random.Get01();
	Float32 g = (Float32)random.Get01();
	Float32 b = (Float32)random.Get01();
  
	for(Int32 i = 0; i < (width * height); ++i)
	{
		const Int32 offset	= i*4;
  
		payload[offset]		= r;
		payload[offset+1]	= g;
		payload[offset+2]	= b;
		payload[offset+3]	= 1.0f;
	}
  
  
	//---------
	// Now I prepare the message to the server
	//---------
  
  
	// setup message
	// I need to allocate enough memory for both the message and the payload
  
	UChar* messageRoughData									= NewMemClear(UChar, sizeof(NetRenderMsgBucketFinished) + payloadSize);
	NetRenderMsgBucketFinished* messageData = (NetRenderMsgBucketFinished* )messageRoughData;
  
	messageData->bucketID				= data->_currentBucket;
	messageData->offsetX				= data->_x1;
	messageData->offsetY				= data->_y1;
	messageData->sizeX					= data->_x2 - data->_x1;
	messageData->sizeY					= data->_y2 - data->_y1;
  
	messageData->bufferSize				= payloadSize;
  
	// the payload bufer can be found at the "end" of the NetRenderMsgBucketFinished object memory
	messageData->buffer						= (Float32* )(messageRoughData + sizeof(NetRenderMsgBucketFinished));
  
	// copy the image data into the payload memory
	ClearMem(messageData->buffer,	payloadSize);
	CopyMem(payload,	messageData->buffer,	payloadSize);
  
	// setup message
	NetRenderBuffer* message		= NewObj(NetRenderBuffer);
  
	message->id								= MSG_NETRENDER_BUCKET_FINISHED;
	message->job							= data->_vps->net->_renderJob->GetUuid();
	message->size							= sizeof(NetRenderMsgBucketFinished) + payloadSize;
	message->data							= messageData;
  
	// send message
	GePrint("Send MSG_NETRENDER_BUCKET_FINISHED");
	NetSendData(data->_vps->net->_service, data->_vps->net->_renderJob->GetServerUuid(), message, nullptr, data->_vps->thread);
  
	DeleteMem(payload);
}
  
  
//----------------------------------------------------------------------------------------
/// The defined region of the frame buffer is filled to indicate the start of the rendering of this bucket.
//----------------------------------------------------------------------------------------
void TeamRenderVideoPost::FillRegion(Int32 x1, Int32 x2, Int32 y1, Int32 y2)
{
	if(_serverData._targetBitmap == nullptr)
		return;
  
  
	VPBuffer* rgba = (VPBuffer* )_serverData._targetBitmap;
  
  
	Int32 x, y, cnt;
	cnt = x2 - x1;
  
	Int32 cpp = rgba->GetInfo(VPGETINFO_CPP);
  
  
	Int			 bufferSize = cpp * cnt;
	Float32* b, *buffer = nullptr;
  
	if (bufferSize > 0)
		buffer = NewMemClear(Float32, bufferSize);
  
	if (!buffer)
		return;
  
  
	for (y = y1; y < y2; y++)
	{
		rgba->GetLine(x1, y, cnt, buffer, 32, true);
  
		for (b = buffer, x = x1; x < x2; x++, b += cpp)
		{
			b[0] = 1.0;
			b[1] = 0.0;
			b[2] = 0.0;
  
			if(cpp == 4)
				b[3] = 1.0f;
		}
  
		rgba->SetLine(x1, y, cnt, buffer, 32, true);
	}
	DeleteMem(buffer);
  
}
  
//----------------------------------------------------------------------------------------
/// The region defined in the message is filled with the data of that message.
//----------------------------------------------------------------------------------------
void TeamRenderVideoPost::FillRegion(NetRenderMsgBucketFinished* result)
{
	if(_serverData._targetBitmap == nullptr)
		return;
  
	if(result == nullptr)
		return;
  
  
	VPBuffer* rgba = (VPBuffer* )_serverData._targetBitmap;
	VPBuffer* multipass = nullptr;
  
	// multipasses
	Int32 layerCount = _serverData._targetBitmap->GetLayerCount();
  
	for(Int32 i = 0; i < layerCount; ++i)
	{
		MultipassBitmap* bmp = _serverData._targetBitmap->GetLayerNum(i);
  
		if(bmp)
		{
			GeData userID = bmp->GetParameter(MPBTYPE_USERID);
  
			if(userID == 2000)
				multipass = (VPBuffer* )bmp;
		}
	}
  
  
  
	const Int32 x1 = result->offsetX;
	const Int32 x2 = result->offsetX + result->sizeX;
	const Int32 y1 = result->offsetY;
	const Int32 y2 = result->offsetY + result->sizeY;
  
	Int32 x, y, cnt;
	cnt = result->sizeX;
  
	Int32 cpp = rgba->GetInfo(VPGETINFO_CPP);
  
  
	Int			 bufferSize = cpp * cnt;
	Float32* b, *buffer = nullptr;
  
	if (bufferSize > 0)
		buffer = NewMemClear(Float32, bufferSize);
  
	if (!buffer)
		return;
  
	Int bufferOffset = 0;
  
	for (y = y1; y < y2; y++)
	{
		rgba->GetLine(x1, y, cnt, buffer, 32, true);
  
		for (b = buffer, x = x1; x < x2; x++, b += cpp)
		{
			b[0] = result->buffer[bufferOffset];
			b[1] = result->buffer[bufferOffset+1];
			b[2] = result->buffer[bufferOffset+2];
  
			if(cpp == 4)
				b[3] = 1.0f;
  
			bufferOffset += 4;
		}
  
		rgba->SetLine(x1, y, cnt, buffer, 32, true);
	}
	DeleteMem(buffer);
  
  
	// fill a layer
	if(multipass)
	{
		buffer = NewMemClear(Float32, bufferSize);
		cpp = 3;
  
		for (y = y1; y < y2; y++)
		{
			multipass->GetLine(x1, y, cnt, buffer, 32, true);
  
			for (b = buffer, x = x1; x < x2; x++, b += cpp)
			{
				b[0] = 1.0f;
				b[1] = 0.0f;
				b[2] = 0.0f;
			}
  
			multipass->SetLine(x1, y, cnt, buffer, 32, true);
		}
		DeleteMem(buffer);
	}
}
  
  
  
Bool RegisterSDKTeamRenderExample()
{
	return RegisterVideoPostPlugin(ID_SDK_TEAMRENDER_EXAMPLE, "Team Render Example", PLUGINFLAG_VIDEOPOST_ISRENDERER|PLUGINFLAG_VIDEOPOST_ISRENDERER_NET, TeamRenderVideoPost::Alloc, "vptr", 0, 0);
};

On 17/03/2016 at 10:26, xxxxxxxx wrote:

Hello,

a custom renderer that handles single frame rendering is responsible for pretty much everything. This means that Cinema won't execute the video post effects after the single frame finished. One would have to execute the video posts in NetFrameFree(). But since BaseVideoPost offers no way to execute the video post I'm afraid that there is no solution for this scenario.

Best wishes,
Sebastian

On 18/03/2016 at 06:21, xxxxxxxx wrote:

Hi Sebastian,

thanks for the information. IMHO this is something that TR should be doing automatically and nothing
that would need to be done manually. Maybe R18 will feature this.

Anyway, that's bad for my client since the only "reliable" protection of his plugin is the watermark.
With the watermark being circumvented using third party renderers and V-Ray, well you get the point.

Best,
Niklas