Sign in to follow this  
Vincent Torri

OpenGL 2d with direct3d: how to display argb data in a window

Recommended Posts

Hey, I have a rectangle (a piece of memory) that I fill with argb data. I would like to display it in a window, using direct3d. I have 3 questions about that. 1) I've read several tutorials. One solution would be to use the sprite interface. Then create a texture, fill it with data and render it onto the surface. I have written that code:
// the variable d contains a window and d3d object, device and sprite



  d->device->Clear (0, NULL,
                    D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
                    D3DCOLOR_ARGB (0, 0, 0, 0),
                    1.0f, 0);

  d->device->BeginScene ();

 // Here is my argb surface, named 'surf'

  w = 64;
  h = 64;
  depth = 4;
  pitch = depth * w;
  surf = malloc (depth * w * h);
  if (!surf) return 0;
  ZeroMemory (surf, depth * w * h);

  tmp = (unsigned char *)surf;
  for (r = 0; r < w; r++) {
    for (g = 0; g < h; g++, tmp += depth) {
      tmp[0] = (r * 2 + foo) % 256;
      tmp[1] = (g + foo) % 256;
      tmp[2] = (w - 1 - g) * 4;
    }
  }
  foo++;

  rect.left = 0;
  rect.top = 0;
  rect.right = w;
  rect.bottom = h;

  // FIXME : other flags might speed up things
  d->sprite->Begin (0);

  d->device->CreateTexture (w, h, 0, 0,
                            D3DFMT_A8R8G8B8,
                            D3DPOOL_DEFAULT,
                            &texture, NULL);

  // I have to fill the texture with the data of surf

  d->sprite->SetTransform (&matrix);
  d->sprite->Draw (texture, &rect, NULL, NULL, D3DCOLOR_ARGB (0, 0, 0, 0));
  d->sprite->End ();

  d->device->EndScene ();
  d->device->Present (NULL, NULL, NULL, NULL);


As I mentioned in a comment of that code, I have to fill the texture with the surf data. I've looked at a lot of tutorials, sites, etc... I didn't find any code to do that. I find code to fill a texture from a file, but not from a piece of memory. So my first question is: is it possible, and if yes, how ? 2) My second question is about speed. My main cincern is speed. I've already written a code with directdraw 7. The speed is the same than a similar code on linux, using software routines. I don't know if it is normal or not. I wanted to get more speed, so I tried to do that with direct3d, in order to use hardware accelerated features of d3d. With all the doc and tutorial I've read, I found several things to speed up all that stuff: a) Using vertices instead of sprites b) Using the immediate mode of d3d (see http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/dnardir3d/html/d3dim.asp) Is it correct ? and if so, do you have some documentation about vertices or immediate mode, in order to achieve what I want ? Also, if there are better solutions (that is, faster techniques related to d3d to do what I want), I would be pleased to know them 3) Finally, between opengl and d3d, is there a difference in speed ? (for what I want to do) thank you very much Vincent Torri [Edited by - Vincent Torri on July 11, 2007 3:06:58 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Vincent Torri
So my first question is: is it possible, and if yes, how ?
Yes, it's certainly possible.
You'll want to lock the top level of the texture (and each level in turn if you have mipmaps - which you almost certainly don't want - see #2), that'll give you a pointer to memory you can copy into.

EDIT: Here's some code from my engine:

void PTexture::Update(const u32* pData)
{
Assert(m_desc.Usage & D3DUSAGE_DYNAMIC, "Update() only valid on dynamic textures");
Assert(m_nLevels == 1, "Can only Update() textures with one mip level in them");

// Lock texture
D3DLOCKED_RECT d3dRect;
HRESULT hResult = m_pTexture->LockRect(0, &d3dRect, NULL, 0);
if(FAILED(hResult))
{
ELog::Get().DebugFormat(L"TEXTURE : IDirect3DTexture9::LockRect() failed. Error: %s\n",
DXGetErrorString(hResult));
return;
}

// Fill - do a fast copy if we can
if((u32)d3dRect.Pitch == m_desc.Width * 4)
{
memcpy(d3dRect.pBits, pData, m_desc.Width * m_desc.Height * 4);
}
else
{
// Need to copy scanline by scanline to avoid crapping up the pitch
for(UINT y=0; y<m_desc.Height; ++y)
{
memcpy((BYTE*)d3dRect.pBits + y*d3dRect.Pitch, pData, m_desc.Width * 4);
pData += m_desc.Width;
}
}

// Unlock
m_pTexture->UnlockRect(0);
}



