Jump to content

Safely rendering any effect programmatically


Recommended Posts

In short, I need to get any effect to at least not crash, because I can't release a feature that crashes PDN. Any effect that can normally run in Paint.net needs to theoretically be able to run in DynamicDraw. ScriptLab is the best for this, but it's inoperable right now and hard to glean info from since it uses chunks of code copied from PDN that depend on now-internal classes. I've also scoured forums a bit and the plugin index; I took what I could from Effect Lab already.

Some complicated effects like G'mic work, while some like Radial Blur crash (the only built-in effect that's not behaving). Maybe someone can tell what's null here?

Spoiler

System.NullReferenceException: Object reference not set to an instance of an object.
   at PaintDotNet.Effects.Gpu.RadialBlurGpuEffect.OnCreateConfigUI(PropertyCollection props) in D:\src\pdn\src_4_3_x\Effects\Gpu\RadialBlurGpuEffect.cs:line 76
   at PaintDotNet.Effects.Gpu.PropertyBasedGpuEffect.PaintDotNet.Effects.IPropertyBasedEffectHost.OnCreateConfigUI(PropertyCollection props) in D:\src\pdn\src_4_3_x\Effects\Gpu\PropertyBasedGpuEffect.cs:line 60
   at PaintDotNet.Effects.PropertyBasedEffectHandler`1.RelayCreateConfigUI(PropertyCollection props) in D:\src\pdn\src_4_3_x\Effects\PropertyBasedEffectHandler.cs:line 58
   at PaintDotNet.Effects.Gpu.PropertyBasedGpuEffect.CreateConfigUI(PropertyCollection props) in D:\src\pdn\src_4_3_x\Effects\Gpu\PropertyBasedGpuEffect.cs:line 55
   at PaintDotNet.Effects.PropertyBasedEffectHandler`1.RelayCreateConfigDialog() in D:\src\pdn\src_4_3_x\Effects\PropertyBasedEffectHandler.cs:line 33
   at PaintDotNet.Effects.Gpu.PropertyBasedGpuEffect.CreateConfigDialog() in D:\src\pdn\src_4_3_x\Effects\Gpu\PropertyBasedGpuEffect.cs:line 49
   at DynamicDraw.WinDynamicDraw.CmbxChosenEffect_SelectedIndexChanged(Object sender, EventArgs e)
   at System.Windows.Forms.ComboBox.OnSelectedIndexChanged(EventArgs e)
   at System.Windows.Forms.ComboBox.WmReflectCommand(Message& m)
   at System.Windows.Forms.ComboBox.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, WM msg, IntPtr wparam, IntPtr lparam)


Noteworthy things I'm doing
I subscribe to Application.ThreadException already and successfully catch thread errors from some plugins that crash

I'm not running any effects in the DoNotDisplay effect category. I run effects using CreateConfigDialog(), then setting the effect, the dialog owner, and token, then ShowDialog().

I pass in these environment parameters:

effectToDraw.effect.EnvironmentParameters = new EffectEnvironmentParameters(
    bttnBrushColor.BackColor,
    Color.Black,
    sliderBrushSize.Value,
    Document.DefaultResolution,
    new PdnRegion(EnvironmentParameters.GetSelectionAsPdnRegion().GetRegionData()),
    Surface.CopyFromBitmap(bmpCommitted));

 On a worker thread, just after the dialog opens or the effect token changes, I render the effect. (ROIs are well-tested and valid, accounting for the full image.)

effectToDraw.effect.Render(
    changedToken ?? effectToDraw.settings ?? new PropertyBasedEffectConfigToken(effectToDraw.propertySettings),
    stagedBmpSurface,
    committedBmpSurface,
    Utils.GetRois(bmpCommitted.Width, bmpCommitted.Height));

All built-in effects work except radial blur, which crashes and isn't caught by the application-level thread exception handler (there are 2 effects that have no dialog and don't give results, but that's probably my fault, and similar ones like Sepia still work).

Custom effects work, both property and non-property based, but with varying success. Effects like PasteAlpha will indefinitely hang paint.net and I really, really need a solution to those because I don't want people losing work and I really don't want to maintain a whitelist of working plugins. Has anyone besides ScriptLab, EffectLab and myself tried to run any effect programmatically? Do you see anything wrong with my approach? Any thoughts on what could be failing? All advice welcome!

Link to comment
Share on other sites

1 hour ago, NinthDesertDude said:

Noteworthy things I'm doing
I subscribe to Application.ThreadException

 

Do not do this. It is not acceptable to subscribe to that event -- you will cause massive problems if you do anything wrong, or if you do not unsubscribe from the event. You are attempting a global solution to a local problem.

 

Your job is not to catch and handle exceptions from plugins that you are trying to host. If they crash, you crash. Maintain a list of blocked plugins instead.

 

By "Effect Lab" do you mean pyro's ScriptLab? It was doing a ton of bad things that resulted in it not surviving the migration to .NET 5 / PDN 4.3.

 

Again: Do. Not. Subscribe. To. That. Event. I will block your plugin if you do this. If you're already doing this, I will need to block your current release in the next app update, so you will want to fix that ASAP.

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

Posted (edited)
38 minutes ago, Rick Brewster said:

Do. Not. Subscribe. To. That. Event.

