Sign in to follow this  
leonard2012

Poor rendering performance when drawing simple 2D shapes

Recommended Posts

leonard2012    167
Hi all,
I am new to Direct3D programming and now an issue has driven me nuts. I am writing an graphics editing desktop application that use D3D for rendering graphical shapes (lines, polygons etc.). My application window is only updated when necessary (i.e. upon receiving a WM_PAINT message), and it is not updated periodically by a timer. It's not a game after all.
In the code I attatched at the end of this post, I want to draw world map that consists of multiple (400 or so) linestrips/loops in the client window. The function will be called for each linestrip/loop. The problem with my code is that the rendering performance is so poor so that the rendering process looks like animation, one linestrip/loop after another. It is noticably slower than GDI rendering.
Could someone kindly point out problems in my code? Thanks in advance.

[CODE]
void mgral_directx::draw(int obj_type, mpoint *points, int count)
{
static ID3D10Buffer* iD3dVB=NULL;
int numVertices;
std::vector<Vertex> vertices;
numVertices = count;
// Assign values to vertex buffer
vertices.resize(numVertices);
for ( int i = 0; i < count; i++ ) {
vertices[i].pos = D3DXVECTOR3(points[i].x, points[i].y, points[i].z);
vertices[i].color = D3DXCOLOR(penColor_);
}
if ( obj_type == MGRAL_LINE_LOOP ) {
vertices[numVertices-1].pos = D3DXVECTOR3(points[0].x, points[0].y, points[0].z);
vertices[numVertices-1].color = D3DXCOLOR(penColor_);
}

// Create vertex buffer
if ( iD3dVB==NULL ) {
D3D10_BUFFER_DESC vbd;
vbd.Usage = D3D10_USAGE_IMMUTABLE;
vbd.ByteWidth = sizeof(Vertex) * numVertices;
vbd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
vbd.CPUAccessFlags = 0;
vbd.MiscFlags = 0;
D3D10_SUBRESOURCE_DATA vinitData;
vinitData.pSysMem = &vertices[0];
HR(iD3dDevice_->CreateBuffer(&vbd, &vinitData, &iD3dVB));
// Create effect
buildFX();
// Create input layout
buildVertexLayouts();
}
// Restore default states, input layout and primitive topology
// because iD3dxFont_->DrawText changes them. Note that we can
// restore the default states by passing null.
iD3dDevice_->OMSetDepthStencilState(0, 0);
float blendFactors[] = {0.0f, 0.0f, 0.0f, 0.0f};
iD3dDevice_->OMSetBlendState(0, blendFactors, 0xffffffff);
// Bind the input layout to the device
iD3dDevice_->IASetInputLayout(mVertexLayout);
// Specify the primitive topology
iD3dDevice_->IASetPrimitiveTopology(d3dPrimitiveTopologies[obj_type]);
// Bind vertex buffer to the input slot of the device
UINT stride = sizeof(Vertex);
UINT offset = 0;
iD3dDevice_->IASetVertexBuffers(0, 1, &iD3dVB, &stride, &offset);
// set constants
D3DXMATRIX mWVP = modelMatrix_*viewMatrix_*projectMatrix_;
mfxWVPVar->SetMatrix((float*)&mWVP);
// Draw the geometry objects
D3D10_TECHNIQUE_DESC techDesc;
mTech->GetDesc( &techDesc );
for(UINT p = 0; p < techDesc.Passes; ++p) {
mTech->GetPassByIndex( p )->Apply(0);
iD3dDevice_->Draw(numVertices, 0);
}
// Remember to add the line below after all drawing code.
iDxgiSwapChain_->Present(0, 0);
ReleaseCOM(iD3dVB);
}
void mgral_directx::buildFX()
{
DWORD shaderFlags = D3D10_SHADER_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
shaderFlags |= D3D10_SHADER_DEBUG;
shaderFlags |= D3D10_SHADER_SKIP_OPTIMIZATION;
#endif

ID3D10Blob* compilationErrors = 0;
HRESULT hr = 0;
hr = D3DX10CreateEffectFromFile(TEXT("shader.fxo"), 0, 0,
"fx_4_0", shaderFlags, 0, iD3dDevice_, 0, 0, &mFX, &compilationErrors, 0);
if(FAILED(hr)) {
if( compilationErrors ) {
MessageBoxA(0, (char*)compilationErrors->GetBufferPointer(), 0, 0);
ReleaseCOM(compilationErrors);
}
DXTrace(__FILE__, (DWORD)__LINE__, hr, TEXT("D3DX10CreateEffectFromFile"), true);
}
mTech = mFX->GetTechniqueByName("ColorTech");
mfxWVPVar = mFX->GetVariableByName("gWVP")->AsMatrix();
}
void mgral_directx::buildVertexLayouts()
{
// Create the vertex input layout.
D3D10_INPUT_ELEMENT_DESC vertexDesc[] =
{
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0},
{"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0}
};
// Create the input layout
D3D10_PASS_DESC PassDesc;
mTech->GetPassByIndex(0)->GetDesc(&PassDesc);
HR(iD3dDevice_->CreateInputLayout(vertexDesc, 2, PassDesc.pIAInputSignature,
PassDesc.IAInputSignatureSize, &mVertexLayout));
}

