Jump to content

Disable multithreaded rendering.


codester
 Share

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.

Link to comment
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.

The Paint.NET Blog: https://blog.getpaint.net/

Donations are always appreciated! https://www.getpaint.net/donate.html

forumSig_bmwE60.jpg

Link to comment
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?

Link to comment
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.

xZYt6wl.png

ambigram signature by Kemaru

[i write plugins and stuff]

If you like a post, upvote it!

Link to comment
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."

xZYt6wl.png

ambigram signature by Kemaru

[i write plugins and stuff]

If you like a post, upvote it!

Link to comment
Share on other sites

  • 4 months later...

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).

Link to comment
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.

 

Changed the text in the signature yay!

PDN gallery!

Link to comment
Share on other sites

  • 1 month later...

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.

xZYt6wl.png

ambigram signature by Kemaru

[i write plugins and stuff]

If you like a post, upvote it!

Link to comment
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){}

Link to comment
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.

xZYt6wl.png

ambigram signature by Kemaru

[i write plugins and stuff]

If you like a post, upvote it!

Link to comment
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

Link to comment
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.

xZYt6wl.png

ambigram signature by Kemaru

[i write plugins and stuff]

If you like a post, upvote it!

Link to comment
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.

 Share

×
×
  • Create New...