0

The code bellow just polling mouse events and draw lines between the previous and last mouse coordinates in case of left mouse button pressed down, nothing special and its using only fast integer operations.

The code is fast enough on PC but compiling and running it on a pi3 the rendering speed gets extremely slow when i enable the experimental openGL driver. Tried to call SDL_CreateRenderer() with flag SDL_RENDERER_ACCELERATED but it did not help a lot. The rendering speed is much faster when i disable the openGL driver and calling SDL_CreateRenderer() with flag SDL_RENDERER_SOFTWARE (yet not fast enough). I think i can get much better results with somehow utilizing hw accelerated rendering the right way, but how? What can be the problem?

#include <SDL.h>

//Screen dimension constants
const int SCREEN_WIDTH = 1024; //640;
const int SCREEN_HEIGHT = 768; //480;

Uint32* pixels;

struct _mCoords
{
    int xprev;
    int yprev;
    int x;
    int y;
};

_mCoords mCoords;

SDL_Window * window;
SDL_Renderer * renderer;
SDL_Texture * texture;

void clean();
void drawpixel( int mX, int mY );
void bhm_line(int x1,int y1,int x2,int y2);
int isClose(int x1, int y1, int x2, int y2);

int main( int argc, char* args[] )
{
    bool quit = false;
    bool leftMouseButtonDown = false;

    pixels = new Uint32[SCREEN_WIDTH * SCREEN_HEIGHT];
    memset(pixels, 255, SCREEN_WIDTH * SCREEN_HEIGHT * sizeof(Uint32)); //background to white

    SDL_Event event;
    SDL_Init(SDL_INIT_VIDEO);

    window = SDL_CreateWindow("Pixel drawing", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, 0);
    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED );
    texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, SCREEN_WIDTH, SCREEN_HEIGHT);

    while (!quit)
    {
        while (SDL_PollEvent(&event) != 0 )
        {
            switch (event.type)
            {
                case SDL_QUIT:
                    quit = true;
                    break;

                case SDL_MOUSEBUTTONUP:
                    if (event.button.button == SDL_BUTTON_LEFT)
                        leftMouseButtonDown = false;
                    break;

                case SDL_MOUSEBUTTONDOWN:
                    if (event.button.button == SDL_BUTTON_LEFT)
                        leftMouseButtonDown = true;

                    mCoords.xprev = event.motion.x;
                    mCoords.yprev = event.motion.y;

                case SDL_MOUSEMOTION:
                    if (leftMouseButtonDown)
                    {
                        mCoords.x = event.motion.x;
                        mCoords.y = event.motion.y;

                        if (isClose(mCoords.x, mCoords.y, mCoords.xprev, mCoords.yprev) == 0)
                            bhm_line(mCoords.xprev, mCoords.yprev, mCoords.x, mCoords.y);
                        else
                            drawpixel( mCoords.x, mCoords.y );

                        mCoords.xprev = mCoords.x;
                        mCoords.yprev = mCoords.y;

                        //SDL_RenderClear(renderer);
                        SDL_UpdateTexture(texture, NULL, pixels, SCREEN_WIDTH * sizeof(Uint32));    //pixel array to texture
                        SDL_RenderCopy(renderer, texture, NULL, NULL);  //to backbuffer
                        SDL_RenderPresent(renderer);                    //to frontbuffer
                    }
                    break;
            }
        }
    }

    clean();
    SDL_Quit();

    return 0;
}

void clean()
{
    delete[] pixels;
    SDL_DestroyTexture(texture);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
}

void bhm_line(int x1,int y1,int x2,int y2)
//Breshenian's line algorithm
{
    int x,y,dx,dy,dx1,dy1,px,py,xe,ye,i;
    dx=x2-x1;
    dy=y2-y1;
    dx1=fabs(dx);
    dy1=fabs(dy);
    px=2*dy1-dx1;
    py=2*dx1-dy1;
    if(dy1<=dx1)
    {
        if(dx>=0)
        {
            x=x1;
            y=y1;
            xe=x2;
        }
        else
        {
            x=x2;
            y=y2;
            xe=x1;
        }
        drawpixel(x,y);
        for(i=0;x<xe;i++)
        {
            x=x+1;
            if(px<0)
            {
                px=px+2*dy1;
            }
            else
            {
                if((dx<0 && dy<0) || (dx>0 && dy>0))
            {
                y=y+1;
            }
                else
                {
                    y=y-1;
                }
                px=px+2*(dy1-dx1);
            }
            drawpixel(x,y);
        }
    }
    else
    {
        if(dy>=0)
        {
            x=x1;
            y=y1;
            ye=y2;
        }
        else
        {
            x=x2;
            y=y2;
            ye=y1;
        }
        drawpixel(x,y);
        for(i=0;y<ye;i++)
        {
            y=y+1;
            if(py<=0)
            {
                py=py+2*dx1;
            }
            else
            {
                if((dx<0 && dy<0) || (dx>0 && dy>0))
                {
                    x=x+1;
                }
                else
                {
                    x=x-1;
                }
                py=py+2*(dx1-dy1);
            }
            drawpixel(x,y);
        }
    }
}

int isClose(int x1, int y1, int x2, int y2)
{
    if ((abs(x2-x1) > 1) || (abs(y2-y1) > 1))
        return 0;
    else
        return 1;
}

void drawpixel(int mX, int mY)
{
    int pixelidx = mY * SCREEN_WIDTH + mX;
    pixels[pixelidx] = pixels[pixelidx] & 0x0;
}
  • If i put the block... <code> SDL_RenderClear(renderer); SDL_UpdateTexture(texture, NULL, pixels, SCREEN_WIDTH * sizeof(Uint32)); SDL_RenderCopy(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer); </code> ... out of 'case SDL_MOUSEMOTION' then the rendering gets much faster, why? – kidbro Jan 6 '18 at 20:31
0

Using "accelerated" OpenGL doesn't help you when you're only doing software rendering like this.

If you disable "acceleration" then the set of pixels you've hand-written into system memory are copied once into the shadow framebuffer in X11, and then one more time into the actual framebuffer.

With "acceleration", you first have to manually tile the CPU-side pixels you've made into the GPU's texture. With current Mesa on armhf this is fast, but the fast path may still be disabled in Raspbian due to their compiler. Once you've done that, you're using RenderCopy to copy from the texture into the backbuffer. Then you SwapBuffers to copy from the backbuffer into the front buffer.

For a bonus penalty, since you have the Clear call commented out, the GPU ends up loading the previous framebuffer contents before drawing your texture, increasing the bandwidth cost.

To do this fast, you shouldn't be hand-writing pixels on the CPU. Just clear and draw a line using OpenGL, then swap. Or, if not, use an SDL interface that maps to X11PutImage, which will do the software tiling directly into the actual framebuffer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.