idtownie idtownie - 25 days ago 5
C++ Question

SDL 2.0 - Invalid Renderer Error

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.

Answer

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.