Sign in to follow this  
cozzie

check if ID3DXEFFECT has begun? (mEffect->Begin())

Recommended Posts

cozzie    5029

Hi,

I was wondering, is there a way to check if a ID3DXEFFECT has begun / if ->Begin() was executed without a call to ->End().

In the member functions of ID3DXEFFECT / ID3DXBASEEFFECT I didn't find any member function who returns this.

 

Ofcourse I want to prevent creating a bool for tracking this myself.

Share this post


Link to post
Share on other sites
Tom KQT    1704

Is your rendering code really so complicated, that you need to have a way how to remember this? What's the reason why you have so many lines of code between Begin() and End(), or even have them (probably) in completely different places (methods)?

Try to inspect your code ;)

 

Anyway, I don't know of any way other than storing it in your own bool variable.

Share this post


Link to post
Share on other sites
cozzie    5029

Thanks. It's not very complicated, the reason is that I can clean my code up quitely if I know if an efffect has begun or not.

I'll add a bool in my shader class to check if it's active (begin called without end).

 

The initial bindings were to prevent that I end an effect (1st iteration of the loop) which isn't active yet.

Here's the code:

bool CD3d::RenderScene(const char *pTechnique, const CD3dcam &pCam, std::vector<Q_RENDERABLE>& pRenderBucket)
{
	// retrieve the 1st element from bucket and do all bindings
	Q_RENDERABLE *last = &pRenderBucket[0];

	ShaderSetTechnique(last->Effect, pTechnique);
	mD3dscene->mShaders[last->Effect].Begin();
	mD3dscene->ShaderSelectMaterial(last->Material, last->Effect);
	mD3dscene->mMeshes[last->Mesh].SetBuffers();
	mD3dscene->ShaderSetWorld(last.Instance, lastId);
	
	bool bucketDone = false;
	int renderable = 0;

	while(!bucketDone)
	{
		auto next = &pRenderBucket[renderable];
		if(next->Effect != last->Effect) 
		{
			mD3dscene->mShaders[last->Effect].End();

			mD3dscene->mShaders[next->Effect].SetTechnique(pTechnique);
			mD3dscene->mShaders[next->Effect].Begin();
		}

		for(unsigned int p=0;p<mD3dscene->mShaders[next->Effect].GetNumPasses();++p)
		{
			mD3dscene->mShaders[next->Effect].BeginPass(p);

			if(next->Material	!= last->Material)	mD3dscene->ShaderSelectMaterial(next->Material, last->Effect);
			if(next->Mesh		!= last->Mesh)		mD3dscene->mMeshes[next->Mesh].SetBuffers();

			mD3dscene->ShaderSetWorld(next.Instance, next.Id);
			mD3dscene->mShaders[next->Effect].Commit();

			mD3dscene->mMeshes[next->Mesh].RenderSubMesh(next->Id, LIST);	// does the draw call
			mD3dscene->mShaders[next->Effect].EndPass();
		}
		last = &pRenderBucket[renderable];	
		++renderable;

		if(renderable == (int)pRenderBucket.size()) bucketDone = true;
	}
	return true;
}

Gonna be working on it now to 'clean it up'.

I'll probably set the initial value of last to a null pointer and then add || NULL to the comparions of last and next.

 

If you have any ideas how to do this otherwise/ properly, all input welcome

Share this post


Link to post
Share on other sites
L. Spiro    25619
You do not have an equal pairing of Begin() and End() here.  You never End() the last one you Begin()’ed.
 
Why don’t you use RAII?

 
struct EFFECT_RAII {
    EFFECT_RAII( ID3DXEffect * _peEffect ) {
        peEffect = _peEffect;
        if ( peEffect ) { peEffect->Begin(); }
    }
    ~EFFECT_RAII() {
        if ( peEffect ) { peEffect->End(); }
    }


    EFFECT_RAII & operator = ( const ID3DXEffect * _peEffect ) {
        if ( peEffect != _peEffect ) {
            if ( peEffect ) { peEffect->End(); }
            peEffect = _peEffect;
            if ( peEffect ) { peEffect->Begin(); }
        }
        return (*this);
    }
protected :
    ID3DXEffect * peEffect;
};






