Sign in to follow this  
Element of Power

Fastest possible single pixel drawing?

Recommended Posts

I'm trying to make a sand game, such as PyroSand, with the Dark GDK (which uses direct x), but I'm running into a major problem: pixel drawing is too slow! I don't know much about directly accessing pixels in memory nor how to control them once they've been accessed, so I searched the internet and found this code:
#include "DarkSDK.h"
#include "d3d9.h"


#define D3D_FVF_2D_VERTEX (D3DFVF_XYZRHW | D3DFVF_DIFFUSE)

struct TransformedVertex	// D3D_FVF_2D_VERTEX
{
	float x, y, z, w;		// Position of vertex in 3D space
    DWORD color;			// Color of vertex
};


IDirect3DDevice9*			g_pd3dDevice = NULL;

void InitD3DFunc() {
	g_pd3dDevice = dbGetDirect3DDevice();
}

void d3d_dot (int x1,int y1, int r, int g, int b, int a) {
	TransformedVertex *pVertices = NULL;
	TransformedVertex g_lineList[] = 
	{
		{-1.0f,  0.0f, 0.0f, 1.0f, D3DCOLOR_ARGB( 255, 255, 0, 255 ) }, // Line #1
	};

	g_lineList[0].x=(float)x1;
	g_lineList[0].y=(float)y1;
	g_lineList[0].z=1.0f;
	g_lineList[0].color=D3DCOLOR_ARGB ( a, r, g, b );



	g_pd3dDevice->SetTexture(0,NULL);
	g_pd3dDevice->SetTexture(1,NULL);
	g_pd3dDevice->SetVertexShader(NULL);
	g_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
	g_pd3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
	g_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
	g_pd3dDevice->SetFVF( D3D_FVF_2D_VERTEX );
    g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);

	g_pd3dDevice->DrawPrimitiveUP(D3DPT_POINTLIST,1,g_lineList,sizeof(TransformedVertex));


}
The problem is, it's way too slow. The framerate decreases to 40 FPS(from 60 FPS) when I draw just 100x100 pixels every frame, with zero extra calculations. Could anyone suggest a quicker function?

Share this post


Link to post
Share on other sites
emiel1    166
Drawing one pixel at the time is time-consuming. You should try to draw an array of pixels at once:

void d3d_dot (int *x1, int *y1, int *r, int *g, int *b, int *a, int count)
{
TransformedVertex lineList[] = new TransformedVertex[count];

for (unsigned int i = 0; i < count; ++i)
{
lineList[i].x = (float)x1[i];
lineList[i].y = (float)y1[i];
lineList[i].z = 1.0f;
lineList[i].color = D3DCOLOR_ARGB (a[i], r[i], g[i], b[i]);
}

g_pd3dDevice->SetTexture(0,NULL);
g_pd3dDevice->SetTexture(1,NULL);
g_pd3dDevice->SetVertexShader(NULL);
g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
g_pd3dDevice->SetFVF(D3D_FVF_2D_VERTEX);
g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);

g_pd3dDevice->DrawPrimitiveUP(D3DPT_POINTLIST,count,lineList,sizeof(TransformedVertex));

delete[] lineList;
}



This just needs reordering (I assume that you don't store all the x-, y-coordinates, red-, green-, blue- and alpha-components of the pixels in an array), but you get the idea.

This eliminates the multiple draw-calls, that take most of your time. This function is explained here.

Emiel1

Share this post


Link to post
Share on other sites
flammable    280
I don't have much experience with directx (can't be that much diferent from opengl) so I don't know all options there are but one I came up with when I was reading your post is:
every frame:
Store the color of every pixel in a buffer in the RAM. Then send that buffer to the GPU as texture and draw a screen aligned quad with that texture.
I think this is a quite lot more complicated but also faster.


struct RGB; // a struct with 3 bytes representing a color.
RGB Buffer[100][100]; // the buffer
while (true) // main loop
{
setBlack(Buffer)
RGB color = {220, 123, 0}
Buffer[22][44] = color;

convertToTextureAndSendItToTheGPU(Buffer) // I can't help you with this part.
}



If you're using cpp it's better to make a somewhat more OO-design because the example I gave is quite unmanageable at larger scale.

edit:
I think you can beter use the method of emiel1 because mine is a little overkill here.

