Sign in to follow this  
codester

Disable multithreaded rendering.

Recommended Posts

Hello community!

If I understood this in the right way, Paint.NET calls the render method of an effect plugin several times to allow a multithreaded rendering.

I've created an effect plugin where multithreaded rendering is quite useless because the parts of the image have to be rendered in a predefined order, that means the simultaneous rendering of two parts could cause bad results. I'd like to ask if it's possible to prevent multiple calls of my render function.

Share this post


Link to post
Share on other sites

No, simply pass the appropriate EffectFlags to the base constructor, "EffectFlags.SingleThreaded".

This will not necessarily give you control over the ordering of calls to Render(). Keep in mind that your effect will not necessarily be rendering against a square region-of-interest, either, as it is clipped to whatever arbitrary selection the user has crafted.

Share this post


Link to post
Share on other sites

Unfortunately there's still a problem I didn't remark at first...

PDN still calls the render function of my plugin for several different rectangles of interest. I actually wanted it to call the render function only once so I can draw the whole image. Is that possible?

Share this post


Link to post
Share on other sites

Rick will bite me for telling you this, as it's not exactly how plugins are supposed to work, but anything that has to process on the entire image before anything else can be processed can be done in the OnSetRenderInfo method. Anything that can be done in an arbitrary order (once that function has finished) should be put in Render.

This is how ScriptLab works, because there is simply no other way to do it.

To avoid the Wrath of Rick, however, it may be worth it to just rethink the way your plugin does what it does.

Share this post


Link to post
Share on other sites
From what I understand in your post, you should simply tell people on the topic you post for the plugin that they should not Ctrl+F it, or multithread render it.

Ctrl+F has nothing to do with multi-threaded rendering. All it does is tell Paint.NET, "hey, you know that thing I just did? Do it again."

Share this post


Link to post
Share on other sites

pyrochild said:

> ... anything that has to process on the entire image before anything else can be processed can be done in the OnSetRenderInfo method

This is also what I was trying to do. That is, I have a large amount of initialization code which would make rendering the effect very slow if it was performed every time Render was called. I did not know about OnSetRenderInfo, so what I did was this:

private bool firstCall = true;

public override void Render(EffectConfigToken parameters, ...)

{

lock(this)

{

if (firstCall)

{

// Do time-consuming init here using srcArgs

....

firstCall = false;

}

}

for (int i = startIndex; i < startIndex + length; ++i)

// render to current rect(s)

}

Note that this assumes that PDN itself does not lock have a lock on the effect instance at the time, which is a pretty safe bet (but Rick can confirm this). If this turns out to be a problem you can use a private member object to lock instead.

The above will work even if you have not specified the EffectFlags.SingleThreaded flag in the ctor, since the lock means that the initialization is only performed once even if other threads are also executing the Render method.

I have looked at the source code for a few effects and I have noticed 2 common problems:

** they specify EffectFlags.SingleThreaded when they do not need to

** they repeat a lot of initialization code each time Render is called which can be done once

Back to the original question:

codester said:

> ... parts of the image have to be rendered in a predefined order ...

I don't think turning off multithreaded rendering will help you. Even if you set the EffectFlags.SingleThreaded flag it is still not defined which order the rects are passed to consecutive calls to Render. All EffectFlags.SingleThreaded does is ensure that multiple Renders are not running at the same time (ie in different threads).