bool CD3d::RenderScene(const char *pTechnique, const CD3dcam &pCam, std::vector<Q_RENDERABLE>& pRenderBucket)
{
	// retrieve the 1st element from bucket and do all bindings
	Q_RENDERABLE *last = &pRenderBucket[0];

	ShaderSetTechnique(last->Effect, pTechnique);
        EFFECT_RAII erEffect( &mD3dscene->mShaders[last->Effect] );
	//mD3dscene->mShaders[last->Effect].Begin();
	mD3dscene->ShaderSelectMaterial(last->Material, last->Effect);
	mD3dscene->mMeshes[last->Mesh].SetBuffers();
	mD3dscene->ShaderSetWorld(last.Instance, lastId);
	
	bool bucketDone = false;
	int renderable = 0;

	while(!bucketDone)
	{
		auto next = &pRenderBucket[renderable];
		if(next->Effect != last->Effect) 
		{
			//mD3dscene->mShaders[last->Effect].End();

			mD3dscene->mShaders[next->Effect].SetTechnique(pTechnique);
                        erEffect = &mD3dscene->mShaders[next->Effect];
			//mD3dscene->mShaders[next->Effect].Begin();
		}

		for(unsigned int p=0;p<mD3dscene->mShaders[next->Effect].GetNumPasses();++p)
		{
			mD3dscene->mShaders[next->Effect].BeginPass(p);

			if(next->Material	!= last->Material)	mD3dscene->ShaderSelectMaterial(next->Material, last->Effect);
			if(next->Mesh		!= last->Mesh)		mD3dscene->mMeshes[next->Mesh].SetBuffers();

			mD3dscene->ShaderSetWorld(next.Instance, next.Id);
			mD3dscene->mShaders[next->Effect].Commit();

			mD3dscene->mMeshes[next->Mesh].RenderSubMesh(next->Id, LIST);	// does the draw call
			mD3dscene->mShaders[next->Effect].EndPass();
		}
		last = &pRenderBucket[renderable];	
		++renderable;

		if(renderable == (int)pRenderBucket.size()) bucketDone = true;
	}
	return true;
        //~erEffect == LASTSETEFFECT.End() and Begin() and End() are now properly paired.
}
 
You should be using RAII any time you have to pair calls to an object, not just for Begin()/End() but for BeginPass() and EndPass() as well.
 
 
L. Spiro Edited by L. Spiro

Share this post


Link to post
Share on other sites
cozzie    5029

Thanks, I think I get it.

The ->End() is Always called when begin was called, because the destructor will be called at the end of function.

- can I do exactly the same with the passes that begin/end?
 

Meanwhile I've been playing around a bit, and the function is now as below (without the EFFECT_RAII).

(by the way, what does RAII stand for?)

/***************** TO DO : ERROR HANDLING WHEN DOING BINDINGS !!!!! ******************/

bool CD3d::RenderScene(const char *pTechnique, const CD3dcam &pCam, std::vector<Q_RENDERABLE>& pRenderBucket)
{
	// retrieve the 1st element from bucket and do all bindings
	Q_RENDERABLE *last = NULL; //&pRenderBucket[0];
	
	size_t bucketSize = pRenderBucket.size();

	for(size_t renderable = 0;renderable<bucketSize;++renderable)
	{
		auto next = &pRenderBucket[renderable];
		if(next->Effect != last->Effect) 
		{
			if(!last) mD3dscene->mShaders[last->Effect].End();

			mD3dscene->mShaders[next->Effect].SetTechnique(pTechnique);
			mD3dscene->mShaders[next->Effect].Begin();
		}

		for(unsigned int p=0;p<mD3dscene->mShaders[next->Effect].GetNumPasses();++p)
		{
			mD3dscene->mShaders[next->Effect].BeginPass(p);

			if(next->Material	!= last->Material)	mD3dscene->ShaderSelectMaterial(next->Material, last->Effect);
			if(next->Mesh		!= last->Mesh)		mD3dscene->mMeshes[next->Mesh].SetBuffers();

			mD3dscene->ShaderSetWorld(next.Instance, next.Id);
			mD3dscene->mShaders[next->Effect].Commit();

			mD3dscene->mMeshes[next->Mesh].RenderSubMesh(next->Id, LIST);	// does the draw call
			mD3dscene->mShaders[next->Effect].EndPass();
		}
		last = &pRenderBucket[renderable];	
	}
	mD3dscene->mShaders[bucketSize-1].End();
	return true;
}

