Mipmaps on Render Target Texture

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
Post Reply
tasulife
Posts: 20
Joined: Sat Sep 21, 2013 8:09 pm

Mipmaps on Render Target Texture

Post by tasulife »

I'm using render target textures, and I would like the rendered texture to use mip-maps so the image looks better at a distance. I can't seem to get the mipmapping to work.

Here are some things I've tried:

I'm creating a render texture target by calling the addRenderTargetTexture(). On the returned ITexture, I call hasMipMaps(), which returns false. I assume this means means it doesn't get mipmaps by default.

If I call setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, true); on the video driver before I create addRenderTargetTexture(), hasMipMaps() still returns false. So I guess that means the render texture creation doesn't take the texture creation flag into consideration.

I'm starting to wonder if render textures simply cannot have mip-maps. Therefore my work-around would require the render texture to have its data coppied into a second texture with mip-maps enabled every time the rendertexture is drawn to. This sounds a little silly, so I wanted to make sure there wasn't a way to generate the mipmaps directly in the one texture.

Thanks for the help!
tasulife
Posts: 20
Joined: Sat Sep 21, 2013 8:09 pm

Re: Mipmaps on Render Target Texture

Post by tasulife »

After a bit more searching, I found this thread:
http://irrlicht.sourceforge.net/forum/v ... k&start=45

It looks like RTTs don't have mipmaps.
Can someone help me with code to copy data from one ITexture into another? I've seen a few other snippets but don't have the whole picture. Does the bottom snippet look right? How do I determine the number of bytes for memcpy?

Code: Select all

const void* DonorPixels = DonorTexture->lock();
void* DesinationPixels = DestiationTexture->lock();
memcpy(DestinationPixels,DonorPixels,???);  
DonorPixels->unlock();
DestinationPixels->unlock();
hendu
Posts: 2600
Joined: Sat Dec 18, 2010 12:53 pm

Re: Mipmaps on Render Target Texture

Post by hendu »

Both textures must have the same format (check for it). The size for the default ARGB is 4 * w * h.

Also, use a read-only lock for the donor to speed it up.
tasulife
Posts: 20
Joined: Sat Sep 21, 2013 8:09 pm

Re: Mipmaps on Render Target Texture

Post by tasulife »

Thanks hendu, you're the man. I appreciate your support.
Your suggestion is 100% functional, and this setup does effectively generate the mipmaps for the rendertexture.

I'm running this in OPENGL, and my textures are 1048*768 (the size of the window) in the ECF_R5G6B5 16bit color mode (the lowest data-per-pixel representation). There is a very simple model in my scene that I am applying my render texture. Before locking the textures I got around 2200 frames per second, and calling the code above, I get like 140 frames per second. Holy cow this is an expensive operation! It looks amazing, though so as a proof of concept it's a success.

I'm now trying to get the same effect but without the huge performance hit. I reverted my changes, then rebuilt the engine with rtt mipmaps on (in COpenGLDriver.cpp line 4002, I commented out the command that temporarily disables mipmapping texture creation flag). I also added a cout statement after commenting out the mipmap disabling statement just to make sure I wasn't screwing up the compile.

This did not produce a rendertexture with mipmaps, and my cout statement printed to console, which proves my edits were in the path of the program counter. In this case the hasMipMaps returns false!

The only way I could get hasMipMaps to return true was when I forced the texture creation to not use the FrameBufferObjects (forced "if" in 4009 to be false). In this case, all the mip-maps were pure black, and I couldn't force them to be generated using regenerateMipMapLevels()

Any ideas on what to do? I will note that what I did was (to my knowledge) basically following your suggestion here:
http://irrlicht.sourceforge.net/forum/v ... 15#p280857

Can you give any followup information in what you had in mind in that comment?

At one point in reading forum posts, someone mentioned that using a specifically configured fragment shader might make the render texture look better at a distance wihtout needing mipmaps... this is a long shot but does that give you any ideas?

Do you have any other ideas on how to do what I"m trying to do?

Again, thanks very much for your help.
hendu
Posts: 2600
Joined: Sat Dec 18, 2010 12:53 pm

Re: Mipmaps on Render Target Texture

Post by hendu »

Do you really need to update it every frame? Might help to elaborate on what you're doing.
Can you give any followup information in what you had in mind in that comment?
RTTs use a different class, which sets HasMipMaps directly without that flag. See COpenGLTexture.cpp.
At one point in reading forum posts, someone mentioned that using a specifically configured fragment shader might make the render texture look better at a distance wihtout needing mipmaps... this is a long shot but does that give you any ideas?
That means to implement scaling in it essentially. You compute how many texels this pixel covers, then use one of the image algorithms to combine those texels. Think photoshop's nearest/bilinear/bicubic scaling options.

Some games and video players implement bicubic scaling in the shader, as it has better quality than the default GL mipmaps, which are bilinear.
tasulife
Posts: 20
Joined: Sat Sep 21, 2013 8:09 pm