Quote:
Original post by Vincent Torri
2) My second question is about speed. My main cincern is speed. I've already written a code with directdraw 7. The speed is the same than a similar code on linux, using software routines. I don't know if it is normal or not. I wanted to get more speed, so I tried to do that with direct3d, in order to use hardware accelerated features of d3d.

With all the doc and tutorial I've read, I found several things to speed up all that stuff:

a) Using vertices instead of sprites
b) Using the immediate mode of d3d
Notice the date on that link: 1997; 10 years ago [smile] - non-immediate mode hasn't existed since D3D5 or so; All D3D is immediate mode now, and there's no retained mode.
Using vertices instead of sprites won't make much difference at all here.

Things that will make a difference though:
  • Create your texture as D3DUSAGE_DYNAMIC. Otherwise the texture will probably be dumped into video RAM and updating it will be extremely expensive.
  • Don't create any mip-maps (Set the 3rd parameter of CreateTexture() to 1 instead of 0)
  • Don't update the texture every frame if you can help it (depends what you're doing really). It's better to update the texture once at load time, and then never have to touch it. If that's the case, don't bother with D3DUSAGE_DYNAMIC.
  • If you update and render the texture every frame, it might be a good idea to use 4 or so textures in a round-robbin fassion. That way D3D doesn't have to wait for the data to be uploaded to the graphics card before it renders the frame, but you'll get 4 frames of "lag" on the texture.
  • When you lock the texture, you can write directly into that, instead of writing to a memory buffer, then copying that buffer to the locked texture.

    If you're using dynamic textures, then you can lock the texture, if not, you'll have to either put the texture into D3DPOOL_MANAGED, or use IDirect3DDevice9::UpdateTexture to copy the data to the texture instead.

    Quote:
    Original post by Vincent Torri
    3) Finally, between opengl and d3d, is there a difference in speed ? (for what I want to do)
    Almost certainly not. I can't really day for sure, since I'm not an OpenGL person, but I'll be surprised if there's a significant performance difference provided both ways are optimal for their own API.

    Share this post


    Link to post
    Share on other sites
    Quote:
    Original post by Evil Steve
    You'll want to lock the top level of the texture (and each level in turn if you have mipmaps - which you almost certainly don't want - see #2), that'll give you a pointer to memory you can copy into.

    haa, ok. thank you ! I'll try that.
    Quote:

    Quote:
    Original post by Vincent Torri
    2) My second question is about speed. With all the doc and tutorial I've read, I found several things to speed up all that stuff:

    a) Using vertices instead of sprites
    b) Using the immediate mode of d3d

    Notice the date on that link: 1997; 10 years ago - non-immediate mode hasn't existed since D3D5 or so; All D3D is immediate mode now, and there's no retained mode.
    Using vertices instead of sprites won't make much difference at all here.

    ok. That saves me some work. That immediate mode is a bit a pain to implement. Same for vertices, btw.
    Quote:

    Things that will make a difference though:
  • Create your texture as D3DUSAGE_DYNAMIC. Otherwise the texture will probably be dumped into video RAM and updating it will be extremely expensive.
  • Don't create any mip-maps (Set the 3rd parameter of CreateTexture() to 1 instead of 0)
  • Don't update the texture every frame if you can help it (depends what you're doing really). It's better to update the texture once at load time, and then never have to touch it. If that's the case, don't bother with D3DUSAGE_DYNAMIC.
  • If you update and render the texture every frame, it might be a good idea to use 4 or so textures in a round-robbin fassion. That way D3D doesn't have to wait for the data to be uploaded to the graphics card before it renders the frame, but you'll get 4 frames of "lag" on the texture.
  • When you lock the texture, you can write directly into that, instead of writing to a memory buffer, then copying that buffer to the locked texture.

    If you're using dynamic textures, then you can lock the texture, if not, you'll have to either put the texture into D3DPOOL_MANAGED, or use IDirect3DDevice9::UpdateTexture to copy the data to the texture instead.

  • What I want to do is the engine of a canvas library. What it does is exactly what I try to do : having a rectangle of some size (i know that size only at runtime, so I think that I have to create the texture each time I need it) with some data, and drawing it at some position. These rectangles are created by the user of the lib.

    But the engine is written such that I have to render a sequence of such rectangles. I know the number of these rectangles and their size. So I think that I can do what you describe in your 4th point ("round-robin fashion"). Could you please elaborate a bit on that technic, please ? Or maybe is there something else to do in that case ?
    Quote:

    Quote:
    Original post by Vincent Torri
    3) Finally, between opengl and d3d, is there a difference in speed ? (for what I want to do)
    Almost certainly not. I can't really day for sure, since I'm not an OpenGL person, but I'll be surprised if there's a significant performance difference provided both ways are optimal for their own API.

    ok.

    thank you very much for your answer !

    Vincent Torri

    PS: if you are interested, that canvas is named 'evas' and is part of the enlightenment project.

    Share this post


    Link to post
    Share on other sites
    Quote:
    Original post by Vincent Torri
    What I want to do is the engine of a canvas library. What it does is exactly what I try to do : having a rectangle of some size (i know that size only at runtime, so I think that I have to create the texture each time I need it) with some data, and drawing it at some position. These rectangles are created by the user of the lib.
    You definitely want to avoid creating the texture every frame. Only create the texture when the canvas size changes, otherwise you'll get horrible performance (Creating D3D resources is very expensive). Locking the texture each frame (Or using the 4-texture method) is ok though, so long as you create the texture as dynamic.
    I'm not sure exactly how often you have to lock the texture to make a dynamic texture better, but if you're doing it more than once a second, I'd go for dynamic, and even if you lock it less frequently, you're likely to see a stutter with a non-dynamic texture (See the 4-texture method stuff below) while D3D uploads the texture.

    Quote:
    Original post by Vincent Torri
    But the engine is written such that I have to render a sequence of such rectangles. I know the number of these rectangles and their size. So I think that I can do what you describe in your 4th point ("round-robin fashion"). Could you please elaborate a bit on that technic, please ? Or maybe is there something else to do in that case ?
    If you have one texture, it goes like this:
    Lock texture and fill it with data
    Render textured quad / sprite
    However, in the background, this is happening:
    Lock texture and fill it with data
    Unlock texture. D3D now starts to send the texture to the video card
    Request rendering of textured quad / sprite
    D3D has to wait for the texture to finish transferring to video memory before it can render the textured quad
    Transfer completes, quad gets rendered

    Now, you can use several textures (I'll only use 2 in this example) to get around the stall while D3D uploads the texture. Your code does this:
    Startup: Lock and fill all textures
    Start of frame 1:
    Lock texture B and fill it with data
    Render textured quad / sprite with texture A
    Present. End of frame 1
    Start of frame 2:
    Lock texture A and fill it with data
    Render textured quad / sprite with texture B
    Present. End of frame 2
    Start of frame 3:
    Lock texture B and fill it with data
    Render textured quad / sprite with texture A
    Present. End of frame 3
    So, you're constantly going through the textures, one after another (If you had 4 textures, then frame 1 you'd update D and use A, frame 2 you'd update A and use B, frame 3 you'd update B and use C, frame 4 you'd update C and use D, repeat).
    The point is that there's now a 1 frame gap between locking a texture and it being used. That means you have 1 frame of "lag", since you're displaying the texture from the previous frame, but it gives D3D a whole extra frame to upload the texture to the card.

    If the bottleneck in your application does turn out to be texture uploading, this is a nice and easy fix for it. But I'd profile your app (PIX, in the DirectX SDK is good for this, and NVPerfHUD for NVidia cards) and see if this is actually a bottleneck, since it's possible that D3D has enough time to transfer the texture to the graphics card anyway, and adding this round-robbin system will only introduce lag in your texture, and require more video memory.


    Incidently, using a dynamic texture usually causes D3D (The video card driver, more correctly) to put the texture into AGP memory, or somewhere else where it's faster for D3D to access but slower for the card, so it averages out better. Not using a dynamic texture usually places the texture into video memory, and getting a texture there from CPU-accessible memory can be pretty expensive.

    Share this post


    Link to post
    Share on other sites
    ok, I understand a bit more how it works, now. Creating the texture before is a bit how I managed to do the same thing with directdraw and its surfaces.

    about the dynamic texture, I can't know if there will be animations or not. So I'll use dynamic textures. The best is checking if an animation is done or not, and use a dynamic texture in that case. I'll do some tests.

    I'll check if I can use the "round-robin" technic in my engine. It can be very interesting for animations.

    thank you for your explanations.

    Share this post


    Link to post
    Share on other sites
    Evil Steve:

    About your code:

    [source lang=cpp]
    for(UINT y=0; y<m_desc.Height; ++y)
    {
    memcpy((BYTE*)d3dRect.pBits + y*d3dRect.Pitch, pData, m_desc.Width * 4);
    pData += m_desc.Width;
    }



    maybe you should avoid y*d3dRect.Pitch and use pointer arithmetic, maybe you can also compute m_desc.Width * 4 as a pitch and use it.

    Share this post


    Link to post
    Share on other sites
    Quote:
    Original post by Vincent Torri
    Evil Steve:

    About your code:

    *** Source Snippet Removed ***

    maybe you should avoid y*d3dRect.Pitch and use pointer arithmetic, maybe you can also compute m_desc.Width * 4 as a pitch and use it.


    Surface width * bytes per pixel may not be equal to the pitch. If you assume that it is, you'll run into problems later.

    Share this post


    Link to post
    Share on other sites
    as I'm away from my home computer and as i didn't take with me my small test code, I wanted to re-write it.

    Unfortunately, it does not work. Here is the complete code:


    // g++ -g -Wall -o d3d d3d.cpp -ld3d9 -ld3dx9d

    #include <cstdlib>
    #include <cstdio>

    #include <windows.h>
    #include <d3d9.h>
    #include <d3dx9.h>

    typedef struct Data Data;

    struct Data
    {
    LPDIRECT3D9 object;
    LPDIRECT3DDEVICE9 device;
    LPD3DXSPRITE sprite;
    LPDIRECT3DTEXTURE9 texture;
    int mask_r;
    int mask_g;
    int mask_b;
    };


    static LRESULT CALLBACK
    MainWndProc(HWND hwnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam)
    {
    switch (uMsg)
    {
    case WM_CREATE:
    return 0;
    case WM_DESTROY:
    PostQuitMessage(0);
    return 0;
    case WM_CLOSE:
    PostQuitMessage(0);
    return 0;
    default:
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    }

    Data *
    data_init (HWND window,
    int width,
    int height)
    {
    D3DPRESENT_PARAMETERS pp;
    D3DDISPLAYMODE dm;
    D3DSURFACE_DESC sd;
    Data *d;

    d = (Data *)malloc (sizeof (Data));
    if (!d)
    goto no_data;

    d->object = Direct3DCreate9 (D3D_SDK_VERSION);
    if (!d->object)
    goto no_object;

    if (FAILED (d->object->GetAdapterDisplayMode (D3DADAPTER_DEFAULT, &dm)))
    goto no_device;

    ZeroMemory(&pp, sizeof(pp));
    pp.BackBufferFormat = dm.Format;
    pp.BackBufferCount = 1;
    pp.SwapEffect = D3DSWAPEFFECT_FLIP;
    pp.hDeviceWindow = window;
    pp.Windowed = TRUE;

    if (FAILED(d->object->CreateDevice (D3DADAPTER_DEFAULT,
    D3DDEVTYPE_HAL,
    window,
    D3DCREATE_HARDWARE_VERTEXPROCESSING,
    &pp,
    &d->device)))
    goto no_device;

    if (FAILED (D3DXCreateSprite (d->device, &d->sprite)))
    goto no_sprite;

    if (FAILED (d->device->CreateTexture (width, height, 1,
    D3DUSAGE_DYNAMIC,
    dm.Format,
    D3DPOOL_DEFAULT,
    &d->texture, NULL)))
    goto no_texture;

    if (FAILED (d->texture->GetLevelDesc (0, &sd)))
    goto no_level_desc;

    switch (sd.Format) {
    case D3DFMT_A8R8G8B8:
    case D3DFMT_X8R8G8B8:
    d->mask_r = 0x00ff0000;
    d->mask_g = 0x0000ff00;
    d->mask_b = 0x000000ff;
    break;
    case D3DFMT_R5G6B5:
    d->mask_r = 0xf800;
    d->mask_g = 0x07e0;
    d->mask_b = 0x001f;
    break;
    default:
    goto no_level_desc;
    }

    return d;

    no_level_desc:
    d->texture->Release ();
    no_texture:
    d->sprite->Release ();
    no_sprite:
    d->device->Release ();
    no_device:
    d->object->Release ();
    no_object:
    free (d);
    no_data:
    return NULL;
    }

    void
    data_shutdown (Data *d)
    {
    if (!d)
    return;

    d->texture->Release ();
    d->sprite->Release ();
    d->device->Release ();
    d->object->Release ();
    free (d);
    }

    void
    data_paint (Data *d)
    {
    D3DLOCKED_RECT d3d_rect;
    RECT rect;
    int w;
    int h;
    int depth;
    int pitch;
    unsigned char *tmp;
    unsigned char *d3d_tmp;
    static int foo = 0;

    void *surf;

    w = 64;
    h = 64;
    depth = 4;
    pitch = depth * w;

    surf = calloc (w * h * depth, 1);
    if (!surf) {
    printf ("pas bon \n");
    return;
    }
    tmp = (unsigned char *)surf;
    for (int r = 0; r < w; r++) {
    for (int g = 0; g < h; g++, tmp += depth) {
    tmp[0] = (r * 2 + foo) % 256;
    tmp[1] = (g + foo) % 256;
    tmp[2] = (w - 1 - g) * 4;
    }
    }
    foo++;

    rect.left = 0;
    rect.top = 0;
    rect.right = w;
    rect.bottom = h;

    if (FAILED (d->device->BeginScene ()))
    return;

    if (FAILED (d->sprite->Begin (0)))
    return;
    if (FAILED (d->texture->LockRect (0, &d3d_rect, NULL, D3DLOCK_DISCARD))) {
    d->sprite->End ();
    d->device->EndScene ();
    return;
    }

    tmp = (unsigned char *)surf;
    d3d_tmp = (unsigned char *)d3d_rect.pBits;
    for (int j = 0; j < h; j++, tmp += pitch, d3d_tmp += d3d_rect.Pitch)
    memcpy (d3d_tmp, tmp, pitch);

    d->texture->UnlockRect(0);

    d->sprite->Draw (d->texture, &rect, NULL, NULL,
    D3DCOLOR_ARGB (0, 0, 0, 0));
    d->sprite->End ();

    d->device->EndScene ();
    d->device->Present (NULL, NULL, NULL, NULL);

    free (surf);
    }

    int WINAPI
    WinMain(HINSTANCE hinstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow)
    #if 0
    main ()
    #endif
    {
    WNDCLASS wc;
    RECT rect;
    MSG msg;
    HINSTANCE instance;
    HWND window;
    Data *d;
    int width;
    int height;

    instance = GetModuleHandle(0);
    if (!instance)
    goto no_instance;

    ZeroMemory(&wc, sizeof(wc));
    wc.lpfnWndProc = MainWndProc;
    wc.hInstance = instance;
    wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor (NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(1 + COLOR_BTNFACE);
    wc.lpszClassName = "Direct3D_test";

    if(!RegisterClass(&wc))
    goto no_window_class;

    width = 320;
    height = 200;

    rect.left = 0;
    rect.top = 0;
    rect.right = width;
    rect.bottom = height;
    AdjustWindowRect (&rect, WS_OVERLAPPEDWINDOW | WS_SIZEBOX, FALSE);

    window = CreateWindow("Direct3D_test",
    "Direct3D_test",
    WS_OVERLAPPEDWINDOW | WS_SIZEBOX,
    CW_USEDEFAULT, CW_USEDEFAULT,
    rect.right - rect.left, rect.bottom - rect.top,
    NULL, NULL, instance, NULL);
    if (!window)
    goto no_window;

    d = data_init (window, width, height);
    if (!d)
    goto no_data;

    ShowWindow(window, SW_SHOWDEFAULT);
    UpdateWindow(window);

    while (GetMessage (&msg, NULL, 0, 0))
    {
    TranslateMessage (&msg);
    DispatchMessage (&msg);
    data_paint (d);
    }

    data_shutdown (d);
    DestroyWindow (window);
    UnregisterClass ("Direct3D_test", instance);
    FreeLibrary (instance);

    return EXIT_SUCCESS;

    no_data:
    DestroyWindow (window);
    no_window:
    UnregisterClass ("Direct3D_test", instance);
    no_window_class:
    FreeLibrary (instance);
    no_instance:
    return EXIT_FAILURE;
    }





    this program creates a window and displays a small animation in a 64x64 pixels large square at the top-left of the window. The window is created, but no animation is displayed.

    I don't really know where the problem is (maybe in the initialisation of direct3d, in data_init).
    does someone see the problem ?

    thank you very much

    [Edited by - Vincent Torri on July 29, 2007 12:19:52 PM]

    Share this post


    Link to post
    Share on other sites

    Create an account or sign in to comment

    You need to be a member in order to leave a comment

    Create an account

    Sign up for a new account in our community. It's easy!

    Register a new account

    Sign in

    Already have an account? Sign in here.

    Sign In Now

    Sign in to follow this