ShapeBase::prepRenderImage renders things twice?
by Eric Hartman · in Torque Game Engine · 05/02/2005 (11:44 am) · 7 replies
In Shapebase::prepRenderImage() we have some code that looks like this
Now it seems to me that if we have an object that isn't faded or cloaked and has a shadow or is translucent, this code will cause the item to be rendered twice.
Maybe I've done something wrong in my game, but I changed Shapebase::prepRenderImage() and the performance nearly doubled in character-heavy scenes.
Continued in reply....
if (mCloakLevel == 0.0f && mShapeInstance->hasSolid() && mFadeVal == 1.0f)
{
SceneRenderImage* image = new SceneRenderImage;
image->obj = this;
image->isTranslucent = false;
image->textureSortKey = mSkinHash ^ (U32)(dsize_t)(mDataBlock);
state->insertRenderImage(image);
}
if ((mCloakLevel != 0.0f || mFadeVal != 1.0f || mShapeInstance->hasTranslucency()) ||
(mMount.object == NULL && mGenerateShadow == true))
{
SceneRenderImage* image = new SceneRenderImage;
image->obj = this;
image->isTranslucent = true;
image->sortType = SceneRenderImage::Point;
image->textureSortKey = mSkinHash ^ (U32)(dsize_t)(mDataBlock);
state->setImageRefPoint(this, image);
state->insertRenderImage(image);
}Now it seems to me that if we have an object that isn't faded or cloaked and has a shadow or is translucent, this code will cause the item to be rendered twice.
Maybe I've done something wrong in my game, but I changed Shapebase::prepRenderImage() and the performance nearly doubled in character-heavy scenes.
Continued in reply....
About the author
#2
In Shapebase.cc
And in Player.cc
05/02/2005 (11:44 am)
Some cleanup is needed to get shadows working (changes are in bold):In Shapebase.cc
void ShapeBase::renderImage(SceneState* state, SceneRenderImage* image)
{
...
// Shadow...
if (mShapeInstance && mCloakLevel == 0.0 &&
mMount.object == NULL && mGenerateShadow == true [b]/*&&
image->isTranslucent == true*/[/b])
{
renderShadow(dist,fogAmount);
}
PROFILE_END();
}And in Player.cc
void Player::renderImage(SceneState* state, SceneRenderImage* image)
{
...
if (mShapeInstance && renderPlayer && mCloakLevel == 0.0 &&
mMount.object == NULL [b]/*&& image->isTranslucent == true*/[/b])
{
renderShadow(dist,fogAmount);
}
TSMesh::setOverrideFade( 1.0 );
PROFILE_END();
...
}
#3
I think you have a misunderstanding as to how SceneRenderImages work - since translucent and non translucent "images" are rendered seperately, it's necessary to register TWO images in the case of a translucent object, one to render the opaque bits and another to render the translucent bits afterwards. The original code is actually correct, and does not result in any extra drawing.
05/02/2005 (12:05 pm)
Hey Eric,I think you have a misunderstanding as to how SceneRenderImages work - since translucent and non translucent "images" are rendered seperately, it's necessary to register TWO images in the case of a translucent object, one to render the opaque bits and another to render the translucent bits afterwards. The original code is actually correct, and does not result in any extra drawing.
#4
05/02/2005 (1:06 pm)
..oops. I had commented out some rather important code in shapebase::renderObject() and that causedeverything to be rendered on the translucent image instead of just the translucent stuff.
#5
I thought of this thread while I was looking at a profile dump and am slightly puzzled - I dont think I am understanding the rendering code correctly (after spending quite a while trying to trace it throgh the code) so would you be kind enough to (briefly) explain why the player (and mounted object and shadow) seem to be rendering twice per frame and does it result in more drawing??
If I am reading this right (and please correct me if I am not), 67505 frames were rendered with 67505 calls to InteriorRenderObject (1 interior in this) but PlayerRenderPrimary, PlayerRenderShadow and PlayerRenderMounted are all called 135758 times??
I get the same result (twice as many calls to player than frame) in the stock demo.
There is only one player in the mission (albeit fairly hefty on the triangle count) and it only has one level of detail.
05/10/2005 (10:03 am)
@ Ben (or anyone who knows the rendering code well)I thought of this thread while I was looking at a profile dump and am slightly puzzled - I dont think I am understanding the rendering code correctly (after spending quite a while trying to trace it throgh the code) so would you be kind enough to (briefly) explain why the player (and mounted object and shadow) seem to be rendering twice per frame and does it result in more drawing??
If I am reading this right (and please correct me if I am not), 67505 frames were rendered with 67505 calls to InteriorRenderObject (1 interior in this) but PlayerRenderPrimary, PlayerRenderShadow and PlayerRenderMounted are all called 135758 times??
I get the same result (twice as many calls to player than frame) in the stock demo.
There is only one player in the mission (albeit fairly hefty on the triangle count) and it only has one level of detail.
Ordered by stack trace total time - % Time % NSTime Invoke # Name 0.315 -99.685 0 ROOT 100.000 7.669 319264 MainLoop 92.214 0.124 67505 ProcessTimeEvent 85.635 0.056 67505 RenderFrame 80.011 4.408 67505 CanvasRenderControls 73.968 0.984 67505 GameRenderWorld 72.984 0.224 67505 SceneGraphRender 70.820 0.256 67505 TraverseScene 70.490 3.266 764627 SceneStateRenderImage 38.284 2.259 271516 ShapeBaseRenderObject 18.062 0.204 135758 PlayerRenderPrimary 17.858 17.565 135758 TSShapeInstanceRender 0.286 0.286 135758 TSShapeInstanceMaterials 0.007 0.007 135758 TSShapeInstanceRenderBillboards 14.165 14.165 135758 PlayerRenderShadow 0.000 0.000 87 MemoryFree 0.000 0.000 96 MemoryAlloc 0.000 0.000 14 MemoryRealloc 0.000 0.000 8 MemoryAlloc 0.000 0.000 8 MemoryFree 3.753 0.261 135758 PlayerRenderMounted 3.492 3.137 135758 TSShapeInstanceRender 0.350 0.350 135758 TSShapeInstanceMaterials 0.004 0.004 135758 TSShapeInstanceRenderBillboards 0.045 0.027 271516 GetLightingAmbientColor 0.017 0.017 7109 ContainerCastRay 25.471 0.171 67505 InteriorRenderObject 23.616 0.126 67505 IRO_RenderSolids 23.490 0.019 67505 IRO_RenderARB_FC . . . snip
#6
05/10/2005 (1:21 pm)
That is perfectly right. Each render call is made once for opaque stuff, and again for translucent stuff. This is necessary so that everything gets sorted out properly.
#7
05/10/2005 (1:39 pm)
Thankyou.
Torque Owner Eric Hartman
//Badspot: redid this function to ensure everything only gets rendered once bool ShapeBase::prepRenderImage(SceneState* state, const U32 stateKey, const U32 startZone, const bool modifyBaseState) { AssertFatal(modifyBaseState == false, "Error, should never be called with this parameter set"); AssertFatal(startZone == 0xFFFFFFFF, "Error, startZone should indicate -1"); if (isLastState(state, stateKey)) return false; setLastState(state, stateKey); if( ( getDamageState() == Destroyed ) && ( !mDataBlock->renderWhenDestroyed ) ) return false; // Select detail levels on mounted items // but... always draw the control object's mounted images // in high detail (I can't believe I'm commenting this hack :) F32 saveError = TSShapeInstance::smScreenError; GameConnection *con = GameConnection::getServerConnection(); bool fogExemption = false; ShapeBase *co = NULL; if(con && ( (co = con->getControlObject()) != NULL) ) { if(co == this || co->getObjectMount() == this) { TSShapeInstance::smScreenError = 0.001; fogExemption = true; } } if (state->isObjectRendered(this)) { mLastRenderFrame = sLastRenderFrame; // get shape detail and fog information...we might not even need to be drawn Point3F cameraOffset; getRenderTransform().getColumn(3,&cameraOffset); cameraOffset -= state->getCameraPosition(); F32 dist = cameraOffset.len(); if (dist < 0.01) dist = 0.01; F32 fogAmount = state->getHazeAndFog(dist,cameraOffset.z); F32 invScale = (1.0f/getMax(getMax(mObjScale.x,mObjScale.y),mObjScale.z)); if (mShapeInstance) DetailManager::selectPotentialDetails(mShapeInstance,dist,invScale); if (mShapeInstance) mShapeInstance->animate(); if ((fogAmount>0.99f && fogExemption == false) || (mShapeInstance && mShapeInstance->getCurrentDetail()<0) || (!mShapeInstance && !gShowBoundingBox)) { // no, don't draw anything return false; } for (U32 i = 0; i < MaxMountedImages; i++) { MountedImage& image = mMountedImageList[i]; if (image.dataBlock && image.shapeInstance) { DetailManager::selectPotentialDetails(image.shapeInstance,dist,invScale); [b] ShapeImageRenderImage* rimage = new ShapeImageRenderImage; rimage->obj = this; rimage->mSBase = this; rimage->mIndex = i; //are we transparent in some way? if (mFadeVal != 1.0f || mShapeInstance->hasTranslucency()) { rimage->isTranslucent = true; rimage->sortType = SceneRenderImage::Point; state->setImageRefPoint(this, rimage); } else { rimage->isTranslucent = false; rimage->sortType = SceneRenderImage::Normal; state->setImageRefPoint(this, rimage); } rimage->textureSortKey = mSkinHash ^ (U32)(dsize_t)(mDataBlock); state->insertRenderImage(rimage); [/b] } } TSShapeInstance::smScreenError = saveError; [b] SceneRenderImage* image = new SceneRenderImage; image->obj = this; //are we transparent in some way? if (mFadeVal != 1.0f || mShapeInstance->hasTranslucency()) { image->isTranslucent = true; image->sortType = SceneRenderImage::Point; state->setImageRefPoint(this, image); } else { image->isTranslucent = false; image->sortType = SceneRenderImage::Normal; state->setImageRefPoint(this, image); } image->textureSortKey = mSkinHash ^ (U32)(dsize_t)(mDataBlock); state->insertRenderImage(image); [/b] calcClassRenderData(); } return false; } //<-badspotContinued in reply...