Using HLSL shaders in a WinForms app



  • Ok, I have a stupid hobby project which basically just draws nice-looking wallpapers for my computer. Right now, it's written in plain ol' WinForms, and basically draws the wallpapers pixel-by-pixel. It's not very fast.

    The other day, for an unrelated project, I was learning me some pixel shaders, and a lightbulb turned on in my head, and I realized almost everything my wallpaper program was doing could be done by the GPU in a pixel shader! Which is great, but here's the problem: I can't figure out how to use a pixel shader from a normal WinForms app. I added in all the XNA references that deal with graphics, and attempted to use what I know of XNA to accomplish this.

    It amounts to this:

    namespace WallpaperMaker
    {
    	public class ShaderHelper
    	{
    		private GraphicsDevice _graphicsDevice;
    		private SpriteBatch _spriteBatch;
    
    	private Effect _distanceToPoint;
    	
    	public ShaderHelper( IntPtr windowHandle )
    	{
    		PresentationParameters presentParams = new PresentationParameters();
    		presentParams.IsFullScreen = false;
    		presentParams.BackBufferWidth = 1024;
    		presentParams.BackBufferHeight = 768;
    
    		presentParams.DeviceWindowHandle = windowHandle;
    		
    		_graphicsDevice = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, GraphicsProfile.HiDef, new PresentationParameters());
    	}
    
    	public Image DistanceToPoint(float x, float y, float cutoffDistance, int resultWidth, int resultHeight)
    	{
    		RenderTarget2D target = new RenderTarget2D(_graphicsDevice, resultWidth, resultHeight);
    
    		_graphicsDevice.SetRenderTarget(target);
    
    		_spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
    		_distanceToPoint.CurrentTechnique.Passes[0].Apply();
    		_spriteBatch.Draw(new Texture2D(_graphicsDevice, resultWidth, resultHeight), Vector2.Zero, XnaColor.White);
    		_spriteBatch.End();
    
    		MemoryStream imageStream = new MemoryStream();
    		target.SaveAsPng(imageStream, resultWidth, resultHeight);
    		Image result = Image.FromStream(imageStream);
    		
    		return result;
    	}
    }
    

    }

    Yes, this code has several problems-- for one thing I'm never actually loading the shader.

    But the real problem is that my code for setting the graphics device doesn't work. After calling this line:
    _graphicsDevice = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, GraphicsProfile.HiDef, new PresentationParameters());
    _graphicsDevice is null. Yet, there's no exception throw, no crash-- no error of any kind!

    What the holy heck is going on? How the fuck is that line of code failing without either crashing or throwing an exception?

    Note that I don't even really want a GraphicsDevice for what I'm doing-- I just need to render into a RenderTarget2D and spit it out as a bog-standard WinForms Image object. Am I doing something way over complicated and retarded here?



  • Oops.

    It's always the most retarded thing, isn't it? I set up presentParams all correctly, then forgot to pass it into the constructor for GraphicsDevice!... so now I have (what appears to be) a correct GraphicsDevice. From that I should be able to get a SpriteBatch, which leaves only "how in heck do I load Shaders without a XNA Content Pipeline?"



  • Ok, so I've found that Effect has a constructor that takes an array of bytes, which the documentation calls "the effect code" (it doesn't, natch, tell whether that's the compiled or uncompiled shader code, because that would actually be useful information to have). So I load in my shader as a text file, and pass it into the constructor as a byte array and--

    Once again, the function just stops working. No exception. No crash. No indication of error of any kind. It just hits the line:
    _distanceToPoint = new Effect(_graphicsDevice, EmbeddedTxtToByteArray("test_shader.txt"));
    and stops cold. Ugh.

    How the holy fuck are you supposed to debug this shit when it doesn't throw any exceptions?



  • Which version of XNA are you using.



  • @delta534 said:

    Which version of XNA are you using.

    4, which apparently removed the shader compiler from the distribution. Meaning, nobody would actually be able to run the resulting program when compiled. :( (Is it me, or is XNA getting shittier and shittier over time?)

    Now I'm rethinking the whole deal. The WinForms UI is pretty simple-- it might be a better idea to make the whole thing a XNA "game" (so it's redistributable) and write a quick & dirty UI in XNA. I haven't decided for sure yet, though.

    But the real question is, why is it so hard to use a simple pixel shader in WinForms? You'd think tons of graphic apps would be doing this... which is what makes me think there's a real simple way I'm missing.



  • Yes it is getting shittier in the name of cross platform compatibility. As for using a pixel shader in winforms, I have no idea on how to do it. Though I would not change it to an XNA game, I would change it to a WPF app. It has shaders as well. The class resides in System.Windows.Media.Effects.



  • @delta534 said:

    Yes it is getting shittier in the name of cross platform compatibility. As for using a pixel shader in winforms, I have no idea on how to do it. Though I would not change it to an XNA game, I would change it to a WPF app. It has shaders as well. The class resides in System.Windows.Media.Effects.

    Ah! Thanks.

    So to ask a probably stupid question, if you're a WinForms paint program like Paint.NET, and you're doing some somewhat-difficult-to-apply effect, are you still doing it entirely in the CPU? Because that seems stupid and inefficient... and it seems like a huge oversight on Microsoft's part.

    Edit: And yes, their reason for getting rid of the shader compiler is that they couldn't ship it on Xbox, so (supposedly) game developers would write games using the shader compiler, then submit it to Xbox Live Arcade or whatever, and then they'd have to rewrite tons of code because they weren't aware the shader compiler wasn't on Xbox... whatever. That happened maybe once or twice, ever. It sounds like an excuse to me.



  • Based on my experience of attempting to write an effect plugin for paint.net, yes it is done on the cpu. There might be ways around it but I'm not sure. I blame winforms being a wrapper for the windows api.


  • Garbage Person

    @delta534 said:

    Yes it is getting shittier in the name of cross platform compatibility. As for using a pixel shader in winforms, I have no idea on how to do it. Though I would not change it to an XNA game, I would change it to a WPF app. It has shaders as well. The class resides in System.Windows.Media.Effects.
    You just sold me on WPF. Dick. Now I have to actually learn something.



  • Since this thread is alive again, I did some research and found the only way to use shader effects in Silverlight/WPF is to compile them into a specially-constructed .dll first. I couldn't find much documentation on this, and since it's so goddamned inconvenient, I gave up at that point.

    Right now the ONLY environment where you can simply include a shader in a project, have it compiled as part of the build process, and show the results on the screen immediately with no hassle is fucking XNA. Why!!!???

    Edit: oh and for my app? Since speed wasn't critical, I just implemented my own "shaders" in C# classes. It's slow as molasses, but the results are the same.


  • Garbage Person

     Did you try any of the DirectX bindings for managed code (SlimDX looks DELICIOUS) or perhaps consider a GPGPU lib (Brahma is the only one I've seriously looked at, it uses LINQ)?



  • God damn it, I should have looked at the docs more. The bright mind at microsoft who thought it would be a good idea to prevent people from passing in uncompiled hlsl in C# can go screw himself or herself. I don't want to download the directx sdk just to use shaders in WPF.



  • Yup, that was pretty much my opinion as well.

    Why is there this huge-- I dunno what you call it, misconception? Assumption? Prejudice? Ass-headed-ness?-- that shaders are only useful for video games? Where does it come from? Why is it still around? Why do programs that do nothing but shovel huge amounts of pixels around, like Paint.NET and Photoshop, not make use of them?

    I can think of a million things shaders would be useful for, even in a pretty basic office app, say Excel. The language, and passing textures/params to it, is pretty basic and easy-to-learn. Its limitations can be overcome by doing rendering in multiple passes. This is all figured out. It's not like this is shiny-new, this is old and proven. Plus, even more annoying, we know VS2008 is capable of compiling shaders during the build process-- it fucking does it when you're using XNA! Why the fuck won't it do it when using WPF?

    But the guys who are out there making APIs and tools apparently have NO FUCKING IMAGINATION WHATSOEVER. It angries-up the blood.



  • Using the gpu for computations has taken off in the scientific and academic realm, so there is hope it eventually comes to the desktop.

    I think the reasons most things do not take advantage of shaders are: it is cumbersome and/or slow to read back pixels from an image, most programmers are not used to how shaders work and there is the issue of not every computer having a good gpu to take advantage of.



  • @delta534 said:

    I think the reasons most things do not take advantage of shaders are: it is cumbersome and/or slow to read back pixels from an image, most programmers are not used to how shaders work

    These are true.

    @delta534 said:

    most programmers are not used to how shaders work and there is the issue of not every computer having a good gpu to take advantage of.

    1. Any modern OS almost requires a GPU capable of running shaders

      2) For situations in which the GPU is unavailable (remote desktop, webex, etc) shaders can easily, easily be executed by the CPU with a minimum of code (which is how I built my "shader"-using desktop wallpaper maker)


  • @blakeyrat said:

    1) Any modern OS almost requires a GPU capable of running shaders

    2) For situations in which the GPU is unavailable (remote desktop, webex, etc) shaders can easily, easily be executed by the CPU with a minimum of code (which is how I built my "shader"-using desktop wallpaper maker)
    I'll give you that, though I was thinking more about old xp boxes than servers.



  • I think the concept behind shaders is that you don't always roll your own, there are a limited number of standard useful effects to apply (blur, sepia, contrast adjust...), and if the shader code is already written and compiled, you just drop it in. Just like Visual Studio dosn't (to my knowledge) include a font editor or a texture editor. Those are resources you get from specialists.

    I've done shaders in WPF, the real power of them is they can apply to specific controls, not just screen areas, and can actually remap interactable areas, so that if a zoom effect is applied over a button, the clickable area of the button can expand as well, or if you had two buttons in a panel and flipped them in a shader, the clickable areas swap as well (if it's setup properly).

    I made an app for Microsoft Surface that put virtual round lenses over a selected image that magnified, sharpend, inverted, desaturated, and such the portion of the image they were over. Very cool, but not terribly practical.

     http://wpffx.codeplex.com/ is where I started, it really taught me a lot.



  • OpenCL is supposed to be the solution to most problems with using shaders for things other than displaying textures. Unfortunately when I tried it (about a year ago) the driver support was still horrible, some features were still broken and the C++ bindings were incomplete. Also, they went the opposite route to the compiled shaders: You need to JIT compile your sources every time and the output is only compatible with one GPU driver (NVidia or ATI at the moment).

    If you don't care about being compatible with multiple targets there's also NVidia CUDA which has less bugs but a weirder API.



  • In case you ever come back to this, the Silverlight 5 beta has direct support for shaders - and you compile the shaders as a part of the build process, just like XNA. It's got other XNA stuff too, like Matrix and the content pipeline.

    However, you can't use the .dlls from your XNA 4 install, you have to use the ones built against Silverlight 5 which come with it, but are installed to a different location. They also moved around some of the classes to different namespaces than what you're used to in XNA. No big deal, but I lost some time figuring that out.


Log in to reply