Software Rendering in a DX8 Environment
by Ryan Geiss
8/16/2002

I really don't have time to document all this, so I'm just slapping the
code up here.  Enjoy!


First, to create a texture in system memory:
        
        // create a texture for the fractal, using the D3DPOOL_MANAGED pool so that it can be co-located
        //   in system memory and we can update it whenever we like.  If we used D3DPOOL_DEFAULT, the
        //   texture would probably not exist in system memory, so when we locked it and started writing pixels,
        //   it would be super-super-slow.
        // note that we use the pixel format of the backbuffer to make sure that the blitting will work!
        // also, the actual texture created might be a bit bigger than m_fractal_texsize x m_fractal_texsize,
        //   (especially if it's not a power of 2), so we'll probably end up using just a corner of it.
        D3DFORMAT fmt = m_lpDX->m_current_mode.display_mode.Format;
        D3DPOOL   pool = D3DPOOL_MANAGED;
        if (D3DXCreateTexture(m_lpDX->m_lpDevice, m_fractal_texsize, m_fractal_texsize, 1, 0, fmt, pool, &m_fractal_tex) != S_OK)
        {
            MessageBox(m_lpDX->GetHwnd(), "D3DX failed to allocate system-memory texture", "ERROR ALLOCATING TEXTURE", MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
            return false;
        }
        

Then, to draw to it.  This example draws a simple julia fractal.
        
        void CSomeClass::UpdateFractalTexture()
        {
            // Update the texture with an image of an animated fractal.
            // This can (and should) be called before BeginScene()!
            
            // This function is here solely to show you how to render images, 
            // USING THE CPU, and store them into a texture on the video card, 
            // in any of the standard color formats (888, 565, 555, 444).
        
            D3DSURFACE_DESC sd;
            if (m_fractal_tex->GetLevelDesc(0, &sd) != D3D_OK)
                return;
        
            D3DLOCKED_RECT lr;
            if (m_fractal_tex->LockRect(0, &lr, NULL, D3DLOCK_NOSYSLOCK) != D3D_OK)
                return;
            if (lr.pBits == NULL)
                return;
        
            // 1. figure out how to chop up the 8-bit R/G/B values
            // to package them in the texture's own pixel color format.
            // (note that this should be optimally be done just once
            //  right after the texture is created, instead of each frame!)
        
            int start;
            int bpp;
            int rshift[3];      // 1. bits to first shift right r,g,b
            int mask[3];        // 2. mask for r, g, b
            int lshift[3];      // 3. bits to then shift left r,g,b
        
            switch(sd.Format)
            {
            case D3DFMT_R8G8B8:
            case D3DFMT_A8R8G8B8:
            case D3DFMT_X8R8G8B8:
                start = 0xFF000000;
                bpp = 32;
                rshift[0] = 0;
                rshift[1] = 0;
                rshift[2] = 0;
                mask[0] = 0xFF;
                mask[1] = 0xFF;
                mask[2] = 0xFF;
                lshift[0] = 16;
                lshift[1] = 8;
                lshift[2] = 0;
                break;
        
            case D3DFMT_R5G6B5:
                start = 0x00000000;
                bpp = 16;
                rshift[0] = 3;
                rshift[1] = 2;
                rshift[2] = 3;
                mask[0] = 0x1F;
                mask[1] = 0x3F;
                mask[2] = 0x1F;
                lshift[0] = 11;
                lshift[1] = 5;
                lshift[2] = 0;
                break;
            
            case D3DFMT_X1R5G5B5:
            case D3DFMT_A1R5G5B5:
                start = 0x8000;
                bpp = 16;
                rshift[0] = 3;
                rshift[1] = 3;
                rshift[2] = 3;
                mask[0] = 0x1F;
                mask[1] = 0x1F;
                mask[2] = 0x1F;
                lshift[0] = 10;
                lshift[1] = 5;
                lshift[2] = 0;
                break;
        
            case D3DFMT_A4R4G4B4:
                start = 0xF000;
                bpp = 16;
                rshift[0] = 4;
                rshift[1] = 4;
                rshift[2] = 4;
                mask[0] = 0x0F;
                mask[1] = 0x0F;
                mask[2] = 0x0F;
                lshift[0] = 8;
                lshift[1] = 4;
                lshift[2] = 0;
                break;
        
            default:
                return;
            }
        
        	float i1 =  0.5f + 0.07f*sinf(5 - m_time*1.2430f) - 0.07f*cosf(7 + m_time*1.3121f);
        	float i2 = -0.4f + 0.07f*cosf(0 + m_time*1.4398f) - 0.07f*sinf(2 - m_time*1.0700f);
        
            unsigned __int16* p16 = (unsigned __int16*)lr.pBits;
            unsigned __int32* p32 = (unsigned __int32*)lr.pBits;
            int w = min((int)sd.Width, m_fractal_texsize);
            int h = min((int)sd.Height, m_fractal_texsize);
        
            // render a julia fractal.
            //   the number of iterations we get through ('c') before x0*x0+z0*z0 exceeds
            //   some threshold determines the color intensity; then we can use an 
            //   interpolation trick to increase the color precision.
            int max_its = 10;
            float c_mult = 1.0f / (max_its + 1.0f);
            int half_w = w/2;
            int half_h = h/2;
            float one_over_half_w = 1.0f/(float)(w/2);
            float one_over_half_h = 1.0f/(float)(h/2);
        
            for (int y=0; y5) 
                        {
                            // done; interpolate, using endpoint, to increase color precision
                            partial_c = (5 - prev_breakval) / (breakval - prev_breakval);
                            break;
                        }
                        prev_breakval = breakval;
        		        z0 = 2 * x0 * z0 + i2;
        		        x0 = x0x0 - z0z0 + i1;
        		    }
        
                    float intensity = (c + partial_c)*c_mult;
                    if (intensity>1)
                        intensity=1;
        
                    float temp = (1-intensity);
                    int r = (int)(temp*255);            // 0..255
                    int g = (int)(temp*temp*255);       // 0..255
                    int b = (int)(0);                   // 0..255
        
                    if (bpp==16)
                        p16[offset] = start | 
                            (((r >> rshift[0]) & mask[0]) << lshift[0]) | 
                            (((g >> rshift[1]) & mask[1]) << lshift[1]) | 
                            (((b >> rshift[2]) & mask[2]) << lshift[2]);                
                    else
                        p32[offset] = start | 
                            (((r >> rshift[0]) & mask[0]) << lshift[0]) | 
                            (((g >> rshift[1]) & mask[1]) << lshift[1]) | 
                            (((b >> rshift[2]) & mask[2]) << lshift[2]);                
                    offset++;
                }
            }
        
            m_fractal_tex->UnlockRect(0);
        }
        
        