Re: Mipmaps on Render Target Texture

Post by tasulife »

Every frame, I am drawing a CEGUI multi-line text box to the render texture. It's a stdout from an interpreter, running in a separate thread. If you're running a fast loop with a print statement every iteration, the screen should theoretically get updated every frame, even at 2000 fps. But you're right, of course - I could check to see if the threadsafe buffer has changed, and only then do the texture locking and that would speed up drawing. I'll try this optimization tomorrow night and see what the framerate is like in "running" and "not running" cases.

I need to do a render texture in order to get the CRT barrel distortion of the text- The screen is a real 3d bubble and has the texture coordinates set to distort a flat image into what you see there. This is why I am not just painting directly to the user's screen like you normally would with a GUI. IS there a better way to get this distorortion without needing render textures? I know there are barrel distortion shaders, but I don't know shaders and I do know 3d modeling, so I went with this route.

Mipmaps are important because the text is much harder to read at a distance without them. Even at mipmap-level-1-range without mipmaps, it gets pretty tricky to see, and further out its just garbage. Check out the read-ability of the characters one-by-one in these screenshots, especially the characters at the top and bottom of the screen, where they are distorted the most. some of the characters are indecipherable messes without mipmaps (the 2000fps one), but that isn't the case with the mipmap version (the blurry one). These screens are taken at the distance from the screen where mipmap1 has just appeared.


Image
Image

I'm excited to try the the shader solution if your simple and excellent first suggestion doesn't work. I've been looking for an excuse to write my first shader : )

as always, thanks for taking the time to help me.
Last edited by tasulife on Tue Dec 16, 2014 10:39 am, edited 1 time in total.
hendu
Posts: 2600
Joined: Sat Dec 18, 2010 12:53 pm

Re: Mipmaps on Render Target Texture

Post by hendu »

Be warned that bicubic scaling is complex. Think dozens of lookups, advanced math and over a hundred lines of code. OTOH if you do a simple average-all-covered-texels it should be fairly easy.

BTW, in your mipmapped image I can see clear transitions - you should enable trilinear filtering on it.
tasulife
Posts: 20
Joined: Sat Sep 21, 2013 8:09 pm

Re: Mipmaps on Render Target Texture

Post by tasulife »

Adding trillinear filtering knocks me down even more fps, but fixes the mipmap transitions and makes things even more readable - I'll use this from now on, thanks.
Image
mongoose7
Posts: 1227
Joined: Wed Apr 06, 2011 12:13 pm

Re: Mipmaps on Render Target Texture

Post by mongoose7 »

But, but, but your RTT is so small that you could render to a texture twice as large (2 X 2) and downsample in the shader. I think four texture reads per pixel would be faster.
tasulife
Posts: 20
Joined: Sat Sep 21, 2013 8:09 pm

Re: Mipmaps on Render Target Texture

Post by tasulife »

But, but, but your RTT is so small that you could render to a texture twice as large (2 X 2) and downsample in the shader. I think four texture reads p
My rtt dimensions are 1024*768, which is equal to the window size in my test case. When I scale the window, the render texture dimensions also change proportionally. Do you still think this is small enough for your idea when the texture goes up to 1920*1080? Or should I be using a fixed rtt size independent of the window?

Point taken , guys. I'll start experimenting with doing downsampling and multi sampling in the shader. Thanks for the tips.
mongoose7
Posts: 1227
Joined: Wed Apr 06, 2011 12:13 pm

Re: Mipmaps on Render Target Texture

Post by mongoose7 »

Well, I guess I don't know what you are doing because the size of the texture in the images you posted was quite small. But experiment with different techniques. You may be limited by the font choice(s).
tasulife
Posts: 20
Joined: Sat Sep 21, 2013 8:09 pm

Re: Mipmaps on Render Target Texture

Post by tasulife »

Irrlicht already has a ITexture::regenerateMipMapLevels() function, which uses the glGenerateMipmap() function to use the graphics card to rebuild the texture pyramid. I changed how render textures were configured, so that when regnereateMipMapLevels() is called, mip-mapped render textures can filter their way down into the glGenerateMipmap() call. And in order for it to work, I had to also first call glBindTexture(GL_TEXTURE_2D, TextureName) before the mipmap generation call, because scenemanger->drawAll() just leaves "whatever" bound to GL_Texture_2D.

I did testing on the irrlicht RTT demo scene, and with 8x antialiasing and rendering to a 2048 texture. In this case, using trunk irrlicht renders at 820 fps. If I calculate mipmaps every frame, which uses my code, it runs at 770fps (nvidia gtx 760). So Even in a reasonably expensive case, it is much faster than what I had before. Still, this is somewhat apples and oranges and I need to rebuild some libraries before I can test my CRT screen experiment.

so here is the patch. It only works when you set the new texture creation flag "ETCF_ALLOW_RENDER_TARGET_MIP_MAPS", so it doesn't break everyone's projects. After the texture is created, the user must manually regenerate the mipmaps when calling regenerateMipMapLevels() on the ITexture.

