5

Closed

SpriteBatch doesn't unset the last texture it binds

description

If you create a setup where you draw to a render target and then use SpriteBatch to draw that render target to the back buffer, after your first draw loop, every time you call ID3D11DeviceContext::OMSetRenderTargets to bind your render target to the pipeline you will get warnings from the runtime (assuming you created your device with D3D11_CREATE_DEVICE_DEBUG) because the SRV of your render target is still bound as an input from the last call to PSSetShaderResources by SpriteBatch.
 
I was able to eliminate these warnings by adding the following two lines of code to SpriteBatch::Impl::End() right before the mInBeginEndPair = false; statement:
 
const static std::unique_ptr<ID3D11ShaderResourceView*> nullSRVBind(new ID3D11ShaderResourceView*(nullptr));
mContextResources->deviceContext->PSSetShaderResources(0, 1, nullSRVBind.get());
 
Basically, just create a single element array of pointers to ID3D11ShaderResourceView interfaces with the sole array element being a nullptr and then set that on the device context to unbind the last texture that was used. I used a static unique_ptr for convenience. If any of this doesn't make sense, just let me know!
Closed Feb 5, 2013 at 11:20 PM by ShawnHargreaves
I recommend using ID3D11DeviceContext::ClearState (http://msdn.microsoft.com/en-us/library/windows/desktop/ff476389%28v=vs.85%29.aspx) any time you need to unbind resources that may have been left behind by previous rendering.

Since there is an existing device context API for doing this, there is no need to add this functionality to individual rendering APIs such as SpriteBatch.

comments

ShawnHargreaves wrote Jul 8, 2012 at 5:46 PM

This is potentially a broader issue than just the input texture, though. SpriteBatch also doesn't unbind it's vertex buffer, index buffer, shaders, or constant buffer.

My question is, should it? Sometimes you may want things to be unbound, but many times you won't care (or will be immediately replacing them with different bindings in any case). SpriteBatch cannot know which is the case, and unbinding resources is not free...

I'm really on the fence on this one.

BobTacoInd wrote Jul 9, 2012 at 1:27 PM

Everything other than the texture is internal to SpriteBatch so to me it doesn't really matter whether those stay bound. Though I didn't check to see if the vertex buffer and index buffer are unbound when you need to update them and if they would need to be (I hadn't seen any error messages but I haven't really flexed it like that either).

Focusing just on the texture, perhaps it's make sense to give SpriteBatch::End a signature to the effect of SpriteBatch::End(bool unbindTextures = false); so that people get the default no-unbind just leaving their code as is but can opt-in to unbinding for the case where they're using a render target that they're then using SpriteBatch to draw such as in my situation.

The reason I'm using a render target is that it makes it easy to target a fixed resolution (say 1366x768 or 1920x1080) and then scale with anisotropic filtering to the output window dimensions in order to get a good output without needing to create three separate sets of texture assets. If you have a better solution to that issue, though, I'm open to suggestions.

walbourn wrote Oct 3, 2012 at 8:03 PM

How about an explicit method for 'Unbind' so if you need this behavior, you can call sprite->Draw() then sprite->Unbind()? That way the Unbind() is just undoing exactly what Draw is using rather than having to know which things it does and doesn't set?

BobTacoInd wrote Oct 3, 2012 at 9:15 PM

I think an explicit Unbind would work just fine, yeah. Anyone who's using a render target should be able to figure out to use it. (In theory they could just unbind the textures themselves, though this would provide a nice, safe way for developers who might still be learning DX11 and thus could be uncomfortable with changing things on the Immediate Context directly).

walbourn wrote Feb 5, 2013 at 11:07 PM

Why not just use ClearState on the context you used to draw. All references to the resources and context in SpriteBatch uses Microsoft::WRL::ComPtr so the lifetime ownership cleanup is automatic.