I tried to modify this version using RAII, but I got stuck because I have no initial bindings anymore.

Meaning that when I create the erEffect in the scope of the whole function, I have to create it for last->effect, which is a null pointer at that moment.

Also then I try this:

EFFECT_RAII erEffect(&mD3dscene->mShaders[last->Effect].mEffect);

I get an error "no instance of constructor ... matches the argument" list.

mEffect is a CComPtr<ID3DXEffect>. When I remove the reference (&), the error is gone, probably because it already is a pointer.

 

Note; I will also add the number of passes to the constructor/ & operator, because this is needed for the 'begin' call.

Edited by cozzie

Share this post


Link to post
Share on other sites
L. Spiro    25619

can I do exactly the same with the passes that begin/end?

Can and should.
 

by the way, what does RAII stand for?

Resource Acquisition Is Initialization

I have to create it for last->effect, which is a null pointer at that moment.

My code allows NULL to be passed.

When I remove the reference (&), the error is gone, probably because it already is a pointer.

Whatever it takes to pass it as a pointer.

if(!last) mD3dscene->mShaders[last->Effect].End();

This always crashes.


L. Spiro

Share this post


Link to post
Share on other sites
cozzie    5029

Thanks, this helps :)

I'm currently trying it out, only last thing I'm struggling with is that I also need to pass numPasses which is needed for the Begin call of the effect

 

For the constructor this works already, but for the & operator I can't add another parameter.
I think this means I have to call the constructor when I switch to another effect and then pass numPasses

struct EFFECT_RAII 
{
    EFFECT_RAII(ID3DXEffect * _peEffect, unsigned int *pNumPasses) 
	{
        peEffect = _peEffect;
		if(peEffect) peEffect->Begin(pNumPasses, D3DXFX_DONOTSAVESTATE); 
    }
    
	~EFFECT_RAII() 
	{
        if(peEffect) peEffect->End();
    }


    EFFECT_RAII & operator = (const ID3DXEffect * _peEffect) 
	{
        if(peEffect != _peEffect ) 
		{
            if(peEffect) peEffect->End();
            peEffect = const_cast<ID3DXEffect*>(peEffect);
            if(peEffect) peEffect->Begin(0, D3DXFX_DONOTSAVESTATE);	// 0 should be numpasses??
        }
        return (*this);
    }

protected :
    ID3DXEffect *peEffect;
};


