Programming 2D Games

The official forum for "Programming 2D Games" the book by: Charles Kelly

It is currently Sun Oct 22, 2017 10:05 am

All times are UTC




Post new topic Reply to topic  [ 1 post ] 
Author Message
PostPosted: Tue Aug 08, 2017 7:39 pm 
Offline

Joined: Wed Mar 22, 2017 10:33 pm
Posts: 14
Hi. In my game, I have a large drawing operation of [650][650] entities for my map. This is very laggy. So, I am implementing multithreading. What is the best way to go about this? the way I have it, only 1 map thread will run and any concurrent threads will not produce any output. The way my threads work is that each thread runs a method that runs a modified draw() method (the one in graphics), which is overloaded to take the core of that thread. When this is done the modified draw will match the core to a sprite object (i added an vector of sprite objects to support multithreading).


IF YOU REALLY DONT WANT TO FIND THE ISSUE, just ask me the best way to multithread my drawing() operations on entities.
I will provide code below (warning: this is alot of front loading but may be useful):

MapManager.h
#ifndef _MAPMANAGER_H
#define _MAPMANAGER_H
#include "TSXPARSER.h"
#include "constants.h"
#include "gameError.h"
#include "Tile.h"
#include "game.h"
#include "textureManager.h"
#include <vector>
//tmx stuff that no one should touch
#include "Tmxmap.h"
#include "TmxTileset.h"
#include "TmxLayer.h"
#include "TmxTileLayer.h"
#include "TmxObjectGroup.h"
#include "TmxImageLayer.h"
#include "TSXParser.h"
#include "TmxMapTile.h"
#include <iostream>
class MapManager
{
private:
int centerY;
int centerX;
float mapScale;
Tmx::Map map;
const Tmx::TileLayer *groundLevel;
int cores;
public:
bool *centersquareOn;
Graphics *mapGraphics;
std::vector<std::thread>threads;
MapManager(bool *centersquare,Graphics *graph);
void drawMap(int cores, Tile layer[650][650], int centX, int centY);
//void drawTiles(Tile layer[650][650], int startGridX, int startGridY, int endGridX, int endGridY,int centX,int centY);
void centralize(Tile layer[650][650],Tile toCenter);//this method centralizes the map by making a tile the center
void mapZoom(Input *input,Tile layer[650][650],int &xbuffer,int &ybuffer, float &scalebuffer);//this method handles zooming in and out of the map via scroll bar
bool mapInit(Tile layer[650][650], Tmx::MapTile layerinfo[650][650], Game *game, TextureManager &gametiles);
bool tilesetParse(const char* filename);
bool parse(std::string filename,Tmx::MapTile gamemap[650][650]);
~MapManager();
};
#endif

and mapManager.cpp:
#include "MapManager.h"

void drawTiles(MapManager *map,Tile layer[650][650], int startGridX, int startGridY, int endGridX, int endGridY, int centX, int centY,int core);

MapManager::MapManager(bool *centersquare,Graphics *graph):map()
{
mapScale = 1;
centersquareOn = centersquare;
mapGraphics = graph;
}

void MapManager::drawMap(int cores,Tile layer[650][650],int centX,int centY)
{


int workshare = MAP_SIZE / cores; //this variable roughly holds how many tiles each thread has to operate on (square this number to get the total tiles that the thread will work on)
int differenceBetween = workshare*cores;//Holds the square root of the total tiles operated on (if this varaible is 650 then the whole map is covered by the threads)
int extraTiles = MAP_SIZE - differenceBetween;//this variable holds how many tiles the last thread will operate on(if is equal to 0 then all threads have equal tile amounts)
int threadTiles;

for (int x = 0; x < cores; x++)
{
mapGraphics->spriteBegin(mapGraphics->mtSprites[x]);


if (x == (cores - 1))
{
threadTiles = x*workshare + extraTiles;
}

else
threadTiles = x*workshare;
// threads.emplace_back(&drawTiles,this,layer, 325*(x-1), 325*(x-1), 324*(x-1), 324*(x-1), centX, centY,x-1);


}
//}
std::thread thread1(&drawTiles, this, layer, 0, 0 , 324 , 324 , centX, centY, 0);
std::thread thread2(&drawTiles, this, layer, 325, 325,650,650, centX, centY, 1);
/*for (std::<std::thread>::iterator it = threads.begin(); it < threads.end(); it++)
{
if (it->joinable())
it->join();
else {}

}*/
if (thread1.joinable()&&thread2.joinable())
{
thread1.join();
thread2.join();
}
else
{ }
for (int x = 0; x < cores; x++)
{
mapGraphics->spriteEnd(mapGraphics->mtSprites[x]);
}
return;
}
void MapManager::centralize(Tile layer[650][650],Tile toCenter)
{
centerX = toCenter.getTileX();//this variable holds the grid X coord of toCenter
centerY = toCenter.getTileY();//this variable holds the grid Y coord of toCenter
int xOffset;//offset of x grid coord in relation to the center tile in the for loop
int yOffset;//offset of y grid coord in relation to the center tile in the for loop
//make tocenter x and y the middle of the screen
toCenter.setX(GAME_WIDTH / 2);
toCenter.setY(GAME_HEIGHT / 2);
for (int x = 0; x < MAP_SIZE; x++)
{
xOffset = -(centerX - x);
for (int y = 0; y < MAP_SIZE; y++)
{
yOffset = -(centerY - y);
layer[x][y].setX(toCenter.getX() + (xOffset*32*toCenter.getScale()));
layer[x][y].setY(toCenter.getY() + (yOffset*32*toCenter.getScale()));
}
}


}

