Fastest possible single pixel drawing?

Started by
7 comments, last by scope 14 years, 2 months ago
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?
Advertisement
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.x = (float)x1;      lineList.y = (float)y1;      lineList.z = 1.0f;      lineList.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,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
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 bufferwhile (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.
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]
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
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.
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]
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.
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 pixelstex->UnlockRect(0);


It's very fast.

This topic is closed to new replies.

Advertisement