Question

I have just gotten too frustrated with trying to figure this out. I have been following http://lazyfoo.net/tutorials/SDL/index.php. I then tried to start making a game with SDL2 but ran into this error very quickly: I can't render anything to the screen. The screen pops up, and I can clear it correctly and everything. If I call SDL_GetError(), the output is "Invalid Renderer". I've checked the code to try to find out where this error could be caused, and searched the internet to no avail. Something that might be causing the problem is that I have a class which has a render method. This render method calls a render method on its texture. That might be the place where I screwed up somehow, but I can't tell.

Source code:

#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <string>
#include <stdio.h>

const int WINDOW_WIDTH = 1280;
const int WINDOW_HEIGHT= 960;

SDL_Window* gWindow = NULL;
SDL_Renderer* gRenderer = NULL;

enum MOVEMENT_STATES
{
    DOWN1,
    DOWN2,
    LEFT1,
    LEFT2,
    RIGHT1,
    RIGHT2,
    UP1,
    UP2,
    MOVEMENT_STATES_TOTAL
};

//Texture wrapper class
class LTexture
{
    public:

        //Initializes variables
        LTexture(std::string path);

        //Deallocates memory
        ~LTexture();

        #ifdef _SDL_TTF_H
        //Creates image from font string
        bool loadFromRenderedText( std::string textureText, SDL_Color textColor );
        #endif

        //Deallocates texture
        void free();

        //Set color modulation
        void setColor( Uint8 red, Uint8 green, Uint8 blue );

        //Set blending
        void setBlendMode( SDL_BlendMode blending );

        //Set alpha modulation
        void setAlpha( Uint8 alpha );

        //Renders texture at given point
        void render( int x, int y, SDL_Rect* clip = NULL, double angle = 0.0, SDL_Point* center = NULL, SDL_RendererFlip flip = SDL_FLIP_NONE );

        //Gets image dimensions
        int getWidth();
        int getHeight();

    private:

        //Loads image at specified path
        bool loadFromFile( std::string path );

        //The actual hardware texture
        SDL_Texture* mTexture;

        //Image dimensions
        int mWidth;
        int mHeight;
};

//Sprite classes
class Entity
{
    public:

        Entity(int hp);

        ~Entity();

        //void handleEntity(); Unimplemented

        void render(int x, int y);

        int health;
        int maxHealth;

        LTexture* mCurrentTexture;
};

Entity::Entity(int hp)
{
    mCurrentTexture = NULL;

    health = hp;
    maxHealth = hp;
}

Entity::~Entity()
{
    mCurrentTexture = NULL;
}

void Entity::render(int x, int y)
{
    //If current texture is non-null, render it
    if (mCurrentTexture != NULL)
    {
        (*mCurrentTexture).render(x, y);
    }
    else
    {
        printf("Texture is null!\n");
    }
}

LTexture::LTexture(std::string path)
{
    //Initialize
    mTexture = NULL;
    loadFromFile(path);
    mWidth = 0;
    mHeight = 0;
}

LTexture::~LTexture()
{
    //Deallocate
    free();
}

bool LTexture::loadFromFile( std::string path )
{
    //Get rid of preexisting texture
    free();

    //The final texture
    SDL_Texture* newTexture = NULL;

    //Load image at specified path
    SDL_Surface* loadedSurface = IMG_Load( path.c_str() );
    if( loadedSurface == NULL )
    {
        printf( "Unable to load image %s! SDL_image Error: %s\n", path.c_str(), IMG_GetError() );
    }
    else
    {
        //Color key image
        SDL_SetColorKey( loadedSurface, SDL_TRUE, SDL_MapRGB( loadedSurface->format, 0, 0xFF, 0xFF ) );

        //Create texture from surface pixels
        newTexture = SDL_CreateTextureFromSurface( gRenderer, loadedSurface );
        if( newTexture == NULL )
        {
            printf( "Unable to create texture from %s! SDL Error: %s\n", path.c_str(), SDL_GetError() );
        }
        else
        {
            //Get image dimensions
            mWidth = loadedSurface->w;
            mHeight = loadedSurface->h;
        }

        //Get rid of old loaded surface
        SDL_FreeSurface( loadedSurface );
    }

    //Return success
    mTexture = newTexture;
    return mTexture != NULL;
}