Easy enough, but how is this worse than crashing paint.net to desktop by any other means? Go ahead and block BrushFilter which does this; the plugin is deprecated and my work here is to replace it, actually. This is the last piece before everything it did is part of DynamicDraw :)

I couldn't find "effect lab", so it's probably the same as ScriptLab. Long ago, I used effect lab to create the BrushFilter plugin. Today all I could find was ScriptLab, which isn't maintained now. All I see is TR's unmerged PR that's now itself out-of-date. Shame to lose such a plugin.


I think I have a good way to deal with maintaining a plugin list, if it comes down to it. Though I'm hopeful the plugin browser does everything just right and I can skate by with all plugins working or at least not crashing.

For clarity, DynamicDraw is safe and does not do this.

Edited by NinthDesertDude
Link to comment
Share on other sites

2 hours ago, BoltBait said:

The plugin browser runs effects programmatically.

https://forums.getpaint.net/topic/110458-the-plugin-browser-v14-oct-2-2021/

Is the source code available and/or can it be shared? I want to see if it's doing something differently

 

2 hours ago, toe_head2001 said:

Sounds like you're using an old version of Paste Alpha.  BoltBait fixed that bug very recently.

Confirmed, it's working correctly and without hanging the entire app. That's great! Thanks for the tip

Link to comment
Share on other sites

2 hours ago, NinthDesertDude said:

Is the source code available and/or can it be shared?

 

As far as I am aware, the source code is not publicly available.

You can use ILSpy to examine the decompiled output of the plugin code.

PdnSig.png

Plugin Pack | PSFilterPdn | Content Aware Fill | G'MICPaint Shop Pro Filetype | RAW Filetype | WebP Filetype

The small increase in performance you get coding in C++ over C# is hardly enough to offset the headache of coding in the C++ language. ~BoltBait

 

Link to comment
Share on other sites

10 hours ago, NinthDesertDude said:

Go ahead and block BrushFilter which does this; the plugin is deprecated and my work here is to replace it, actually. This is the last piece before everything it did is part of DynamicDraw :)

BrushFilter was already blocked because the way it was discovering and instantiating plugins just broke with PDN 4.3 / .NET 5. I forget the details.

 

You're using IEffectsService, right?

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

46 minutes ago, Rick Brewster said:

BrushFilter was already blocked because the way it was discovering and instantiating plugins just broke with PDN 4.3 / .NET 5. I forget the details.

You're using IEffectsService, right?

Wasn't sure if you meant blocked from continuing to future PDN versions, or if you meant effective-immediate removal of any download links and such.
Yes I'm using IEffectsService. Thank goodness for it, too. So much easier.

Link to comment
Share on other sites

19 hours ago, NinthDesertDude said:

Maybe someone can tell what's null here?

 

Looking at the code for that method, the only thing I can see that may be null is the EnvironmentParameters property.

The Radial Blur effect accesses the EnvironmentParameters.SourceSurface property to set the background image for the offset control.

 

Effects can access the EnvironmentParameters in CreateConfigDialog, OnCreatePropertyCollection and OnCreateConfigUI, so it needs to be initialized before those methods are called.

  • Upvote 1

PdnSig.png

Plugin Pack | PSFilterPdn | Content Aware Fill | G'MICPaint Shop Pro Filetype | RAW Filetype | WebP Filetype

The small increase in performance you get coding in C++ over C# is hardly enough to offset the headache of coding in the C++ language. ~BoltBait

 

Link to comment
Share on other sites

On 6/16/2022 at 4:44 PM, null54 said:

Effects can access the EnvironmentParameters in CreateConfigDialog, OnCreatePropertyCollection and OnCreateConfigUI, so it needs to be initialized before those methods are called.

You've got some really insightful psychic debugging skills there, null54. I was creating the property collection earlier, thinking that was safe to do. Now it works fine.
 

On 6/16/2022 at 4:46 PM, Rick Brewster said:

Pretty sure you're not setting the Services property

I do have it set actually; I just didn't mention it in the post. Accidental omission. It's set at the same time as environment parameters.

Now all I have to do is get an enormous list of all the up-to-date plugins and go through that whole rigamarole again to see what's working...thanks for the help, guys.

Link to comment
Share on other sites

3 hours ago, Rick Brewster said:

Are you setting Services with your own implementation, or with the Services that Paint.NET gave to you for your Effect class?

Right now I do this:

effectToDraw.effect.Services = this.Services;

let me know if I don't need to set it, and when it gets set would be very good to know in that case, to make sure I won't initialize it too late or something. There's a 2017 thread saying the Services would be set up correctly as long as the effect was set up correctly, but at some point it was either scriptlab or plugin browser which had this line of code that I borrowed, since they render plugins correctly in practice and I was taking implementation over documentation. So yeah, whatever the correct way to do it would be best to know.

Link to comment
Share on other sites

Yeah that's the right way to do it.

 

I may introduce an IEffectsService2, hopefully in 4.4 because I've got a bunch of changes I want to make to the Effect system and class hierarchy. At that point I may change the way things initialize, and make IEffectsService obsolete, but that kind of breaking change will be on an interface boundary and you won't have existing code broken (as in, it won't break plugins that are already installed on end-user's systems).

  • Upvote 1

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

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