How to incorporate an algorithm into a geometry shader.

You CBuffer is an illegal size. Has to be a multiple of 16 bytes (yours is 268, closest legal size is 272).

Also, you’re guessing at the location of elements in the CBuffer - they’re not tightly packed. You really need to use the shader reflection APIs to query where things actually are in the CBuffer.

If the CBuffer is not entirely made of float4’s then nothing will be where you think it is (well, the packing rules are documented, but you won’t have that stuff in your head - and the reflection APIs eliminate the need to).

The core of what you’re doing there is workable, you’re just botching CBuffer setup is all (just explicitly writing in 272 instead of params.sum will get you past the error, but won’t make your buffer correct).


The absolute easiest way to plug a GS in is to just use MGFX as usual but inject the geometry shader after the EffectPass is applied but before you draw anything.

That only works on DX11 (GL uses whole program linking so you have to roll an entire raw shader library).

How would I do this? As in, how would I get access to the appropriate cbuffer for the GS?

Edit
Ah, don’t worry, I got it.

Grab all of them with the SharpDX API VertexShader.GetConstantBuffers, iterate the array and set them all into the GeometryShader.SetConstantBuffer(currentIdx, buffers[currentIdx])

Using the style you’ve been using:

// assume low-spec DX10 8 regs
// Requires enforcement that GS uses same CBuffers at the same registers as the VS, CBuffers the GS doesn't use are A-Okay, shader doesn't care
Buffer[] vsBuffers = ((Device)GraphicsDevice.Handle).ImmediateContext.VertexShader.GetConstantBuffers(0, 8);
if (vsBuffers != null)
{
    for (int i = 0; i < vsBuffers.Length; ++i)
        ((Device)GraphicsDevice.Handle).ImmediateContext.GeometryShader.SetConstantBuffer(i, vsBuffers[i]);
}

I managed to get the debug layer slightly working, but now I’m getting a C# exception…
I’ve updated my gist to include the changes to the code. GardeningGame is the game I’m aiming to eventually use this for, and Terrain.Water is basically just a flat primitive mesh with water-like colours. I don’t use the primitive effect in GardeningGame, instead I use the one in the gist.

@AcidFaucent
I figured it out! First of all, I was using the wrong settings in the sharpdx debug panel. Second of all, I needed to expose the internal shaders in monogame, and set them manually in my program. Third of all, I needed to look at the output of Visual Studio to find my errors (all of which were semantic mismatches) and now I get no errors or warnings, just a blank screen. :confused: I will update my gist shortly. The gist is now updated to include the most recent code.

RenderDoc time! If you’ve never used it before first you start render-doc then setup the info in the last tab before hitting launch. Which will run your program and you hit F12 to grab a frame, then inspect the frame.

That’ll tell you the full state of all of your draw calls so you’ll be able to see what’s up (you can do similar with PIX/VS-GFX-debugger if you prefer it). If everything looks okay in there then you’ve probably got a view-projection transform issue.

Edit: sure you updated it? you’ve got a lot of odd S_POSITION stuff in that shader still.

Yeah I’m sure I updated it. I had to use S_Position because of “duplicate sv_position” stuff. I’ll look into renderdoc tomorrow. Thanks!

So I tried RenderDoc but had no luck, so I used GPA. GPA tells me about a few problems but fixing those still doesn’t change the output.
I wanted to load the output from each of the shaders into a buffer that I can then save in my program to a file. This would allow me to debug the specific parts of the shader. Is there any way to create a writeable buffer in the shader that can then be saved in C#?

Is there any way to create a writeable buffer in the shader that can then be saved in C#?

Nope. Technically there’s a printf for HLSL but I doubt Monogame/SharpDX has the plumbing set up for it, requires some plumbing and a GPU supporting it for it to work.

Rigging up a tiny example. Almost done … the problem is the immediate context stuff. I derped out on you, you have to grab the right device context with reflection or tweak Monogame to expose it.

For shorthand, my example is showing it with the handle exposed, but that could be looked up once and stored.

Created a simple example with lighting done in the GS:

The Graphicsdevice.ContextHandle is shorthand for access to the internal _d3dContext of GraphicsDevice.

Also, note that github doesn’t allow / paths so \\ is used for the content files. ie. Effects/WaterShader.fx and Effects/WaterGS.hlsl

Pretty sure I bumbled my math somewhere (spends too much time in black), but hey … it’s a 40 minute quickie.

Hopefully this helps you out. Deliberately kept as light as possible so the shader isn’t doing anything fancy, just the basics in the VS/PS and then dot-prod’ing a light-vector in the GS.

1 Like

Hey, I looked at your code. I debugged and eventually found out that it was a problem with the distortion algorithm I was using. I copied it from a ThinMatrix video. Anyway, I found a noise function that fit my needs and was off! The end result is in my repo and here is an example of it working!

Looks good!

o/t: ThinMatrix tuts are probably some of the best out there, riding that fine line of exposition, code, and explanation just right.

1 Like

Hey there, another quick question, somewhere I heard that MonoGame doesn’t compile cbuffer parameters that aren’t used. Is this true?