[/CODE]

Share this post


Link to post
Share on other sites
Guns    150
Hidden
You are recreating vertex layout, vertex buffer and shader every time you draw. You should create resources only once, at the beginning of the program. If you want to draw different set of lines every frame, check dynamic buffers.

Share this post


Link to post
Guns    150
You are recreating your vertex layout, vertex buffer and shader every time you draw. You should create resources only once at the beginning of the program. If you want to draw different set of lines every frame, check dynamic buffers.

Share this post


Link to post
Share on other sites
leonard2012    167
Hi you two, thank you very much for your help. Now I rewrite my code to optionally use dynamic buffers or immutable buffers based on the definition of USE_DYNAMIC_BUFFER macro. But the performance is still poor. Could you please kindly point out the problems in my code?

[CODE]
#define USE_DYNAMIC_BUFFER 1
void mgral_directx::draw(int obj_type, mpoint *points, int count)
{
static ID3D10Buffer* iD3dVB=NULL;
int numVertices;
std::vector<Vertex> vertices;
numVertices = count;
// Assign values to vertex buffer
#ifndef USE_DYNAMIC_BUFFER
vertices.resize(numVertices);
for ( int i = 0; i < count; i++ ) {
vertices[i].pos = D3DXVECTOR3(points[i].x, points[i].y, points[i].z);
vertices[i].color = D3DXCOLOR(penColor_);
}
#endif

// Create vertex buffer
if ( iD3dVB==NULL ) {
D3D10_BUFFER_DESC vbd;
#ifndef USE_DYNAMIC_BUFFER
vbd.Usage = D3D10_USAGE_IMMUTABLE;
vbd.ByteWidth = sizeof(Vertex) * numVertices;
vbd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
vbd.CPUAccessFlags = 0;
vbd.MiscFlags = 0;
D3D10_SUBRESOURCE_DATA vinitData;
vinitData.pSysMem = &vertices[0];
HR(iD3dDevice_->CreateBuffer(&vbd, &vinitData, &iD3dVB));
#else
vbd.Usage = D3D10_USAGE_DYNAMIC;
vbd.ByteWidth = sizeof(Vertex) * numVertices;
vbd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
vbd.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
vbd.MiscFlags = 0;
HR(iD3dDevice_->CreateBuffer(&vbd, 0, &iD3dVB));
#endif
// Create effect
buildFX();
// Create input layout
buildVertexLayouts();
}
// Update vertex buffer
#ifdef USE_DYNAMIC_BUFFER
Vertex* v = 0;
HR(iD3dVB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&v ));
for ( int i = 0; i < count; i++ ) {
v[i].pos = D3DXVECTOR3(points[i].x, points[i].y, points[i].z);
v[i].color = D3DXCOLOR(penColor_);
}
iD3dVB->Unmap();
#endif
// Restore default states, input layout and primitive topology
// because iD3dxFont_->DrawText changes them. Note that we can
// restore the default states by passing null.
iD3dDevice_->OMSetDepthStencilState(0, 0);
float blendFactors[] = {0.0f, 0.0f, 0.0f, 0.0f};
iD3dDevice_->OMSetBlendState(0, blendFactors, 0xffffffff);
// Bind the input layout to the device
iD3dDevice_->IASetInputLayout(mVertexLayout);
// Specify the primitive topology
iD3dDevice_->IASetPrimitiveTopology(d3dPrimitiveTopologies[obj_type]);
// Bind vertex buffer to the input slot of the device
UINT stride = sizeof(Vertex);
UINT offset = 0;
iD3dDevice_->IASetVertexBuffers(0, 1, &iD3dVB, &stride, &offset);
// set constants
D3DXMATRIX mWVP = modelMatrix_*viewMatrix_*projectMatrix_;
mfxWVPVar->SetMatrix((float*)&mWVP);
// Draw the geometry objects
D3D10_TECHNIQUE_DESC techDesc;
mTech->GetDesc( &techDesc );
for(UINT p = 0; p < techDesc.Passes; ++p) {
mTech->GetPassByIndex( p )->Apply(0);
iD3dDevice_->Draw(numVertices, 0);
}
// Remember to add the line below after all drawing code.
iDxgiSwapChain_->Present(0, 0);
#ifndef USE_DYNAMIC_BUFFER
ReleaseCOM(iD3dVB);
#endif
}

