[SOLVED] Problem: Drawing a RenderTarget2D to another RenderTarget2D

UPDATE: I’m running Monogame 3.5 & DesktopGL

I got a bit of a curious problem when I dabbled with drawing to a RenderTarget2D and I’ve tried a few different setups and switched a lot of parameters, but perhaps I’m missing something or this is not how it’s intended to be used.

Say I have a RenderTarget2D that I call rtScreen, I draw everything to this RenderTarget2D because I want to be able to draw this scaled up. So the way I do that is:

SetRenderTarget(rtScreen);
// .. draw stuff
SetRenderTarget(null);
spriteBatch.Draw(rtScreen, etc..);

This works great, no problems!

Now, I want to be able to draw a textbox, which consists of different textures, and every texture is 8x8, however I want to draw a textbox that is not limited to a multiple of 8, so what I want to do is draw everything to separate textures and then draw them to rtScreen one by one.

So, my attempts have been something similar to this:

SetRenderTarget(rtTextbox);
// ..draw textbox-stuff
SetRenderTarget(rtScreen);
spriteBatch.Draw(rtTextbox, etc..);
SetRenderTarget(null);
spriteBatch.Draw(rtScreen, etc...);

This draws the rtTextbox in the correct size in the rtScreen (if there’s a background color, i can see it drawing the texture at the correct size), however the actual drawn stuff inside rtTextbox has been garbled and is a one-pixel long line.

Now, I am currently writing this while I’m not at the luxury of providing images, so this will have to be a question that might make people immediately go “wait, you are doing things wrong, here’s how it’s supposed to go”. But for anyone who goes “wait, it does that? let me investigate” I’ll have to later supply with more reasonable examples and/or images of the result.

So, finally, I guess that I could (perhaps should?) use Silhouette functionality, and before I start opening up that can of mystery ~ is drawing in memory like this not supposed to work? And yes, I have enabled Preserve on RenderTargetUsage, I’ve tried changing SurfaceFormat, I’ve tried changing DepthStencilFormat, and yes they’ve all been synchronized to be the same so I haven’t drawn with one set of parameters in one RenderTarget to another, they were all created from the same GraphicsDevice. I’ve tried creating a separate SpriteBatch to draw the stuff to one RenderTarget… let’s say I’ve put a few hours into this and I am finally just throwing this in here and hope that someone knows what I’m dealing with. :slight_smile:

If I’ve been rambling, been unhelpful, explained the example badly and/or other stuff, sorry, just tell me and I’ll take your feedback on it.

Regards,
S

1 Like

Are you ending draw calls before setting new RT?

I’ve tried both. I’ve had a separate SpriteBatch do a Begin/End cycle individually for the Textbox part, I’ve tried Beginning/End my main SpriteBatch used for all the drawing (as in, begin, draw textbox, end, begin, draw everything to screen, end) and I’ve tried the way my example shows it - drawing everything in one go, also while using SortMode.Immediate

Also thank you for showing interest, it’s appreciated. :slightly_smiling:

Regards,
S

private void DrawRTToTarget(RenderTarget2D map, RenderTarget2D target)
        {
            
            _graphicsDevice.SetRenderTarget(target);
            _spriteBatch.Begin(0, BlendState.Opaque, SamplerState.PointClamp);
            _spriteBatch.Draw(map, new Rectangle(0, 0, map.Width, map.Height), Color.White);
            _spriteBatch.End();
        }

this exact code works for me, sorry haven’t really checked if you have a special problem

1 Like

Are you using Monogame 3.5 and also, SharpDX or OpenGL?

3.5 sharp dx though.

Alternatively I also implemented the spritebatch draw as a fullscreenquad myself, but that makes no big difference.

If your textRT is somewhat “broken” - make sure you don’t assign a custom shader in your spritebatch.begin or .draw() unless of course that is a pass through shader, which would look like this

Texture2D ScreenTexture;

sampler TextureSampler = sampler_state
{
    Texture = <ScreenTexture>;
    Filter = Anisotropic;
};

float4 PixelShaderFunction(float4 pos : SV_POSITION, float4 color1 : COLOR0, float2 texCoord : TEXCOORD0) : SV_TARGET0
{
    float4 color = tex2D(TextureSampler, texCoord.xy);
    return color;
}

//-------------------------- TECHNIQUES ----------------------------------------
// This technique is pretty simple - only one pass, and only a pixel shader
technique Basic
{
    pass Pass1
    {
        PixelShader = compile ps_4_0 PixelShaderFunction();
    }
}

and then the modified c# function is

_graphicsDevice.SetRenderTarget(target);
            _spriteBatch.Begin(0, BlendState.Opaque, SamplerState.PointClamp, null, null, _passThroughEffect, null);
            _spriteBatch.Draw(map, new Rectangle(0, 0, map.Width, map.Height), Color.White);
            _spriteBatch.End();

Ok, I’m pretty sure I’ve tried what you’re suggesting, because that’s a very simple and straight forward approach, and it’s the first stuff I’ve tried. I wonder if it’s an OpenGL thing, I guess I could just make a little test-project when I get home and try the same example in DirectX and OpenGL.

Thanks for the input!

You don’t need to. With the default RT’s are only cleared when you set them as a target and setting this to Preserved might impact performance (just a detail, but good to know). Why do you need this?

On spritebatch: You should use different spritebatch cycles and set the target outside the cycles. Spritebatch should flush before changing the RT and I don’t think GraphicsDevice lets it know when that happens.

Not sure what else it could be. Could you share some more code of the textbox rendering? Also try drawing the textbox rt directly to the screen just to make sure the problem is in rendering to the textbox rt.

As I read and have experienced is that the default is that the RTs are cleared because of backwards compatibility with Xbox and such, legacy from XNA, and I know about the performance hit, and it’s negligible in this case, all I need is for it to work at all.

I’ve tried this, unless you mean something special about “flush”, I mean, I have tried letting the spritebatch End before I switch the RenderTarget for the GraphicsDevice.

I stream when I code so I recorded most of these things, I was thinking I might be able to just clip out a very short segment, and I show easily how I comment out the same render code, one targeting the rtScreen and one targetting the rtTextbox and as long as I draw on the rtScreen (which is then drawn on no rendertarget) it works, but if I render it to another rendertarget which is later rendered to the screen, is when it breaks.

If you could share the relevant code or a minimal project that shows the issue I’m willing to try and figure this out.

1 Like

Ok, so I figured I’d just make a separate project and start from scratch and try to do the absolute basics (so i could give you something to look at) and I actually got it to work. Which both surprises me and embarrasses me. I don’t understand why it’s not working in my project, but at least that answers one important question - it’s supposed to work - i’m just being dumb. I’ll just set this one as solved and sorry for wasting anyone’s time, I should have just made this test-project from the start and I guess thanks to all of this I got there eventually and one step closer to solving it. Thanks guys :slightly_smiling:

TL;DR - nothing wrong with drawing RenderTarget2D on another RenderTarget2D and then onto the screen. I’ve just been having some weird special case I need to solve.

Woops :stuck_out_tongue: No problem! Good luck :slight_smile:

1 Like