More than one graphic on the screen using textures

I want to draw more than one graphic on the screen, but all I can get is one graphic that fills the screen or nothing?!

SDL_RenderCopy(renderer, mTexture, null, &mRect); - draws nothing, while SDL_RenderCopy(renderer, mTexture, null, null); - draws as expected

I’m using the D programming language dlang.org and SDL 2.0.10 binding https://code.dlang.org/packages/bindbc-sdl in a bit of a wrapper I made, on macOS 10.14.6

module app;

//#mTexture.width, mTexture.height);
//#libpng warning: iCCP: known incorrect sRGB profile

/This source code copyrighted by Lazy Foo’ Productions (2004-2019)
and may not be redistributed without written permission.
/

//Using SDL and standard IO
import jecsdl;

//Frees media and shuts down SDL
void close();

string gPicture;

struct Sprite {
private:
float mx,my, mdx, mdy;
SDL_Texture* mTexture;
int mWidth, mHeight;
SDL_Rect mPos;
public:
this(float x, float y, SDL_Rect r, SDL_Texture* texture) {
mx = x;
my = y;
mdx = mdy = 1;
mWidth = r.w;
mHeight = r.h;
mTexture = texture;
}

void process() {
	mx += mdx;
	if (mx > SCREEN_WIDTH - mWidth || mx < 0)
		mdx *= -1;
	my += mdy;
	if (my > SCREEN_HEIGHT - mHeight || my < 0)
		mdy *= -1;
	mPos = SDL_Rect(cast(int)mx,cast(int)my, mWidth,mHeight);
}

void draw(SDL_Renderer* renderer) {
	//Render texture to screen
	SDL_RenderCopy(renderer, mTexture, null, &mPos);
}

}
Sprite[] gSprites;

void close()
{
//Destroy window
SDL_DestroyWindow( gWindow );

//Destroy texture
SDL_DestroyTexture(gTexture);

//Quit SDL subsystems
SDL_Quit();

}

int main(string[] args)
{
gPicture = “sprites.png”;
if (args.length > 1)
gPicture = args[1];

//Start up SDL and create window
if (setup != 0)
{
	writef( "setup - Failed to initialize!\n" );
}
else
{
	auto surface = IMG_Load("fire.png");
	if (surface is null) {
		writef("Surface load failed: ", IMG_GetError() );
		return 1;
	}
	scope(exit)
		SDL_FreeSurface(surface);
	gTexture = SDL_CreateTextureFromSurface( gRenderer, surface );
	SDL_Rect r = {0,0, surface.w,surface.h};
	writef("suface.w=%s, suface.h=%s\n", surface.w,surface.h);
	import std.random : uniform;
	foreach(_; 0 .. 10) {
		gSprites ~= Sprite(cast(float)uniform(0, SCREEN_WIDTH),cast(float)uniform(0, SCREEN_HEIGHT), r, gTexture);
	}

	SDL_Event event;
	bool done;
	while(! done) {
		SDL_PollEvent(&event);
		if(event.type == SDL_QUIT)
				done = true;

		foreach(s; gSprites)
			s.process;

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

		foreach(ref s; gSprites)
			s.draw(gRenderer);

		//Update screen
		SDL_RenderPresent( gRenderer );

		//for CPU
		SDL_Delay( 2 );
	}
}

//Free resources and close SDL
close();

return 0;

}

I can see that you’re executing things in the correct order, which are: clear the render target with SDL_RenderClear(), render everything you want to render and then finally update the window with SDL_RenderPresent(). That’s good.

There might be something wrong with either your SDL_Renderer object and/or your texture(s) is/are invalid. Do some error checking and make sure that everything is created properly.

The first thing I would look at is your homemade D bindings. From past experience, this is the place you’re most likely to have a problem.

Build SDL yourself, open the program in whatever debugger you have for the Mac, and put breakpoints on the SDL APIs you’re calling into, on the C side. Make sure all the arguments are coming across correctly. Calling convention mismatches and pointer issues can b0rk up everything in really bizarre ways if there’s a mistake in your bindings somewhere.

It sounds like your destination rect might be wrong. Make sure you’re passing a valid pointer to it, and that all its fields are valid.

Here’s everything else:

module jecsdl.setup;

