I ported an app I use a lot to MonoGame from XNA. The app uses a material caching system, so multiple meshes can share materials.
It used to create a new rasteriser state every time the material was applied to the shader. This did not create garbage collection problems in XNA. In MonoGame it made the app unusable.
So I made the rasteriser states part of the material to avoid the garbage issue, and now I cannot render the mesh because I constantly get errors because you cannot modify a rasteriser state after it has been bound to a graphics device.
I tried setting the rasteriser state to a pre-generated default state after each apply of a material, but that made no difference.
I cannot use the trick I used above, because two meshes calling Material.Apply with the same material is perfectly valid. So the old state is the same object as the new state.
I am going to write a work around that builds the rasteriser state in advance, then only changes it when an app setting is changed, but someone needs to look at this.
I never had problems with garbage collection before, it’s constant now with MonoGame.
I never had a binding issue with rasteriser states before.
Something is wrong here. If you can point be at the relevant source code I will have a look for you.
Forgive me if you already know this, but since you mentioned that you got the error cant modify the rasterstate on the graphics object. I thought i should post a example as the syntax for caching renderstates is a little un-intuitive.
Excuse the lack of code casing below.
// some often used states
public static class MyStates
{
public static RasterizerState rs_regular = new RasterizerState()
{
FillMode = FillMode.Solid,
CullMode = CullMode.CullCounterClockwiseFace,
MultiSampleAntiAlias = false
};
public static RasterizerState rs_solid_nocull = new RasterizerState()
{
FillMode = FillMode.Solid,
CullMode = CullMode.None
};
public static RasterizerState rs_solid_ccw = new RasterizerState()
{
FillMode = FillMode.Solid,
CullMode = CullMode.CullCounterClockwiseFace
};
public static RasterizerState rs_solid_cw = new RasterizerState()
{
FillMode = FillMode.Solid,
CullMode = CullMode.CullClockwiseFace
};
public static RasterizerState rs_wireframe_cullnone = new RasterizerState()
{
FillMode = FillMode.WireFrame,
CullMode = CullMode.None
};
public static DepthStencilState ds_depthtest_lessthanequals = new DepthStencilState()
{
DepthBufferEnable = true,
DepthBufferFunction = CompareFunction.LessEqual
};
public static DepthStencilState ds_depthtest_less = new DepthStencilState()
{
DepthBufferEnable = true,
DepthBufferFunction = CompareFunction.Less
};
public static DepthStencilState ds_depthtest_greater = new DepthStencilState()
{
DepthBufferEnable = true,
DepthBufferFunction = CompareFunction.Greater
};
public static DepthStencilState ds_depthtest_disabled = new DepthStencilState()
{
DepthBufferEnable = false,
};
public static SamplerState ss_point_clamp = new SamplerState()
{
Filter = TextureFilter.Point,
AddressU = TextureAddressMode.Clamp,
AddressV = TextureAddressMode.Clamp,
AddressW = TextureAddressMode.Clamp
};
}
then in draw
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
GraphicsDevice.DepthStencilState = Gh.MyStates.ds_depthtest_less;
GraphicsDevice.RasterizerState = Gh.MyStates.rs_solid_cw;
basicQuadDrawEffect.SetQuadDrawParameters(meshObject.GetTheWorldViewProjection, texture);
if (keyoptWireframe == 1)
GraphicsDevice.RasterizerState = Gh.MyStates.rs_wireframe_cullnone;
else
GraphicsDevice.RasterizerState = Gh.MyStates.rs_solid_nocull;
// ^^ there is a bit of overhead to setting the states to the device.
basicQuadDrawEffect.SetQuadDrawParameterTexture(texture2);
if (keyoptOldMesh == 1)
DrawMesh(basicQuadDrawEffect.testEffect);
else
{
if (keyoptOldMesh == 3)
DrawMesh3(basicQuadDrawEffect.testEffect);
else
DrawMesh2(basicQuadDrawEffect.testEffect);
}
basically you want to batch i.e. set the state then draw everything that uses that state.
I do initialise the raster state when I create it, but I also allow dynamic changes to raster state.
I am pretty sure the binding issue is either a bug, or we have a design I hate.
Think of it this way…
Create RasterStateA.
Create RasterStateB.
Bind RasterStateA and renderer. All good.
BInd RasterStateB and render. All good.
Modify RasterStateA CRASH (Cannot modify a rasterstate after being bound to a GraphicsDevice)
Once you bind RasterStateB you should be able to modify RasterStateA. unless they are using the memory block in place instead of doing a copy.I personally hate that practice. I have had to fix so many hard to find bugs in games caused by this practice.
The problem definitely is the garbage collection. Not only can I see the GC in visual studio, I can run reports and see what garbage is being collected.