void MapManager::mapZoom(Input *input, Tile layer[650][650], int &xbuffer, int &ybuffer, float &scalebuffer)
{

int halfx = GAME_WIDTH / 2;
int halfy = GAME_HEIGHT / 2;
int xOfMiddle=-1;//the "grid" x coord for middle tile note: only 0 on startup
int yOfMiddle=-1;//the "grid" y coord for middle tile note: only 0 on startup
//find which tile contains the middle pixel
for (int x = 0; x < MAP_SIZE; x++)
{
for (int y = 0; y < MAP_SIZE; y++)
{
if (halfx >= layer[x][y].getX() && halfx <= layer[x][y].getX() + layer[x][y].getWidth()//if the middle of screen is inbetween left edge of tile and right edge
&& halfy >= layer[x][y].getY() && halfy <= layer[x][y].getY() + layer[x][y].getHeight())//and the middle of screen is inbetween top and bottom of tile
{
xOfMiddle=x;
yOfMiddle = y;
}
}
}
if (xOfMiddle == -1)
xOfMiddle = MAP_SIZE/2;
if (yOfMiddle == -1)
yOfMiddle = MAP_SIZE/2;
xbuffer = xOfMiddle;
ybuffer = yOfMiddle;
scalebuffer = layer[325][325].getScale();

float scrollCoefficient;
bool mouseUp=input->getMouseScrollUp();
bool mouseDown=input->getMouseScrollDown();
mouseUp == true ? scrollCoefficient = 2 : mouseDown == true ? scrollCoefficient = 0.5 : scrollCoefficient = -1;//the negative one part means that no scrolling input was entered in that frame
if (scrollCoefficient == -1)//if no scroll was entered than the method can end right here
{
return;
}
float reciprocal = 1 / ZOOMMAX;//this varriable is the reciprocal of ZOOMMAX.
if (layer[0][0].getScale() >= ZOOMMAX&&mouseUp==true)
return;
if (layer[0][0].getScale() <= reciprocal&&mouseDown == true)
return;
mapScale = layer[0][0].getScale() *scrollCoefficient;
for (int x = 0; x < MAP_SIZE; x++)
{

for (int y = 0; y < MAP_SIZE; y++)
{
layer[x][y].setScale(mapScale);

}
}
centralize(layer, layer[xOfMiddle][yOfMiddle]);

xOfMiddle = 0;
yOfMiddle = 0;
scalebuffer = layer[0][0].getScale();
input->clear(inputNS::SCROLL_IN);
}

bool MapManager::mapInit(Tile layer[650][650],Tmx::MapTile layerinfo[650][650],Game *game, TextureManager &gametiles)
{
cores = std::thread::hardware_concurrency();
int tileframe;
if (!tilesetParse(TSXSHEET))
throw(GameError(gameErrorNS::FATAL_ERROR, "Could not parse the .tsx tilesheet."));
if (!parse(TMXMAP, layerinfo))
throw(GameError(gameErrorNS::FATAL_ERROR, "Could not parse the .tmx map"));
for (int x = 0; x < MAP_SIZE; x++)
{
for (int y = 0; y < MAP_SIZE; y++)
{

if (!layer[x][y].initialize(game, 32, 32, 4, &gametiles))
return false;
layer[x][y].setTileX(x);
layer[x][y].setTileY(y);

tileframe = layerinfo[x][y].id;
if (tileframe == 0)
tileframe = 12;
layer[x][y].setLoop(false);
layer[x][y].setCurrentFrame(tileframe);
layer[x][y].setX(32 * x);
layer[x][y].setY(32 * y);
layer[x][y].flipHorizontal(layerinfo[x][y].flippedHorizontally);
layer[x][y].flipVertical(layerinfo[x][y].flippedVertically);
layer[x][y].setScale(1);

}
}
/* int workshare = MAP_SIZE /cores; //this variable roughly holds how many tiles each thread has to operate on (square this number to get the total tiles that the thread will work on)
int differenceBetween = workshare*cores;//Holds the square root of the total tiles operated on (if this varaible is 650 then the whole map is covered by the threads)
int extraTiles = MAP_SIZE - differenceBetween;//this variable holds how many tiles the last thread will operate on(if is equal to 0 then all threads have equal tile amounts)
int threadTiles;
for (short x = 0; 0 < std::thread::hardware_concurrency; x++)
{
if (x == (cores - 1))
{
threadTiles = x*workshare + extraTiles;
}
else
threadTiles = x*workshare;

if (x == 1)
threads.emplace_back(&drawTiles, this, layer, 0, 0, 324, 324, 10, 10, x - 1);
if (x == 2)
threads.emplace_back(&drawTiles, this, layer, 325, 325, 650, 650, 10, 10, x - 1);
}*/
return true;
}