import jecsdl.base;

bool bindbcSetup() {
/*
This version attempts to load the SDL shared library using well-known variations
of the library name for the host system.
*/
SDLSupport ret = loadSDL();
if(ret != sdlSupport) {
// Handle error. For most use cases, this is enough. The error handling API in
// bindbc-loader can be used for error messages. If necessary, it’s possible
// to determine the primary cause programmtically:

	if(ret == SDLSupport.noLibrary) {
		// SDL shared library failed to load
		assert(0, "no library");
	}
	else if(SDLSupport.badLibrary) {
		// One or more symbols failed to load. The likely cause is that the
		// shared library is for a lower version than bindbc-sdl was configured
		// to load (via SDL_201, SDL_202, etc.)
		assert(0, "old library, or some thing");
	}
}
/*
This version attempts to load the SDL library using a user-supplied file name.
Usually, the name and/or path used will be platform specific, as in this example
which attempts to load `SDL2.dll` from the `libs` subdirectory, relative
to the executable, only on Windows. It has the same return values.
*/
// version(Windows) loadSDL("libs/SDL2.dll")

/*
The satellite library loaders also have the same two versions of the load functions,
named according to the library name. Only the parameterless versions are shown
here. These return similar values as loadSDL, but in an enum namespace that matches
the library name: SDLImageSupport, SDLMixerSupport, and SDLTTFSupport.
*/
if(loadSDLImage() != sdlImageSupport) {
	/* handle error */
	assert(0,"problem with loading image library");
}

return true;

}

bool initKeys() {
g_keystate = SDL_GetKeyboardState(null);
foreach(tkey; cast(SDL_Scancode)0 … SDL_NUM_SCANCODES)
g_keys ~= new TKey(cast(SDL_Scancode)tkey);

return g_keys.length == SDL_NUM_SCANCODES;

}