#ifdef _SDL_TTF_H
bool LTexture::loadFromRenderedText( std::string textureText, SDL_Color textColor )
{
    //Get rid of preexisting texture
    free();

    //Render text surface
    SDL_Surface* textSurface = TTF_RenderText_Solid( gFont, textureText.c_str(), textColor );
    if( textSurface != NULL )
    {
        //Create texture from surface pixels
        mTexture = SDL_CreateTextureFromSurface( gRenderer, textSurface );
        if( mTexture == NULL )
        {
            printf( "Unable to create texture from rendered text! SDL Error: %s\n", SDL_GetError() );
        }
        else
        {
            //Get image dimensions
            mWidth = textSurface->w;
            mHeight = textSurface->h;
        }

        //Get rid of old surface
        SDL_FreeSurface( textSurface );
    }
    else
    {
        printf( "Unable to render text surface! SDL_ttf Error: %s\n", TTF_GetError() );
    }


    //Return success
    return mTexture != NULL;
}
#endif

void LTexture::free()
{
    //Free texture if it exists
    if( mTexture != NULL )
    {
        SDL_DestroyTexture( mTexture );
        mTexture = NULL;
        mWidth = 0;
        mHeight = 0;
    }
}

void LTexture::setColor( Uint8 red, Uint8 green, Uint8 blue )
{
    //Modulate texture rgb
    SDL_SetTextureColorMod( mTexture, red, green, blue );
}

void LTexture::setBlendMode( SDL_BlendMode blending )
{
    //Set blending function
    SDL_SetTextureBlendMode( mTexture, blending );
}

void LTexture::setAlpha( Uint8 alpha )
{
    //Modulate texture alpha
    SDL_SetTextureAlphaMod( mTexture, alpha );
}

void LTexture::render( int x, int y, SDL_Rect* clip, double angle, SDL_Point* center, SDL_RendererFlip flip )
{
    //Set rendering space and render to screen
    SDL_Rect renderQuad = { x, y, mWidth, mHeight };

    //Set clip rendering dimensions
    if( clip != NULL )
    {
        renderQuad.w = clip->w;
        renderQuad.h = clip->h;
    }

    //Render to screen
    SDL_RenderCopyEx( gRenderer, mTexture, clip, &renderQuad, angle, center, flip );
}

int LTexture::getWidth()
{
    return mWidth;
}

int LTexture::getHeight()
{
    return mHeight;
}

bool init()
{
    bool success = true;

    //Initialize SDL
    if(SDL_Init(SDL_INIT_VIDEO) < 0)
    {
        printf("SDL could not initialize! SDL Error: %s\n", SDL_GetError());
        success = false;
    }
    else
    {
        //Enable VSync
        if(!SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1"))
        {
            printf("Warning: VSync not enabled!");
        }
        //Set texture filtering to linear
        if( !SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "1" ) )
        {
            printf( "Warning: Linear texture filtering not enabled!" );
        }
    }

    //Create window
    gWindow = SDL_CreateWindow("Dungeon Dash", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN);
    if (gWindow == NULL)
    {
        printf("SDL could not create window! SDL Error: %s\n", SDL_GetError());
        success = false;
    }
    else
    {
        //Create renderer for window
        gRenderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_ACCELERATED);
        if (gRenderer == NULL)
        {
            printf("SDL could not create renderer! SDL Error: %s\n", SDL_GetError());
            success = false;
        }
        else
        {
            //Initialize renderer color
            SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );

            //Initialize PNG loading
            int imgFlags = IMG_INIT_PNG;
            if( !( IMG_Init( imgFlags ) & imgFlags ) )
            {
                printf( "SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError() );
                success = false;
            }
        }
    }

    return success;
}