Sort of. CBuffers are all or nothing, if you touch one piece of it the compiler has to take the whole buffer. Curiously, a parallel universe in which OpenGL is not a psychotic mess leaks into this one and GL Uniform Buffer Objects behave the exact same.

You’ll have to deal with CBuffers the hard way (ie. shader reflection API) if you have disparities between shader stages from unused/new cbuffers.

You can’t disable variable elimination, would be the stuff of horror-stories if you could (even a small uber-shader would blow the stack without even executing).

Why would this be?

And, I’m going to write a small interface and release it.
By any chance do you have any knownledge about using the MonoGame processors? I’m doing this:
Use the pipeline to build a dummy version of the effect at runtime (Using the default pipeline is kind of tricky to get the directories right.
Then expose the constant buffers in the effect and use the effect to generate the cbuffers for your parameters.
Then inject the right shaders and cbuffers into the GPU.

The problem I’m having is, I have an effect that has both the real version and the dumbed down version by using #ifdef and #ifndefs to check if using the pipeline or the DirectX compiler. But, since I want to use the pipeline on runtime, I have to add these manually. How would I do this? Also, I’m getting an error stating thatAccess to the path 'C:\Effect.xnb' is denied. any ideas as to why?
Here is my snippet that compiles the effect on runtime:

    public void LoadEffect(string fileName, GraphicsDevice GD)
    {
        Pipeline = new PipelineManager(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Path.Combine(Assembly.GetExecutingAssembly().Location, "\\tempBin"), Path.Combine(Assembly.GetExecutingAssembly().Location, "\\tempBin"));
        Pipeline.Profile = GraphicsProfile.HiDef;
        OpaqueDataDictionary ODD = new OpaqueDataDictionary();
        ODD.Add("Defines", "USINGMONOGAME");
        ODD.Add("Location", Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
        var BuiltContent = Pipeline.BuildContent(fileName, processorParameters: ODD);
        
        var ProcessedContent = Pipeline.ProcessContent(BuiltContent);
        var eff = new Effect(GD, ((CompiledEffectContent)ProcessedContent).GetEffectCode());
        this.InternalEffect = eff;
        File.Delete(".\\tempBin\\" + fileName.TrimEnd(".fx".ToArray()) + ".xnb");
    }

Thanks!

Actually I think I got it! Here is the code:

public void LoadEffect(string fileName, GraphicsDevice GD, string Defines)
{
    this.Pipeline = new PipelineManager(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Path.Combine(Assembly.GetExecutingAssembly().Location, "\\tempBin"), Path.Combine(Assembly.GetExecutingAssembly().Location, "\\tempBin"));
    Pipeline.Profile = GraphicsProfile.HiDef;

    EffectProcessor EProcessor = new EffectProcessor();
    EffectContent EContent = new EffectContent();
    EffectImporter EImporter = new EffectImporter();

    PipelineImporterContext ImporterContext = new PipelineImporterContext(Pipeline);

    var ImportedData = EImporter.Import(fileName, ImporterContext);

    EContent.EffectCode = ImportedData.EffectCode;

    string importerName = null, processorName = null;
    Pipeline.ResolveImporterAndProcessor(fileName, ref importerName, ref processorName);

    var contentEvent = new PipelineBuildEvent
    {
    SourceFile = fileName,
    DestFile = fileName + ".built", //Never actually used
    Importer = importerName,
    Processor = processorName,
    Parameters = new OpaqueDataDictionary(),
    };

    ContentProcessorContext ProcessorContext = new PipelineProcessorContext(Pipeline, contentEvent);

    EProcessor.Defines = Defines;
    EContent.Identity = ImportedData.Identity;

    CompiledEffectContent ProcessedContent = EProcessor.Process(EContent, ProcessorContext);
    Effect EndResult = new Effect(GD, ProcessedContent.GetEffectCode());
    CommonEffect.InternalEffect = EndResult;
}

hi, i just try to compile the project from your [repo] (https://github.com/OptimisticPeach/GardeningGame) and seem like there is some asset missing. can you help?

Ah! I’m sorry for the late reply. I uploaded the project solely for its security and to use in other examples. In theory you could run it, but it is in fact missing some assets (Which I bought and am therefore not allowed to release, hence this entry in my gitignore) For this, I can make a new branch to remove the part which would depend on the assets. This would basically limit it to the use of the water example.

On a side note, if you have questions about geometry shaders and compute shaders and the like (Or really most things that aren’t exposed publicly in the Monogame API) you should contact @AcidFaucent who was an excellent help throughout my endeavor. I’ve since moved on to other things (Rust to be specific. It’s most definitely a great language!) so I don’t quite recall most things about this :sweat_smile:

@11110 I’ve updated my repo to include a cleaned branch! That should compile and is quite a bit cleaner. This will remain a branch though as though it removes most of the “game” code and results in the following:

  • A circular patch of water
  • With alternating triangles like so (With colour for effect):
  • And moving surface
  • The shader which controls it is located here.
    • This shader updates on changes so the changes can be viewed live!
    • This shader includes a geometry shader stage

With some other random algorithms and things scattered around:

3 Likes

Hi @AcidFaucent. Thanks for the great example.

Unfortunately in my case the returned buffers are all null. Could you think of a reason why this might be?

Thank you : )

Edit: Nevermind. They are empty during the first frame only.