This needs more testing, as I suggested. How can I test older opengl versions on my computer? I would like to see what happens when glGenerateMipmap() isn't available on the card, for example.

Code: Select all

Index: include/ITexture.h
===================================================================
--- include/ITexture.h  (revision 4999)
+++ include/ITexture.h  (working copy)
@@ -65,6 +65,12 @@
    /** BurningVideo can handle Non-Power-2 Textures in 2D (GUI), but not in 3D. */
    ETCF_ALLOW_NON_POWER_2 = 0x00000040,
 
+    /** Lets the driver build mip maps for render target textures.
+    Mip maps are only created when the user calls
+    video::ITexture->regenerateMipMapLevels() when she is done drawing.
+    */
+   ETCF_ALLOW_RENDER_TARGET_MIP_MAPS = 0x00000080,
+
    /** This flag is never used, it only forces the compiler to compile
    these enumeration values to 32 bit. */
    ETCF_FORCE_32_BIT_DO_NOT_USE = 0x7fffffff
Index: source/Irrlicht/COpenGLDriver.cpp
===================================================================
--- source/Irrlicht/COpenGLDriver.cpp   (revision 4999)
+++ source/Irrlicht/COpenGLDriver.cpp   (working copy)
@@ -4261,9 +4261,12 @@
                    const io::path& name,
                    const ECOLOR_FORMAT format)
 {
-   //disable mip-mapping
+   // back up and disable mipmaps if the ETCF_ALLOW_RENDER_TARGET_MIP_MAPS
+   // is not set.
    bool generateMipLevels = getTextureCreationFlag(ETCF_CREATE_MIP_MAPS);
-   setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, false);
+   bool GenerateRttMipmap = getTextureCreationFlag(ETCF_ALLOW_RENDER_TARGET_MIP_MAPS);
+    if(!GenerateRttMipmap)
+        setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, false);
 
    video::ITexture* rtt = 0;
 #if defined(GL_EXT_framebuffer_object)
@@ -4288,8 +4291,9 @@
        }
    }
 
-   //restore mip-mapping
-   setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, generateMipLevels);
+   //restore mip-mapping if previously disabled
+   if(!GenerateRttMipmap)
+        setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, generateMipLevels);
 
    return rtt;
 }
Index: source/Irrlicht/COpenGLTexture.cpp
===================================================================
--- source/Irrlicht/COpenGLTexture.cpp  (revision 4999)
+++ source/Irrlicht/COpenGLTexture.cpp  (working copy)
@@ -755,8 +755,18 @@
    if (!mipmapData && AutomaticMipmapUpdate && !MipmapLegacyMode)
    {
        glEnable(GL_TEXTURE_2D);
+
+        // backup old bind and bind this texture.
+       GLint tmpTexture;
+       glGetIntegerv(GL_TEXTURE_BINDING_2D, &tmpTexture);
+       glBindTexture(GL_TEXTURE_2D, TextureName);
+
+       // generate mipmaps.
        Driver->extGlGenerateMipmap(GL_TEXTURE_2D);
 
+       // restore old bind.
+       glBindTexture(GL_TEXTURE_2D, tmpTexture);
+
        return;
    }
 
@@ -895,9 +905,18 @@
    GLint FilteringType = 0;
    InternalFormat = getOpenGLFormatAndParametersFromColorFormat(format, FilteringType, PixelFormat, PixelType);
 
-   HasMipMaps = false;
-   IsRenderTarget = true;
-
+    IsRenderTarget = true;
+   if(Driver->getTextureCreationFlag(ETCF_ALLOW_RENDER_TARGET_MIP_MAPS)
+        && Driver->getTextureCreationFlag(ETCF_CREATE_MIP_MAPS))
+   {
+       HasMipMaps = true;
+       AutomaticMipmapUpdate = true;
+       MipmapLegacyMode = false;
+   }
+   else
+   {
+        HasMipMaps = false;
+   }
    // generate color texture
 
    glGenTextures(1, &TextureName);
 
tasulife
Posts: 20
Joined: Sat Sep 21, 2013 8:09 pm

Re: Mipmaps on Render Target Texture

Post by tasulife »

I've gone back to my old project and I got my FPS back up to around 1750 fps using this patch. 1750fps gets me a screen full of text, 4x anisotropic filtering, trilinear filtering, and mipmap generation on every frame.
I had to start using the trunk version of Irrlicht, because the 1.8.1 version of ITexture::regenreateMipmapLevels() doesn't have the goodies that I needed.

Guys, Irrlicht is awesome.

Image
tasulife
Posts: 20
Joined: Sat Sep 21, 2013 8:09 pm

Re: Mipmaps on Render Target Texture

Post by tasulife »

Here's a demo if you're interested:
https://www.youtube.com/watch?v=HALkuLV ... e=youtu.be
Post Reply