// the draw function

	EFFECT_RAII erEffect(mD3dscene->mShaders[last->Effect].mEffect, &mD3dscene->mShaders[last->Effect].mNumPasses);

	for(size_t renderable=0;renderable<bucketSize;++renderable)
	{
		auto next = &pRenderBucket[renderable];
		if(next->Effect != last->Effect) 
		{
			erEffect = mD3dscene->mShaders[last->Effect].mEffect;

Share this post


Link to post
Share on other sites
L. Spiro    25619
struct EFFECT_RAII {
    EFFECT_RAII( ID3DXEffect * _peEffect, UINT * _puiPasses ) {
        peEffect = _peEffect;
        if ( peEffect ) { peEffect->Begin( _puiPasses, D3DXFX_DONOTSAVESTATE ); }
    }
    ~EFFECT_RAII() {
        if ( peEffect ) { peEffect->End(); }
    }


    EFFECT_RAII & SetEffect( const ID3DXEffect * _peEffect, UINT * _puiPasses ) {
        if ( peEffect != _peEffect ) {
            if ( peEffect ) { peEffect->End(); }
            peEffect = _peEffect;
            if ( peEffect ) { peEffect->Begin( _puiPasses, D3DXFX_DONOTSAVESTATE ); }
        }
        return (*this);
    }
protected :
    ID3DXEffect * peEffect;
};

Call SetEffect() instead of using the assignment operator.


L. Spiro

Share this post


Link to post
Share on other sites
cozzie    5029
Thanks, got it.
One thing I ran into (and solved) whas that assigning const _peEffect to peEffect is a const/non const assignment, I solved this by putting a const cast before the _peEffect, to make it non-const

Share this post


Link to post
Share on other sites
cozzie    5029

I think I've got it completely now.

My GetNumPasses returns an unsigned int instead of unsigned int*, I'll dig into maybe fixing this 'cleaner' instead of the cast (UINT*).

bool CD3d::RenderScene(const char *pTechnique, const CD3dcam &pCam, std::vector<Q_RENDERABLE>& pRenderBucket)
{
	// retrieve the 1st element from bucket and do all bindings
	Q_RENDERABLE *last = NULL; //&pRenderBucket[0];
	
	EFFECT_RAII erEffect(mD3dscene->mShaders[last->Effect].mEffect, (UINT*)mD3dscene->mShaders[last->Effect].GetNumPasses());
	size_t bucketSize = pRenderBucket.size();

	for(size_t renderable=0;renderable<bucketSize;++renderable)
	{
		auto next = &pRenderBucket[renderable];
		if(next->Effect != last->Effect) 
		{
			erEffect.SetEffect(mD3dscene->mShaders[next->Effect].mEffect, (UINT*)mD3dscene->mShaders[next->Effect].GetNumPasses());
			mD3dscene->mShaders[next->Effect].SetTechnique(pTechnique);
		}

		for(unsigned int p=0;p<mD3dscene->mShaders[next->Effect].GetNumPasses();++p)
		{
			mD3dscene->mShaders[next->Effect].BeginPass(p);

			if(next->Material	!= last->Material)	mD3dscene->ShaderSelectMaterial(next->Material, last->Effect);
			if(next->Mesh		!= last->Mesh)		mD3dscene->mMeshes[next->Mesh].SetBuffers();

			mD3dscene->ShaderSetWorld(next.Instance, next.Id);
			mD3dscene->mShaders[next->Effect].Commit();

			mD3dscene->mMeshes[next->Mesh].RenderSubMesh(next->Id, LIST);	// does the draw call
			mD3dscene->mShaders[next->Effect].EndPass();
		}
		last = &pRenderBucket[renderable];	
	}
	return true;
}

Thanks for the help, next step; filling the buckets :)

 

I have to add the RAII for the passes to, but I have to think about that a little longer. Not sure how to do that, because one of the parameters will the the pass number itself and there can be more passes for 1 effect. Honestly I'm not sure if I need to do it, because I Always have a pair of Begin/End in the iterations of the loop. 

I've come so far:

struct PASS_RAII 
{
    // constructor
	PASS_RAII(ID3DXEffect *_peEffect, UINT pPass) 
	{
        peEffect = _peEffect;
		if(peEffect) peEffect->BeginPass(pPass);
    }
    
	// destructor, ends the effect it not null
	~PASS_RAII() 
	{
        if(peEffect) peEffect->EndPass();
    }

	// update to the next effect, end current if not null
	PASS_RAII & SetPass(ID3DXEffect *_peEffect, UINT pPass)
	{
		if(peEffect != _peEffect)
		{
			if(peEffect) peEffect->EndPass();
			peEffect = _peEffect;
			if(peEffect) peEffect->BeginPass(pPass);
		}
		return (*this);
	}

protected:
    ID3DXEffect *peEffect;
};

Share this post


Link to post
Share on other sites
L. Spiro    25619

Honestly I'm not sure if I need to do it, because I Always have a pair of Begin/End in the iterations of the loop.

Safety measures are never “needed”. They are always a good idea.
Using RAII this way is also the only safe way to handle exceptions while ensuring a paired BeginPass()/EndPass().
 

if(peEffect != _peEffect)

This should be:

if(peEffect != _peEffect && uiPass != pPass)

And you need to add uiPass to the structure as a member and handle it correctly.


L. Spiro Edited by L. Spiro

Share this post


Link to post
Share on other sites
cozzie    5029

Thanks, got it. Was working it at the moment, to fixed immediately (I think smile.png)

Also fixed the UINT* for saving the number of passes returned from effect Begin().

 

The draw function is not only more safe, but also getting smaller/ better readable.

struct PASS_RAII 
{
    // constructor
	PASS_RAII(ID3DXEffect *_peEffect, UINT pPass) 
	{
        peEffect = _peEffect;
		uiPass = pPass;
		if(peEffect) peEffect->BeginPass(uiPass);
    }
    
	// destructor, ends the effect it not null
	~PASS_RAII() 
	{
        if(peEffect) peEffect->EndPass();
    }

	// update to the next effect, end current if not null
	PASS_RAII & SetPass(ID3DXEffect *_peEffect, UINT pPass)
	{
		if(peEffect != _peEffect && uiPass != pPass)
		{
			if(peEffect) peEffect->EndPass();
			peEffect = _peEffect;
			uiPass = pPass;
			if(peEffect) peEffect->BeginPass(uiPass);
		}
		return (*this);
	}

protected:
    ID3DXEffect		*peEffect;
	unsigned int	uiPass;
};

// the draw function

bool CD3d::RenderScene(const char *pTechnique, const CD3dcam &pCam, std::vector<Q_RENDERABLE>& pRenderBucket)
{
	// retrieve the 1st element from bucket and do all bindings
	Q_RENDERABLE *last = NULL;
	
	EFFECT_RAII erEffect(mD3dscene->mShaders[last->Effect].mEffect, mD3dscene->mShaders[last->Effect].GetNumPassesPtr());
	PASS_RAII erPass(mD3dscene->mShaders[last->Effect].mEffect, 0);

	size_t bucketSize = pRenderBucket.size();

	for(size_t renderable=0;renderable<bucketSize;++renderable)
	{
		auto next = &pRenderBucket[renderable];
		if(next->Effect != last->Effect) 
		{
			erEffect.SetEffect(mD3dscene->mShaders[next->Effect].mEffect, mD3dscene->mShaders[next->Effect].GetNumPassesPtr());
			mD3dscene->mShaders[next->Effect].SetTechnique(pTechnique);
		}

		for(unsigned int p=0;p<mD3dscene->mShaders[next->Effect].GetNumPasses();++p)
		{
			erPass.SetPass(mD3dscene->mShaders[next->Effect].mEffect, p);

			if(next->Material	!= last->Material)	mD3dscene->ShaderSelectMaterial(next->Material, last->Effect);
			if(next->Mesh		!= last->Mesh)		mD3dscene->mMeshes[next->Mesh].SetBuffers();

			mD3dscene->ShaderSetWorld(next.Instance, next.Id);
			mD3dscene->mShaders[next->Effect].Commit();
			mD3dscene->mMeshes[next->Mesh].RenderSubMesh(next->Id, LIST);	// does the draw call
		}
		last = &pRenderBucket[renderable];	
	}
	return true;
}

I only have to do some naming changes in the 2 RAII structs, to align them with my coding style/ base (i.e. m.... for members of the struct etc.).

Edited by cozzie

Share this post


Link to post
Share on other sites
L. Spiro    25619
Move erPass to inside the second for-loop.
 

		for(unsigned int p=0;pmShaders[next->Effect].GetNumPasses();++p)
		{
			PASS_RAII erPass(mD3dscene->mShaders[next->Effect].mEffect, p);

			if(next->Material != last->Material) mD3dscene->ShaderSelectMaterial(next->Material, last->Effect);
			if(next->Mesh != last->Mesh) mD3dscene->mMeshes[next->Mesh].SetBuffers();

			mD3dscene->ShaderSetWorld(next.Instance, next.Id);
			mD3dscene->mShaders[next->Effect].Commit();
			mD3dscene->mMeshes[next->Mesh].RenderSubMesh(next->Id, LIST); // does the draw call
		}

 

L. Spiro Edited by L. Spiro

Share this post


Link to post
Share on other sites
cozzie    5029

Thanks, I was actually debugging at the moment to figure that out :)

It's all working with a test scene I just ran.

 

Maybe a stupid question; when I create a new PASS_RAII for every pass, is it destructed everytime a iteration of the passes loop is finished?

If not, then probably on the stack 'all' PASS_RAII's are destructed after the loop is finished.

Share this post


Link to post
Share on other sites
cozzie    5029
Thanks. That I've solved this means I can make a big improvement on my rendering, because I'm switching from rendering "whole mesh instances" to renderables. Meaning having up to 8 lights per renderable versus per mesh instance. No more disabling lights anymore depending on camera heading and position :)

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