int main(int argc, char* args[])
{

    if(!init())
    {
        printf("SDL could not initialize!\n");
    }
    else
    {

        bool quit = false;
        SDL_Event e; //Event handler

        //Load all textures
        LTexture boltTexture("graphics/bolt.png");
        LTexture burst1Texture("graphics/burst1.png");
        LTexture burst2Texture("graphics/burst2.png");
        LTexture chestTexture("graphics/chest.png");
        LTexture coinTexture("graphics/coin.png");
        LTexture emptyTileTexture("graphics/emptyTile.png");
        LTexture floorTileTexture("graphics/floorTile.png");
        LTexture healthPadTexture("graphics/healthPad.png");
        LTexture keyTexture("graphics/key.png");
        LTexture snakeDown1Texture("graphics/snakeDown1.png");
        LTexture snakeDown2Texture("graphics/snakeDown2.png");
        LTexture snakeLeft1Texture("graphics/snakeLeft1.png");
        LTexture snakeLeft2Texture("graphics/snakeLeft2.png");
        LTexture snakeRight1Texture("graphics/snakeRight1.png");
        LTexture snakeRight2Texture("graphics/snakeRight2.png");
        LTexture snakeUp1Texture("graphics/snakeUp1.png");
        LTexture snakeUp2Texture("graphics/snakeUp2.png");
        LTexture spiderDown1Texture("graphics/spiderDown1.png");
        LTexture spiderDown2Texture("graphics/spiderDown2.png");
        LTexture spiderLeft1Texture("graphics/spiderLeft1.png");
        LTexture spiderLeft2Texture("graphics/spiderLeft2.png");
        LTexture spiderRight1Texture("graphics/spiderRight1.png");
        LTexture spiderRight2Texture("graphics/spiderRight2.png");
        LTexture spiderUp1Texture("graphics/spiderUp1.png");
        LTexture spiderUp2Texture("graphics/spiderUp2.png");
        LTexture teleportPadTexture("graphics/teleportPad.png");
        LTexture upgradePadTexture("graphics/upgradePad.png");
        LTexture wallTileDownTexture("graphics/wallTileDown.png");
        LTexture wallTileLeftTexture("graphics/wallTileLeft.png");
        LTexture wallTileRightTexture("graphics/wallTileRight.png");
        LTexture wallTileUpTexture("graphics/wallTileUp.png");
        LTexture wizardDown1Texture("graphics/wizardDown1.png");
        LTexture wizardDown2Texture("graphics/wizardDown2.png");
        LTexture wizardLeft1Texture("graphics/wizardLeft1.png");
        LTexture wizardLeft2Texture("graphics/wizardLeft2.png");
        LTexture wizardRight1Texture("graphics/wizardRight1.png");
        LTexture wizardRight2Texture("graphics/wizardRight2.png");
        LTexture wizardUp1Texture("graphics/wizardUp1.png");
        LTexture wizardUp2Texture("graphics/wizardUp2.png");

        //Main player
        Entity player1(20);
        player1.mCurrentTexture = &wizardDown1Texture;

        //Main loop
        while(!quit)
        {
            //Event loop
            while(SDL_PollEvent(&e) != 0)
            {
                if(e.type == SDL_QUIT)
                {
                    quit = true;
                }
            }

            //Clear screen
            SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 255);
            SDL_RenderClear(gRenderer);

            player1.render(0, 0);

            //Update screen
            SDL_RenderPresent(gRenderer);
        }

        //If script reaches here, user has exited; quit program

        //Free loaded images
        boltTexture.free();
        burst1Texture.free();
        burst2Texture.free();
        chestTexture.free();
        coinTexture.free();
        emptyTileTexture.free();
        floorTileTexture.free();
        healthPadTexture.free();
        keyTexture.free();
        snakeDown1Texture.free();
        snakeDown2Texture.free();
        snakeLeft1Texture.free();
        snakeLeft2Texture.free();
        snakeRight1Texture.free();
        snakeRight2Texture.free();
        snakeUp1Texture.free();
        snakeUp2Texture.free();
        spiderDown1Texture.free();
        spiderDown2Texture.free();
        spiderLeft1Texture.free();
        spiderLeft2Texture.free();
        spiderRight1Texture.free();
        spiderRight2Texture.free();
        spiderUp1Texture.free();
        spiderUp2Texture.free();
        teleportPadTexture.free();
        upgradePadTexture.free();
        wallTileDownTexture.free();
        wallTileLeftTexture.free();
        wallTileRightTexture.free();
        wallTileUpTexture.free();
        wizardDown1Texture.free();
        wizardDown2Texture.free();
        wizardLeft1Texture.free();
        wizardLeft2Texture.free();
        wizardRight1Texture.free();
        wizardRight2Texture.free();
        wizardUp1Texture.free();
        wizardUp2Texture.free();

        //Destroy window
        SDL_DestroyRenderer( gRenderer );
        SDL_DestroyWindow( gWindow );
        gWindow = NULL;
        gRenderer = NULL;

        //Quit SDL subsystems
        IMG_Quit();
        SDL_Quit();
    }
}

