This project has moved and is read-only. For the latest updates, please go here.

Reusing texture resources in WP8

Oct 13, 2013 at 4:18 PM
I am using DX::ThrowIfFailed(DirectX::CreateDDSTextureFromFile(m_d3dDevice.Get()... function to load very large sprite sheets (>26MB, sized about 2600+ times 2600+ pixels) to ID3D11ShaderResourceView * type variables. I'm also using SpriteBatch draw calls for rendering. I need to change the sprite sheets occasionally and it seems that releasing the texture with
texture->Release();
texture = 0;
and immediately loading a new causes an 'out of memory' error. If I wait a while, e.g. let my render loop run once (without the missing sprite of course), then the loading succeeds. I asked about this on msdn and they suggested it could be a heap issue that needed some processing time. But they also suggested reusing the texture resources and just changing the content as my sprite sheets are of equal size. How could I load a new equal sized .dds into the same memory allocated by previous call to CreateDDSTextureFromFile()? Or is there some other way to circumvent this issue?

Best regards,
Johannes
Oct 14, 2013 at 7:04 AM
Edited Oct 14, 2013 at 7:09 AM
Direct3D resources are lazily released, so you could well get a case where both textures exist simultaneously. One possible work-around is to use ID3D11DeviceContext::Flush, but that may not be reliable.

How often do you update this texture? You could use CreateDDSTextureFromFileEx to specify the DYNAMIC usage and the ability to Map the texture for updating in place, but this could have some performance implications.

Another option would be to use CreateDDSTExtureFromFileEx to load into a STAGING resource and then CopyResource it to the actual rendering texture, but it largely depends on why exactly you are getting 'out of memory'.

26MB with 2600 x 2600 implies a 4-byte-per-pixel format. A BC3 version would be 6.7 MB if you need alpha channel information (half-that if you use BC1 if there's no alpha or only 1-bit transparency). That would probably be the best solution. See Texture Block Compression in Direct3D 11.
Oct 14, 2013 at 7:53 AM
Thanks for the reply. I cannot do any compression as long as windows phone 8 does not support them. Hence every pixel counts. I have lots of fully transparent pixels in my images, but they're as expensive as any other.

I need to load quite a bunch of other smaller .dds images in addition to those large sprite sheets. I ended up doing the "packed" sprite sheets instead of having even more images because I was about to run out of texture memory anyway. It is a game and when it starts those two large ones may have to be reloaded based on what characters are in the new game. So luckily, it is not real-time swapping and currently I have circumvented the issue by letting the render loop run once between the release and reload. Before when I had the large sheets in some 30+ smaller images I could do the release and reload without any delay but apparently heap needs to be reordered somehow when these large images are loaded. I'm currently testing on Nokia Lumia 920.

Could you give an example on how the CopyResource or mapping texture for updating actually works and is used?

Best regards,
Johannes
Oct 14, 2013 at 5:05 PM
Edited Oct 14, 2013 at 5:09 PM
Windows phone 8 supports BC1, BC2, and BC3. These are standard with all Direct3D 11 Feature Levels. BC4 and BC5 require Feature Level 10.0+, and BC6H/BC7 require Feature Level 11.0+. Note BC1 is the same thing as DXT1, BC2 is DXT2/DXT3, and BC3 is DXT4/DXT5. BC1/DXT1 is designed to compress RGB data that is either fully opaque or fully transparent.

You can see some code using CopyResource with staging textures in DirectXTK's ScreenGrab module--it's doing the opposite, taking data from a texture and putting it into a file.

Dynamic textures work just like any other Map-able object. DirectXTK's PrimitiveBatch uses it to update vertex buffers.

You can also make use of UpdateSubresource for updates where you are not trying to render at the same time. DirectXTK's WICTextureLoader does this in the case of auto-gen mipmaps. Using UpdateSubresource in combination with BC1 or BC5 should work for your scenario. You could borrow heavily from the source in DDSTextureLoader to make it update an existing texture rather than create a new one.
Marked as answer by kleimola on 10/15/2013 at 11:36 PM
Oct 14, 2013 at 8:38 PM
Edited Oct 14, 2013 at 9:36 PM
Thank you for correcting me. I guess my beliefs came from the fact that e.g. this msdn page mentions only the missing compression methods. Also the "texconv" utility from DirectXTex does not produce compressed conversions.

However, I do not seem to succeed loading a .dds file compressed with BC1/BC2/BC3. I have tried to do the conversion with both GIMP and Paint.NET. Am I missing some configuration or failing a requirement? E.g. my images are not power-of-2.
Oct 14, 2013 at 10:58 PM
To BC compress, you need the dimensions to be a multiple of 4. Assuming it's a sprite sheet, you also don't want to use mipmaps.
Marked as answer by kleimola on 10/15/2013 at 11:36 PM
Oct 15, 2013 at 7:57 AM
That did it! Thank you! One final question. Does the BC compression only reduce the file size (and quality) due to less bits or will it also use less texture memory when loaded and rendered? And is there a difference in performance to render a non-compressed and BC compressed .dds?
Oct 15, 2013 at 7:19 PM
The primary value of the Direct3D BC texture compression formats is that they are supported natively in the graphics hardware and take up less space both on disk and in video memory. This is why they are not as compact as say JPEG or PNG because they are still usable directly by the hardware when texturing.

Generally BC formats are a bit faster than non-BC formats because they take up less space in the on-GPU texture caches.
May 8, 2014 at 8:13 PM
I am running into this same issue using CreateDDSTextureFromFile and running out of memory. I am unloading the textures from memory after each scene ends, but it appears they are not immediately releasing the memory. I tried using context->ClearState() and then context->Flush() as recommended by this link http://msdn.microsoft.com/en-us/library/windows/desktop/ff476425(v=vs.85).aspx (synchronously, once all the texture com pointers have gone out of scope) but I am now then throwing an exception in spritebatch.cpp at line 876.
The code is
deviceContext->RSGetViewports(&viewportCount, &viewport);

if (viewportCount != 1)
    throw std::exception("No viewport is set");
and it appears my viewport count is not 1 (maybe it was reset by clearstate() ?) does anyone know how to resolve this?
May 8, 2014 at 9:49 PM
You likely need to reset the viewport after the ClearState then...
May 8, 2014 at 9:56 PM
Thanks for the fast reply, did that and it fixed that exception but still hitting an out of memory exception, i'll keep digging!
May 19, 2014 at 1:23 PM
walbourn wrote:
Windows phone 8 supports BC1, BC2, and BC3. These are standard with all Direct3D 11 Feature Levels. BC4 and BC5 require Feature Level 10.0+, and BC6H/BC7 require Feature Level 11.0+. Note BC1 is the same thing as DXT1, BC2 is DXT2/DXT3, and BC3 is DXT4/DXT5. BC1/DXT1 is designed to compress RGB data that is either fully opaque or fully transparent.
It seems that a completely semi-transparent image (in my case a fog that is scrolled on top of other background graphics) cannot be compressed. All of the three compression formats produce opaque results in WP8 (a white cloud in my case) even though BC2 and BC3 looks correct in paint programs. Only uncompressed is rendered correctly. What could be the cause?
May 19, 2014 at 4:45 PM
It depends on exactly how you are rendering the image. See this tutorial.
May 19, 2014 at 7:54 PM
Yes, but...

I am not using any shaders, just the DirectXTK's spritebatch to render images. And why does it work perfectly with non compressed .dds but not with any compression applied? This is not a big issue with small sprites, but with larger ones compression would be needed to conserve memory.
May 19, 2014 at 11:06 PM
There are any number of pipeline configuration states that may need to be setup correctly, and there's a question of premultiplied vs. straight alpha as well.

See the wiki page for more information.
Marked as answer by walbourn on 5/20/2014 at 9:25 AM
May 20, 2014 at 1:23 PM
Thank you. Using straight alpha (states.NonPremultiplied()) did the trick for DXT5 compressed images. However, I already had large images compressed with DXT5 that were partially opaque and partially semi-transparent which were not alpha premultiplied either. How come that they worked (saved with the same Paint.NET .dds exporter) without blend state mangling, but an image with only semi-transparent pixels did not?

A final question is whether using that states.NonPremultiplied() has an impact on rendering performance compared to default state?
May 20, 2014 at 5:25 PM
Here are a few blog posts by Shawn about why premultiplied-alpha is 'better' in many cases. Most editors are going to use straight-alpha, but you can convert to premultiplied alpha as part of building a DDS file as well (see texconv's -pmalpha switch)

There's no performance difference between straight-alpha or premultiplied alpha blending modes.
Marked as answer by walbourn on 5/20/2014 at 9:25 AM
May 21, 2014 at 12:28 PM
Thank you a lot for your help. It seems that you can add premultiplied even to BC (DXT) compressed files with that tool. I had never noticed that you actually could do compression with that tool as well. That was not so obvious from the examples. So e.g.
texconv.exe -f BC3_UNORM -m 1 -pmalpha <file.png>
creates a DXT5 compressed AND premultiplied alpha .dds file from a .png file.

The only thing that remains a mystery is why .png images with both opaque and semi-transparent (and fully transparent) pixels converted to DXT5 (and not premultiplied alpha) worked perfectly with the SpriteBatch's default rendering, but a file with only semi-transparent (and fully transparent) pixels did not.
May 22, 2014 at 6:25 AM
When I first started using C++ I ran into a similar memory problem and it was stack versus heap problems. I'd check the pointers if memory is not being released or my guess is being multiplied in cases like this. texture->Release();
// Borrowed from http://www.programmerinterview.com/index.php/data-structures/difference-between-stack-and-heap/

void somefunction( )
{
/* create an object "m" of class Member
    this will be put on the stack since the 
    "new" keyword is not used, and we are 
   creating the object inside a function
*/
  
  Member m;

} //the object "m" is destroyed once the function ends

void somefunction( )
{
/* create an object "m" of class Member
    this will be put on the heap since the 
    "new" keyword is used, and we are 
   creating the object inside a function
*/
  
  Member* m = new Member( ) ;
  
  /* the object "m" must be deleted
      otherwise a memory leak occurs
  */

  delete m; 
}