[/CODE]

Share this post


Link to post
Share on other sites
LevyDee    376
[CODE]
// Create vertex buffer
if ( iD3dVB==NULL ) {
D3D10_BUFFER_DESC vbd;
#ifndef USE_DYNAMIC_BUFFER
vbd.Usage = D3D10_USAGE_IMMUTABLE;
vbd.ByteWidth = sizeof(Vertex) * numVertices;
vbd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
vbd.CPUAccessFlags = 0;
vbd.MiscFlags = 0;
D3D10_SUBRESOURCE_DATA vinitData;
vinitData.pSysMem = &vertices[0];
HR(iD3dDevice_->CreateBuffer(&vbd, &vinitData, &iD3dVB));
#else
vbd.Usage = D3D10_USAGE_DYNAMIC;
vbd.ByteWidth = sizeof(Vertex) * numVertices;
vbd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
vbd.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
vbd.MiscFlags = 0;
HR(iD3dDevice_->CreateBuffer(&vbd, 0, &iD3dVB));
#endif
// Create effect
buildFX();
// Create input layout
buildVertexLayouts();
}


[/CODE]


Do this once before you enter your draw loop. You are creating a new buffer, and a new effect for every single draw call. This is what is giving you bad performance

Share this post


Link to post
Share on other sites
leonard2012    167
[quote name='Slig Commando' timestamp='1333503793' post='4928066']
[CODE]
// Create vertex buffer
if ( iD3dVB==NULL ) {
D3D10_BUFFER_DESC vbd;
#ifndef USE_DYNAMIC_BUFFER
vbd.Usage = D3D10_USAGE_IMMUTABLE;
vbd.ByteWidth = sizeof(Vertex) * numVertices;
vbd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
vbd.CPUAccessFlags = 0;
vbd.MiscFlags = 0;
D3D10_SUBRESOURCE_DATA vinitData;
vinitData.pSysMem = &vertices[0];
HR(iD3dDevice_->CreateBuffer(&vbd, &vinitData, &iD3dVB));
#else
vbd.Usage = D3D10_USAGE_DYNAMIC;
vbd.ByteWidth = sizeof(Vertex) * numVertices;
vbd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
vbd.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
vbd.MiscFlags = 0;
HR(iD3dDevice_->CreateBuffer(&vbd, 0, &iD3dVB));
#endif
// Create effect
buildFX();
// Create input layout
buildVertexLayouts();
}


[/CODE]


Do this once before you enter your draw loop. You are creating a new buffer, and a new effect for every single draw call. This is what is giving you bad performance
[/quote]

Thank you for your further help, Slig!
Did you notice that the variable iD3dVB is declared as static and assigned to NULL initially and the statement
[color="#660066"]ReleaseCOM[/color][color="#666600"]([/color][color="#000000"]iD3dVB[/color][color="#666600"]);[/color]
won't be called when the macro USE_DYNAMIC_BUFFER is defined. So the code segment you referenced will be executed only once, i.e. the first time the function draw is called.

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