Thanks in advance for any help.

Was it helpful?

Solution

The line which throws the error is the one creating the renderer, but that error can be ignored (as it does not return NULL). SDL uses a macro to check if the renderer is OK and it automatically sets that error string if its not ok. It might be doing that check too early. This has nothing to do with your problem, though.

Your problem here is because in LTexture constructor you call loadFromFile(), which sets mWidth and mHeight properly. Afterwards you set them to 0 again, making your renderQuad have no area, thus making your texture not to render.

Set them to 0 before that or, even better, set them to 0 in the initializer list:

LTexture::LTexture(std::string path)
: mTexture(NULL), mWidth(0), mHeight(0)
{
  loadFromFile(path);
}

Also, LTexture class has a method called free, which, albeit not a reserved name, may cause confusion in your fellow colleagues (as it did with me :), you may consider a different name.

OTHER TIPS

I found this question because I was getting the same error from SDL_GetError. The answers here that indicate that SDL_GetError may say "Invalid Renderer" in situations where there is no error were informative. SDL_GetError can only be relied on in cases where the last SDL function called actually indicated an error.

My situation was slightly different, though. I was calling SDL_WaitEventTimeout, which returns 1 to indicate an early return due to an event, or 0 for either an expired timer or for an error, and makes the user rely on SDL_GetError to determine whether an error occurred. In this case, calling SDL_ClearError before calling SDL_WaitEventTimeout is crucial. Plus, SDL_GetError returns an empty string, not a null pointer, when there is no error.

int frameRate = 30;
SDL_Event e;

SDL_ClearError();
int ret = SDL_WaitEventTimeout( &e, 1000/frameRate );

if ( ret )
{
    // event received, process events
}
else
{
    // timeout or error; have to check
    const char *s = SDL_GetError();
    if ( *s )
    {
        cout << "SDL_WaitEventTimeout: " << s << endl;
    }
    else
    {
        // normal timeout, do normal actions
    }
}

This appears to be a flaw in the design of SDL_WaitEventTimeout, which is wasting most of the space in its return value. It really should be returning -1 when it causes an error, 0 when it times out, and the number of milliseconds remaining when an event occurs. I suspect it's actually deprecated as a frame timer, and I'll have to rely on SDL_PollEvent for events and have to use OS/hardware-native timers for frame sync, which wastes the portability of SDL.

Try changing the flags that you pass to SDL_CreateRenderer(). I was having the same issue as you, and I eventually discovered that it was that, on my machine, SDL has trouble with hardware-accelerated rendering (which happens to be the default). Try changing that SDL_RENDERER_ACCELERATED to a SDL_RENDERER_SOFTWARE, and see if that helps.

Additionally, whenever you have a particularly tricky bug that you are trying to fix, break the problem down. You have a huge (for Stack Overflow) amount of code here, and had you cut it down to just the rendering bits then (1) others might have been able to help you more quickly and (2) you may have been able to solve the problem by yourself.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top