I think what you really need to do is organise your code so that most of your code is done as above in an "init" block. Then render different parts appropriately in the specific calls to Render. You can always do this even if you have to create a temp surface in the "init" code, and then just copy the appropriate pixels (as spec'ed by rois, startIndex, length).

Share this post


Link to post
Share on other sites
From what I understand in your post, you should simply tell people on the topic you post for the plugin that they should not Ctrl+F it, or multithread render it.

Ctrl+F has nothing to do with multi-threaded rendering. All it does is tell Paint.NET, "hey, you know that thing I just did? Do it again."

I realized that, yeah I misunderstood the English. As soon as he reposted and Rick posted below him, I understood that. His wording was a little confusing.

Share this post


Link to post
Share on other sites

I have the same problem but I'm using Codelab.

For this plugin I want to first calculate the middle color and then start processing on the image in a multithreaded way with all threads using the precalculated color. (The middle color should be the arithmetic middle of all pixels)

Do I have a chance?

Share this post


Link to post
Share on other sites

In older versions of CodeLab you could override OnSetRenderInfo to do some pre-processing, but I don't think you can anymore. You can set a flag that tells you when you're running through your Render method for the first time, process the entire image, and then use subsequent passes of Render to actually do your Rendering.

Share this post


Link to post
Share on other sites
process the entire image

What are the edges of the entire image? "rect" is only a small part of the image, isn't it?

Couldn't it happen, that the second rendering thread tries to read the color before it the first is ready with calculating it? I could set a flag for being ready, but how do I wait for the flag? Like this?

while(!ready){}

Share this post


Link to post
Share on other sites
What are the edges of the entire image? "rect" is only a small part of the image, isn't it?

srcArgs.Size

But as i've said, why not just read it every time - it's not as if reading the centre pixel takes a lot of computing power.

Share this post


Link to post
Share on other sites
process the entire image

What are the edges of the entire image? "rect" is only a small part of the image, isn't it?

You can get all the rects of the selection via

Rectangle[] selectionScans = this.EnvironmentParameters.GetSelection(src.Bounds).GetRegionScansInt();

Couldn't it happen, that the second rendering thread tries to read the color before it the first is ready with calculating it? I could set a flag for being ready, but how do I wait for the flag? Like this?

while(!ready){}

I'm not sure if you can use CodeLab to set an Effect as single threaded, but if you can that would be better. If not, change that to

while(!ready){Thread.Sleep(1);}

This avoids bogging down the CPU with spinning a loop.

@ Simon: srcArgs.Size will process the entire image. It's the size of the Surface, not the selection. Also, even if it was the size of the selection, you wouldn't know if the selection was anywhere but at the top-left, or if it was non-rectangular.

And by "middle" he means arithmetic mean, not center pixel.

Share this post


Link to post
Share on other sites
What are the edges of the entire image? "rect" is only a small part of the image, isn't it?
You can get all the rects of the selection via

Rectangle[] selectionScans = this.EnvironmentParameters.GetSelection(src.Bounds).GetRegionScansInt();

To access every selected pixel I would then have to write a triple loop like this?

for (int i = 0; i < selectionScans.size(); i++)
{
   for (int y = selectionScans[i].Top; y < selectionScans[i].Bottom; y++)
   {
       for (int x = selectionScans[i].Left; x < selectionScans[i].Right; x++)
       {
           //add this pixel to the arithmetic mean calculation
       }
   }
}

and to access ie the red value of a pixel would I then use this?

selectionScans[i][x,y].R

Share this post


Link to post
Share on other sites
To access every selected pixel I would then have to write a triple loop like this?

for (int i = 0; i {
   for (int y = selectionScans[i].Top; y     {
       for (int x = selectionScans[i].Left; x         {
           //add this pixel to the arithmetic mean calculation
       }
   }
}

Arrays don't have a size() function. To get the number of elements, use the Length property. But I would clean it up to this (where you don't need to know the size of the array anyway)

foreach(Rectangle rect in selectionScans){
   for(int y=rect.Top;y        for(int x=rect.Left;x            //add this pixel to the arithmetic mean calculation
       }
   }
}

and to access ie the red value of a pixel would I then use this?

selectionScans[i][x,y].R

No, the rectangles themselves don't have pixel data. They're just a struct containing location and size information. (x, y, width, height). To get the pixel value, just use

srcArgs.Surface[x,y].R

Again, the current working rectangle is irrelevant this way, cleaning up the code.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this