Finally, some code to render it.  The first function sets DirectX up
to draw in 2D mode; the second function draws the fractal texture
full-screen.
        
        void PrepareFor2DDrawing(IDirect3DDevice8 *pDevice, int w, int h)
        {
            // w and h should be the width and height of the CLIENT area of the window.
            //   (use GetClientRect())
            // NOTE: After calling this, be sure to then call:
            //  1. SetVertexShader()
            //  2. SetTexture(), if you need it
            // Also be sure your sprites have a z coordinate of 0!!!
            pDevice->SetRenderState( D3DRS_ZENABLE, TRUE );
            pDevice->SetRenderState( D3DRS_ZWRITEENABLE, TRUE );
            pDevice->SetRenderState( D3DRS_ZFUNC,     D3DCMP_LESSEQUAL );
            pDevice->SetRenderState( D3DRS_SHADEMODE, D3DSHADE_FLAT );
            pDevice->SetRenderState( D3DRS_FILLMODE,  D3DFILL_SOLID );
            pDevice->SetRenderState( D3DRS_FOGENABLE, FALSE );
            pDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
            pDevice->SetRenderState( D3DRS_CLIPPING, TRUE ); 
            pDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
            pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
            pDevice->SetRenderState( D3DRS_LOCALVIEWER, FALSE );
            
            pDevice->SetTexture(0, NULL);
            pDevice->SetTexture(1, NULL);
            pDevice->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_POINT);//D3DTEXF_LINEAR);
            pDevice->SetTextureStageState(1, D3DTSS_MAGFILTER, D3DTEXF_POINT);//D3DTEXF_LINEAR);
            pDevice->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
            pDevice->SetTextureStageState(1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);    
            pDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE );
            pDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
            pDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT );
            pDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE );
        
            pDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 );
            pDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE );
            pDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
        
            pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
            
            // set up for 2D drawing:
            {
                D3DXMATRIX Ortho2D;    
                D3DXMATRIX Identity;
                
                D3DXMatrixOrthoLH(&Ortho2D, (float)w, (float)h, 0.0f, 1.0f);
                D3DXMatrixIdentity(&Identity);
        
                pDevice->SetTransform(D3DTS_PROJECTION, &Ortho2D);
                pDevice->SetTransform(D3DTS_WORLD, &Identity);
                pDevice->SetTransform(D3DTS_VIEW, &Identity);
            }
        }

        void CSomeClass::RenderFractal()
        {
            // draw the texture fullscreen
            m_lpDX->m_lpDevice->SetVertexShader( SPRITEVERTEX_FORMAT );
            m_lpDX->m_lpDevice->SetTexture(0, m_fractal_tex);
            m_lpDX->m_lpDevice->SetRenderState( D3DRS_SHADEMODE, D3DSHADE_GOURAUD );
        
            // turn on bilinear texture filtering, to smooth out the display of the
            // low-resolution fractal:
            m_lpDX->m_lpDevice->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
            m_lpDX->m_lpDevice->SetTextureStageState(1, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
        
            D3DSURFACE_DESC sd;
            if (m_fractal_tex->GetLevelDesc(0, &sd) != D3D_OK)
                return;
        
            // set up a quad
            SPRITEVERTEX verts[4];
            for (int i=0; i<4; i++)
            {
                verts[i].x = m_lpDX->m_client_width *0.5f * ((i%2)*2-1);
                verts[i].y = m_lpDX->m_client_height*0.5f * ((i/2)*2-1);
                verts[i].z = 1.0f;
                verts[i].tu = (float)(i%2)*(m_fractal_texsize/(float)sd.Width );
                verts[i].tv = (float)(i/2)*(m_fractal_texsize/(float)sd.Height);
                DWORD alpha_opacity = 255;  // note: only works if alpha blending enabled
                DWORD r = 255;
                DWORD g = 255;
                DWORD b = 255;
                verts[i].Diffuse = (alpha_opacity<<24) | (r<<16) | (g<<8) | b;  // alpha channel here sets the opacity, if alpha blending turned on (see above + below)
            }
        
            // draw it
            m_lpDX->m_lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, verts, sizeof(SPRITEVERTEX));
        
            // turn alpha blending back off (regardless)
            m_lpDX->m_lpDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
        }
        


This document copyright (c)2002+ Ryan M. Geiss.
Return to faq page