bool MapManager::tilesetParse(const char * filename)
{


return true;
}

bool MapManager::parse(std::string filename, Tmx::MapTile gamemap[650][650])
{
int ab = 0;
map.ParseFile(filename);
if (map.HasError())
return false;
groundLevel = new Tmx::TileLayer(&map);


groundLevel = map.GetTileLayer(0);

for (int x = 0; x < groundLevel->GetWidth(); x++)
{
for (int y = 0; y < groundLevel->GetHeight(); y++)
{
gamemap[x][y] = groundLevel->GetTile(x,y);

}
}

}


MapManager::~MapManager()
{
}

void drawTiles(MapManager *map,Tile layer[650][650], int startGridX, int startGridY, int endGridX, int endGridY, int centX, int centY,int core=0)
{
//map->mapGraphics->spriteBegin(map->mapGraphics->mtSprites[core]);
for (int x = startGridX; x<endGridX; x++)
{
//std::cout << x;
for (int y = startGridY; y < endGridY; y++)
{
//std::cout << y;
if (x == centX&&y == centY)
layer[x][y].drawMT(core,graphicsNS::YELLOW);
else layer[x][y].drawMT(core);

}
}
//map->mapGraphics->spriteEnd(map->mapGraphics->mtSprites[core]);
}

modified image.cpp:
void Image::drawMT(int core, COLOR_ARGB color)
{
if (!visible || graphics == NULL)
return;
// get fresh texture incase onReset() was called
spriteData.texture = textureManager->getTexture();
if (color == graphicsNS::FILTER) // if draw with filter
graphics->drawMTSprite(spriteData, colorFilter,core); // use colorFilter
else
graphics->drawMTSprite(spriteData, color,core); // use color as filter
}

graphics.cpp
void Graphics::drawMTSprite(const SpriteData &spriteData, COLOR_ARGB color,int core)//multithreaded drawing for the map
{

if (spriteData.texture == NULL) // if no texture
return;

// Find center of sprite
D3DXVECTOR2 spriteCenter = D3DXVECTOR2((float)(spriteData.width / 2 * spriteData.scale),
(float)(spriteData.height / 2 * spriteData.scale));
// Screen position of the sprite
D3DXVECTOR2 translate = D3DXVECTOR2((float)spriteData.x, (float)spriteData.y);
// Scaling X,Y
D3DXVECTOR2 scaling(spriteData.scale, spriteData.scale);
if (spriteData.flipHorizontal) // if flip horizontal
{
scaling.x *= -1; // negative X scale to flip
// Get center of flipped image.
spriteCenter.x -= (float)(spriteData.width*spriteData.scale);
// Flip occurs around left edge, translate right to put
// Flipped image in same location as original.
translate.x += (float)(spriteData.width*spriteData.scale);
}
if (spriteData.flipVertical) // if flip vertical
{
scaling.y *= -1; // negative Y scale to flip
// Get center of flipped image
spriteCenter.y -= (float)(spriteData.height*spriteData.scale);
// Flip occurs around top edge, translate down to put
// Flipped image in same location as original.
translate.y += (float)(spriteData.height*spriteData.scale);
}
// Create a matrix to rotate, scale and position our sprite
D3DXMATRIX matrix;
D3DXMatrixTransformation2D(
&matrix, // the matrix
NULL, // keep origin at top left when scaling
0.0f, // no scaling rotation
&scaling, // scale amount
&spriteCenter, // rotation center
(float)(spriteData.angle), // rotation angle
&translate); // X,Y location

// Tell the sprite about the matrix "Hello Neo"
mtSprites[core]->SetTransform(&matrix);

// Draw the sprite
mtSprites[core]->Draw(spriteData.texture, &spriteData.rect, NULL, NULL, color);
}


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 1 post ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Jump to:  
cron
Powered by phpBB® Forum Software © phpBB Group