Share this post


Link to post
Share on other sites
Thanks to both of you for the quick replies!

Flammable, I have no idea about the "convertToTextureAndSendItToTheGPU(Buffer)", so your idea won't do me any good.

Emiel, I'll try it. I'll have to rework some stuff in my code though, so it might take a while...

EDIT: Ok, that was quicker than expected. I've managed to make some code. Is this as optimised as possible?

#include "DarkSDK.h"
#include "d3d9.h"


#define D3D_FVF_2D_VERTEX (D3DFVF_XYZRHW | D3DFVF_DIFFUSE)

struct TransformedVertex // D3D_FVF_2D_VERTEX
{
float x, y, z, w; // Position of vertex in 3D space
DWORD color; // Color of vertex
};


IDirect3DDevice9* g_pd3dDevice = NULL;

TransformedVertex g_d3dLineList [0xFFFFFF];
int g_d3dCount = 0;

void InitD3DFunc()
{
g_pd3dDevice = dbGetDirect3DDevice();
}

void d3d_dot (int x1, int y1, int r, int g, int b, int a)
{
g_d3dLineList[g_d3dCount].x = (float)x1;
g_d3dLineList[g_d3dCount].y = (float)y1;
g_d3dLineList[g_d3dCount].z = 1.0f;
g_d3dLineList[g_d3dCount].color = D3DCOLOR_ARGB (a, r, g, b);
g_d3dCount++;
}

void d3d_drawDots()
{
g_pd3dDevice->SetTexture(0,NULL);
g_pd3dDevice->SetTexture(1,NULL);
g_pd3dDevice->SetVertexShader(NULL);
g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
g_pd3dDevice->SetFVF(D3D_FVF_2D_VERTEX);
g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);

g_pd3dDevice->DrawPrimitiveUP(D3DPT_POINTLIST,g_d3dCount,g_d3dLineList,sizeof(TransformedVertex));

g_d3dCount = 0;
}


EDIT: Wow. I just tested it and it takes 400x400 pixels being updated every second at more than 40 FPS! that's more than 16 times faster than before, but is it fast enough for a sand game with all the physics calculations?

[Edited by - Element of Power on February 14, 2010 9:42:50 AM]

Share this post


Link to post
Share on other sites
emiel1    166
Quote:
Original post by Element of Power
Wow. I just tested it and it takes 400x400 pixels being updated every second at more than 40 FPS! that's more than 16 times faster than before, but is it fast enough for a sand game with all the physics calculations?
It should be fast enough, as long as you don't have too many pixels on top of each other. This shouldn't happen if you do the physics correctly.
Quote:
Original post by Element of Power
Is this as optimised as possible?
It is optimised as far as I know, but using that much global variables isn't good form. Try this to make it more reusable:
void d3d_drawDots(TransformedVertex *dots, int count);
Emiel1

Share this post


Link to post
Share on other sites
Erik Rufelt    5901
Quote:
I just tested it and it takes 400x400 pixels being updated every second at more than 40 FPS!

Do you compile in release mode?
If you're running in debug mode it might be a lot slower than necessary.

Share this post


Link to post
Share on other sites
I didn't even realise this debug/release mode compile thing existed. Thanks for the tip - I just gained ~ 3 FPS.

EDIT: I just saw Emiel's edit.
Quote:
It is optimised as far as I know, but using that much global variables isn't good form. Try this to make it more reusable:

void d3d_drawDots(TransformedVertex *dots, int count);
It's reusable enough how it is. Anyway, I can't think what I'd ever use this for besides a sand game...

[Edited by - Element of Power on February 14, 2010 10:30:29 AM]

Share this post


Link to post
Share on other sites
Erik Rufelt    5901
If every pixel you ever draw is drawn manually from the CPU, then it would probably be faster to create a texture in system memory, lock it and update the pixels, and then send it to the GPU with UpdateTexture.

Share this post


Link to post
Share on other sites
scope    108
yeah just use a dynamic texture and lock it



device->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, format, D3DPOOL_DEFAULT, &tex, NULL);

D3DLOCKED_RECT lr;

tex->LockRect(0, &lr, NULL, 0);

DWORD* bits = (DWORD *)lr.pBits; // here's your pointer to the pixels

tex->UnlockRect(0);





It's very fast.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this