int setup(string title = “SDL Joel program”, int screenWidth = 640, int screenHeight = 480) {
SCREEN_WIDTH = screenWidth;
SCREEN_HEIGHT = screenHeight;

bool init()
{
	//Initialization flag
	bool success = true;

	if (! bindbcSetup) {
		success = false;
	} else {
		//Initialize SDL
		if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
		{
			writef( "SDL could not initialize! SDL_Error: %s\n", SDL_GetError() );
			success = false;
		}
		else
		{
			import std.string : toStringz;
			//Create window
			gWindow = SDL_CreateWindow( title.toStringz, SDL_WINDOWPOS_UNDEFINED,
				SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
			if( gWindow is null )
			{
				writef( "Window could not be created! SDL_Error: %s\n", SDL_GetError() );
				success = false;
			}
		}

		//Set texture filtering to linear
		if( !SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "1" ) )
		{
			writef( "Warning: Linear texture filtering not enabled!" );
		}

		//Create renderer for window
		gRenderer = SDL_CreateRenderer( gWindow, -1, SDL_RENDERER_ACCELERATED );
		if( gRenderer is null )
		{
			printf( "Renderer could not be created! SDL Error: %s\n", SDL_GetError() );
			success = false;
		}
		else
		{
			//Initialize renderer color
			SDL_SetRenderDrawColor( gRenderer, 0x00, 0x0, 0xFF, 0xFF );

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

	return success;
}

if (! init)
	return 1;

return 0;

}

################# another file
module jecsdl.base;

public {
//Using SDL and standard IO
import std.stdio;
import bindbc.sdl;
import bindbc.sdl.image; // SDL_image binding

import std.math, std.conv, std.path;

}

import jecsdl.setup;

import std.datetime: Duration;
import std.datetime.stopwatch: StopWatch;

//public import jec.base, jec.input, jec.jexting, jec.setup, jec.sound, jmisc, jec.gui, jec.guifile, jec.guiconfirm;
//public import jec, jmisc;

//Screen dimensions
int SCREEN_WIDTH;
int SCREEN_HEIGHT;

string getClipboardText() {
import std.conv : to;

return SDL_GetClipboardText().to!string;

}

void setClipboardText(string txt) {
import std.string : toStringz;
if (SDL_HasClipboardText() == SDL_TRUE)
SDL_SetClipboardText(txt.toStringz);
}

//The window we’ll be rendering to
SDL_Window* gWindow;

//The window renderer
SDL_Renderer* gRenderer;

//Current displayed texture
SDL_Texture* gTexture;

Uint8* g_keystate;

SDL_Texture* loadTexture( string path )
{
//The final texture
SDL_Texture* newTexture;

import std.string : toStringz;
//Load image at specified path
SDL_Surface* loadedSurface = IMG_Load( path.toStringz );
if( loadedSurface is null )
{
	writef( "Unable to load image %s! SDL_image Error: %s\n", path, IMG_GetError() );
}
else
{
	//Create texture from surface pixels
    newTexture = SDL_CreateTextureFromSurface( gRenderer, loadedSurface );
	if( newTexture is null )
	{
		writef( "Unable to create texture from %s! SDL Error: %s\n", path, SDL_GetError() );
	}

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

return newTexture;

}

/**

  • Handle keys, one hit buttons
    */
    class TKey {
    /// Key state
    enum KeyState {up, down, startGap, smallGap}

    /// Key state variable
    KeyState _keyState;

    /// Start pause
    static _startPause = 200;

    /// Moving momments
    static _pauseTime = 40; // msecs

    /// Timer for start pause
    StopWatch _stopWatchStart;

    /// Timer for moving moments
    StopWatch _stopWatchPause;

    /// Key to use
    SDL_Scancode tKey;

    /// Is key set to down
    bool _keyDown;

    /**

    • Constructor
      */
      this(SDL_Scancode tkey0) {
      tKey = tkey0;
      _keyDown = false;
      _keyState = KeyState.up;
      }

    /// Is key pressed
    bool keyPressed() { // eg. g_keys[Keyboard.Key.A].keyPressed
    //return Keyboard.isKeyPressed(tKey) != 0;
    return g_keystate[tKey] != 0;
    }

    /// Goes once per key hit
    bool keyTrigger() { // eg. g_keys[Keyboard.Key.A].keyTrigger
    if (g_keystate[tKey] && _keyDown == false) {
    _keyDown = true;
    return true;
    } else if (! g_keystate[tKey]) {
    _keyDown = false;
    }

     return false;
    

    }

    // returns true doing trigger other wise false saying the key is already down
    /** One hit key */
    /+
    Press key down, print the character. Keep holding down the key and the cursor move at a staggered pace.
    +/
    bool keyInput() { // eg. g_keys[Keyboard.Key.A].keyInput
    if (! g_keystate[tKey])
    _keyState = KeyState.up;

     if (g_keystate[tKey] && _keyState == KeyState.up) {
     	_keyState = KeyState.down;
     	_stopWatchStart.reset;
     	_stopWatchStart.start;
    
     	return true;
     }
     
     if (_keyState == KeyState.down && _stopWatchStart.peek.total!"msecs" > _startPause)  {
     	_keyState = KeyState.smallGap;
     	_stopWatchPause.reset;
     	_stopWatchPause.start;
     }
     
     if (_keyState == KeyState.smallGap && _stopWatchPause.peek.total!"msecs" > _pauseTime) {
     	_keyState = KeyState.down;
     	
     	return true;
     }
     
     return false;
    

    }

    /** hold key */
    // bool keyPress() {
    // return Keyboard.isKeyPressed(tKey) > 0;
    // }
    }
    // eg lkeys[Letter.p].keyTrigger
    /// Keys array
    TKey[] g_keys; // g_keys[Keyboard.Key.T].keyTrigger

/// Trim off parts of file name
auto trim(T)(in T str) { //if (SomeString!T) {
import std.path : stripExtension;
if (str.length > 6 && str[0 … 2] == “./”)
return str[2 … $].stripExtension.dup;
else
return str.dup;
}

/// Stop with key
void keyHold(int key) {
while(g_keys[key]) {
SDL_PumpEvents();
SDL_Delay(1);
} //# need to add like sleep(50.dur!msecs);, not that it stops the tight loop!
//while(Keyboard.isKeyPressed(cast(Keyboard.Key)key)) { sleep(50.dur!msecs); } // eg. keyHold(Keyboard.Key.Num0 + i);
}

Oh, I got it working. I put in some 'printf’s so to speak. And the values I was passing for SDL_RenderCopy SDL_Rect were all 0’s.