Can't edit a render target texture in DirectX

You discovered a bug in the engine, and you are sure that it is not a problem of your code? Just post it in here. Please read the bug posting guidelines first.
Post Reply
Smuel
Posts: 14
Joined: Fri Dec 08, 2006 10:50 am

Can't edit a render target texture in DirectX

Post by Smuel »

I am trying to edit a render target texture (to do some post-processing), but it seems that with DirectX this doesn't work. I don't get any errors - it just doesn't change anything. Even if I try to read the texture I just get the wrong bytes back instead of the texture contents. However, it works fine with the Software renderer, so it seems that it's a bug in the DirectX code (or maybe I'm not doing it the right way and it's just a coincidence that it works in software.)

Here is a sample program that shows the problem - it's a modification to the 13.RenderToTexture sample. If I run this and choose the software renderer I see a green background and a blue square with a white diagonal line across it. The white diagonal line is being drawn by directly writing to the render target texture. If I run this and choose DirectX the white diagonal line is missing. However - it's not that the texture failed to be locked - the pointer was generated okay and I'm drawing a white diagonal line to some piece of memory - just not anywhere that the DirectX driver pays attention to. I looked in the Irrlicht code, and there is a separate section for when lock() is called on a render target, which calls the D3D GetRenderTargetData() function - so it seems like it should work. Also, if I do this with an ordinary (non render-target) texture, it does work.

So my questions are:

1. Does anyone else see this behaviour?
2. If yes, is it a bug?
3. If yes is it likely to be fixed?
4. If no is there some other way to access the contents of a render target texture?

Code: Select all

#include <irrlicht.h>
#include <iostream>

using namespace irr;

#pragma comment(lib, "Irrlicht.lib")

int main()
{
    video::E_DRIVER_TYPE driverType = video::EDT_DIRECT3D9;

    printf("Please select the driver you want for this example:\n"\
        " (a) Direct3D 9.0c\n (b) Direct3D 8.1\n (c) OpenGL 1.5\n"\
        " (d) Software Renderer\n (e) Burning's Software Renderer\n"\
        " (f) NullDevice\n (otherKey) exit\n\n");

    char i;
    std::cin >> i;

    switch(i)
    {
        case 'a': driverType = video::EDT_DIRECT3D9;break;
        case 'b': driverType = video::EDT_DIRECT3D8;break;
        case 'c': driverType = video::EDT_OPENGL;   break;
        case 'd': driverType = video::EDT_SOFTWARE; break;
        case 'e': driverType = video::EDT_BURNINGSVIDEO;break;
        case 'f': driverType = video::EDT_NULL;     break;
        default: return 1;
    }   

    IrrlichtDevice *device = createDevice(driverType, core::dimension2d<s32>(640, 480), 32, false, false);

    if (device == 0)
        return 1; // could not create selected driver.

    video::IVideoDriver* driver = device->getVideoDriver();

    video::ITexture* rt = driver->createRenderTargetTexture(core::dimension2d<s32>(256,256));

    while(device->run())
        if (rt && device->isWindowActive())
        {
            driver->beginScene(true, true, 0);

                // set render target texture (in order to change its color to blue)
            driver->setRenderTarget(rt, true, true, video::SColor(0,0,0,255));     

                // set back old render target (and clear it to green)
            driver->setRenderTarget(0, true, true, video::SColor(0,0,255,0));

                // draw a diagonal white line across the render target texture
            int *ptr = (int *)rt->lock();
            if( ptr )
            {
                printf("editing... " );
                for(int i=0; i<255; ++i)
                    ptr[i + i*rt->getPitch()/4] = 0xFFFFFFFF;
                rt->unlock();
            }

                // draw the render target texture on the screen.
            driver->draw2DImage( rt, core::position2d<s32>(50,50), core::rect<s32>(0,0,256,256), 0, video::SColor(255,255,255,255), false );

            driver->endScene();
        }

    if (rt)
        rt->drop();

    device->drop();
    return 0;
}
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

I don't think it is a bug. It would be very expensive, especially with AGP graphics, to read the back buffer back out to memory so that you could manually modify it on the CPU in post.

Normally, for post-processing, the render target is used to texture a quad, and that quad is rendered to the screen with a shader applied. This way all of the texture modification happens on the GPU which is very fast. Unfortunately this means you'll have to get down and funky with shaders, but that's a good thing, right?

Travis
Smuel
Posts: 14
Joined: Fri Dec 08, 2006 10:50 am

Post by Smuel »

So what's the point of all that code in the CD3D9Texture::lock() function? It looks to me like it's creating a duplicate of the render target in system memory, using the DirectX GetRenderTargetData() function to copy the contents of the render target to the duplicate texture and then returning a pointer to the duplicate's data. Are you saying that the designers of DirectX deliberately disabled those functions because they knew it would be for my own good, and that Niko knows this too but wrote that code anyway for fun? It seems unlikely...
vitek
Bug Slayer
Posts: 3919
Joined: Mon Jan 16, 2006 10:52 am
Location: Corvallis, OR

Post by vitek »

First off, notice that this doesn't work at all with any of the accelerated drivers? It doesn't work correctly with the original software driver either.

I see that the code is in there in the D3D8/9 drivers. I also see that the lock() is giving you a pointer to a copy of the render target data. I believe that stuff is there to allow you to read the back buffer, not write it. If you wanted to write it, you'd need a call to Device->UpdateSurface() in there to update the actual texture.

This happens to work with the burning renderer because it uses an image as the back buffer and it doesn't need to make a copy of the pixels when it returns them to you. I'm not sure exactly why the old software renderer doesn't work correctly. It looks like the texture might always be 16-bit with that driver.

Travis
Smuel
Posts: 14
Joined: Fri Dec 08, 2006 10:50 am

Post by Smuel »

Okay, so I've worked out what is happening. You do have a bug but it's in a different place...

What happened was that I tried to modify the render target texture, and no changes were made in non-Software modes, so then I tried just reading the texture, and that didn't seem to work either. So then I became convinced that the functionality was broken somewhere and posted here. What actually happened is that I was using getPitch() to get the width of the texture for my editing, and that always returns 0 for render target textures - so my code was just reading the top line of the texture over and over again, and since that was all one color the read result looked like a completely blank texture, leading me to believe that the read didn't work. However, if I calculate the pitch myself then it does work. Well - the reading works, I still can't write to the texture as vitek explained, but that doesn't matter because I can just copy the bytes to a non-rendertarget texture instead and use that for later rendering.

So your bug is: getPitch() always returns 0 for render target textures.

This was in fact raised by alc about a year ago in this thread, and he even wrote a fix for the problem too, but I guess it never made it into the engine.

Thanks for your help, vitek.

By the way, the copy-on-read behaviour of render target textures means that the engine's texture modification functions don't work with them, (e.g. makeColorKeyTexture() - I haven't tested any others). You may want to note this in the documentation somewhere.
hybrid
Admin
Posts: 14144
Joined: Wed Apr 19, 2006 9:20 pm
Location: Oldenburg(Oldb), Germany
Contact:

Post by hybrid »

Ok, the pitch patch is commited to SVN :) Thanks for re-raising this issue.
Post Reply