Commit ccd1a241 authored by hybrid's avatar hybrid

Occlusion Query support.

I've finally took on Nadro's code from the forum and adapted it to overcome some restrictions of the original code. Moreover, there are some additional tweaks to make it simpler to use. A few minor things might still change in the future, but for now it looks like a proper implementation with many useful use cases. I'll come back on this in the next time when I provide an example for efficient usage of occlusion queries.

git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@3265 dfc29bdd-3216-0410-991c-e03cc46cb475
parent 6b3ce459
...@@ -18,7 +18,8 @@ namespace scene ...@@ -18,7 +18,8 @@ namespace scene
EAC_OFF = 0, EAC_OFF = 0,
EAC_BOX = 1, EAC_BOX = 1,
EAC_FRUSTUM_BOX = 2, EAC_FRUSTUM_BOX = 2,
EAC_FRUSTUM_SPHERE = 4 EAC_FRUSTUM_SPHERE = 4,
EAC_OCC_QUERY = 8
}; };
//! Names for culling type //! Names for culling type
...@@ -28,6 +29,7 @@ namespace scene ...@@ -28,6 +29,7 @@ namespace scene
"box", // camera box against node box "box", // camera box against node box
"frustum_box", // camera frustum against node box "frustum_box", // camera frustum against node box
"frustum_sphere", // camera frustum against node sphere "frustum_sphere", // camera frustum against node sphere
"occ_query", // occlusion query
0 0
}; };
......
...@@ -106,6 +106,9 @@ namespace video ...@@ -106,6 +106,9 @@ namespace video
//! Supports geometry shaders //! Supports geometry shaders
EVDF_GEOMETRY_SHADER, EVDF_GEOMETRY_SHADER,
//! Supports occlusion queries
EVDF_OCCLUSION_QUERY,
//! Only used for counting the elements of this enum //! Only used for counting the elements of this enum
EVDF_COUNT EVDF_COUNT
}; };
......
...@@ -521,7 +521,7 @@ namespace scene ...@@ -521,7 +521,7 @@ namespace scene
their geometry because it is their only reason for existence, their geometry because it is their only reason for existence,
for example the OctreeSceneNode. for example the OctreeSceneNode.
\param state The culling state to be used. */ \param state The culling state to be used. */
void setAutomaticCulling( E_CULLING_TYPE state) void setAutomaticCulling( u32 state)
{ {
AutomaticCullingState = state; AutomaticCullingState = state;
} }
...@@ -529,9 +529,8 @@ namespace scene ...@@ -529,9 +529,8 @@ namespace scene
//! Gets the automatic culling state. //! Gets the automatic culling state.
/** \return The automatic culling state. */ /** \return The automatic culling state. */
E_CULLING_TYPE getAutomaticCulling() const u32 getAutomaticCulling() const
{ {
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
return AutomaticCullingState; return AutomaticCullingState;
} }
...@@ -687,7 +686,7 @@ namespace scene ...@@ -687,7 +686,7 @@ namespace scene
out->addVector3d("Scale", getScale() ); out->addVector3d("Scale", getScale() );
out->addBool ("Visible", IsVisible ); out->addBool ("Visible", IsVisible );
out->addEnum ("AutomaticCulling", AutomaticCullingState, AutomaticCullingNames); out->addInt ("AutomaticCulling", AutomaticCullingState);
out->addInt ("DebugDataVisible", DebugDataVisible ); out->addInt ("DebugDataVisible", DebugDataVisible );
out->addBool ("IsDebugObject", IsDebugObject ); out->addBool ("IsDebugObject", IsDebugObject );
} }
...@@ -712,8 +711,12 @@ namespace scene ...@@ -712,8 +711,12 @@ namespace scene
setScale(in->getAttributeAsVector3d("Scale")); setScale(in->getAttributeAsVector3d("Scale"));
IsVisible = in->getAttributeAsBool("Visible"); IsVisible = in->getAttributeAsBool("Visible");
AutomaticCullingState = (scene::E_CULLING_TYPE) in->getAttributeAsEnumeration("AutomaticCulling", s32 tmpState = in->getAttributeAsEnumeration("AutomaticCulling",
scene::AutomaticCullingNames); scene::AutomaticCullingNames);
if (tmpState != -1)
AutomaticCullingState = (u32)tmpState;
else
AutomaticCullingState = in->getAttributeAsInt("AutomaticCulling");
DebugDataVisible = in->getAttributeAsInt("DebugDataVisible"); DebugDataVisible = in->getAttributeAsInt("DebugDataVisible");
IsDebugObject = in->getAttributeAsBool("IsDebugObject"); IsDebugObject = in->getAttributeAsBool("IsDebugObject");
...@@ -825,7 +828,7 @@ namespace scene ...@@ -825,7 +828,7 @@ namespace scene
s32 ID; s32 ID;
//! Automatic culling state //! Automatic culling state
E_CULLING_TYPE AutomaticCullingState; u32 AutomaticCullingState;
//! Flag if debug data should be drawn, such as Bounding Boxes. //! Flag if debug data should be drawn, such as Bounding Boxes.
s32 DebugDataVisible; s32 DebugDataVisible;
......
...@@ -31,7 +31,9 @@ namespace io ...@@ -31,7 +31,9 @@ namespace io
namespace scene namespace scene
{ {
class IMeshBuffer; class IMeshBuffer;
class IMesh;
class IMeshManipulator; class IMeshManipulator;
class ISceneNode;
} // end namespace scene } // end namespace scene
namespace video namespace video
...@@ -429,6 +431,43 @@ namespace video ...@@ -429,6 +431,43 @@ namespace video
//! Remove all hardware buffers //! Remove all hardware buffers
virtual void removeAllHardwareBuffers() =0; virtual void removeAllHardwareBuffers() =0;
//! Create occlusion query.
/** Use node for identification and mesh for occlusion test. */
virtual void createOcclusionQuery(scene::ISceneNode* node,
const scene::IMesh* mesh=0) =0;
//! Remove occlusion query.
virtual void removeOcclusionQuery(scene::ISceneNode* node) =0;
//! Remove all occlusion queries.
virtual void removeAllOcclusionQueries() =0;
//! Run occlusion query. Draws mesh stored in query.
/** If the mesh shall not be rendered visible, use
overrideMaterial to disable the color and depth buffer. */
virtual void runOcclusionQuery(scene::ISceneNode* node, bool visible=false) =0;
//! Run all occlusion queries. Draws all meshes stored in queries.
/** If the meshes shall not be rendered visible, use
overrideMaterial to disable the color and depth buffer. */
virtual void runAllOcclusionQueries(bool visible=false) =0;
//! Update occlusion query. Retrieves results from GPU.
/** If the query shall not block, set the flag to false.
Update might not occur in this case, though */
virtual void updateOcclusionQuery(scene::ISceneNode* node, bool block=true) =0;
//! Update all occlusion queries. Retrieves results from GPU.
/** If the query shall not block, set the flag to false.
Update might not occur in this case, though */
virtual void updateAllOcclusionQueries(bool block=true) =0;
//! Return query result.
/** Return value is the number of visible pixels/fragments.
The value is a safe approximation, i.e. can be larger than the
actual value of pixels. */
virtual u32 getOcclusionQueryResult(scene::ISceneNode* node) const =0;
//! Sets a boolean alpha channel on the texture based on a color key. //! Sets a boolean alpha channel on the texture based on a color key.
/** This makes the texture fully transparent at the texels where /** This makes the texture fully transparent at the texels where
this color key can be found when using for example draw2DImage this color key can be found when using for example draw2DImage
...@@ -1066,9 +1105,9 @@ namespace video ...@@ -1066,9 +1105,9 @@ namespace video
virtual void setTextureCreationFlag(E_TEXTURE_CREATION_FLAG flag, bool enabled=true) =0; virtual void setTextureCreationFlag(E_TEXTURE_CREATION_FLAG flag, bool enabled=true) =0;
//! Returns if a texture creation flag is enabled or disabled. //! Returns if a texture creation flag is enabled or disabled.
/** You can change this value using setTextureCreationMode(). /** You can change this value using setTextureCreationFlag().
\param flag Texture creation flag. \param flag Texture creation flag.
\return The current texture creation mode. */ \return The current texture creation flag enabled mode. */
virtual bool getTextureCreationFlag(E_TEXTURE_CREATION_FLAG flag) const =0; virtual bool getTextureCreationFlag(E_TEXTURE_CREATION_FLAG flag) const =0;
//! Creates a software image from a file. //! Creates a software image from a file.
...@@ -1106,8 +1145,8 @@ namespace video ...@@ -1106,8 +1145,8 @@ namespace video
/** Requires that there is a suitable image writer registered /** Requires that there is a suitable image writer registered
for writing the image. for writing the image.
\param image Image to write. \param image Image to write.
\param file An already open io::IWriteFile object. The name will be used to determine \param file An already open io::IWriteFile object. The name
the appropriate image writer to use. will be used to determine the appropriate image writer to use.
\param param Control parameter for the backend (e.g. compression \param param Control parameter for the backend (e.g. compression
level). level).
\return True on successful write. */ \return True on successful write. */
......
...@@ -35,7 +35,8 @@ CD3D9Driver::CD3D9Driver(const core::dimension2d<u32>& screenSize, HWND window, ...@@ -35,7 +35,8 @@ CD3D9Driver::CD3D9Driver(const core::dimension2d<u32>& screenSize, HWND window,
MaxTextureUnits(0), MaxUserClipPlanes(0), MaxTextureUnits(0), MaxUserClipPlanes(0),
MaxLightDistance(0.f), LastSetLight(-1), Cached2DModeSignature(0), MaxLightDistance(0.f), LastSetLight(-1), Cached2DModeSignature(0),
ColorFormat(ECF_A8R8G8B8), DeviceLost(false), ColorFormat(ECF_A8R8G8B8), DeviceLost(false),
Fullscreen(fullscreen), DriverWasReset(true), AlphaToCoverageSupport(false) Fullscreen(fullscreen), DriverWasReset(true), OcclusionQuerySupport(false),
AlphaToCoverageSupport(false)
{ {
#ifdef _DEBUG #ifdef _DEBUG
setDebugName("CD3D9Driver"); setDebugName("CD3D9Driver");
...@@ -72,9 +73,13 @@ CD3D9Driver::~CD3D9Driver() ...@@ -72,9 +73,13 @@ CD3D9Driver::~CD3D9Driver()
{ {
deleteMaterialRenders(); deleteMaterialRenders();
deleteAllTextures(); deleteAllTextures();
removeAllOcclusionQueries();
// drop the main depth buffer removeAllHardwareBuffers();
DepthBuffers[0]->drop(); for (u32 i=0; i<DepthBuffers.size(); ++i)
{
DepthBuffers[i]->drop();
}
DepthBuffers.clear();
// drop d3d9 // drop d3d9
...@@ -424,6 +429,7 @@ bool CD3D9Driver::initDriver(const core::dimension2d<u32>& screenSize, ...@@ -424,6 +429,7 @@ bool CD3D9Driver::initDriver(const core::dimension2d<u32>& screenSize,
MaxTextureUnits = core::min_((u32)Caps.MaxSimultaneousTextures, MATERIAL_MAX_TEXTURES); MaxTextureUnits = core::min_((u32)Caps.MaxSimultaneousTextures, MATERIAL_MAX_TEXTURES);
MaxUserClipPlanes = (u32)Caps.MaxUserClipPlanes; MaxUserClipPlanes = (u32)Caps.MaxUserClipPlanes;
OcclusionQuerySupport=(pID3DDevice->CreateQuery(D3DQUERYTYPE_OCCLUSION, NULL) == S_OK);
if (VendorID==0x10DE)//NVidia if (VendorID==0x10DE)//NVidia
AlphaToCoverageSupport = (pID3D->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, AlphaToCoverageSupport = (pID3D->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
...@@ -636,6 +642,8 @@ bool CD3D9Driver::queryFeature(E_VIDEO_DRIVER_FEATURE feature) const ...@@ -636,6 +642,8 @@ bool CD3D9Driver::queryFeature(E_VIDEO_DRIVER_FEATURE feature) const
return (Caps.PrimitiveMiscCaps & D3DPMISCCAPS_INDEPENDENTWRITEMASKS) != 0; return (Caps.PrimitiveMiscCaps & D3DPMISCCAPS_INDEPENDENTWRITEMASKS) != 0;
case EVDF_MRT_BLEND: case EVDF_MRT_BLEND:
return (Caps.PrimitiveMiscCaps & D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING) != 0; return (Caps.PrimitiveMiscCaps & D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING) != 0;
case EVDF_OCCLUSION_QUERY:
return OcclusionQuerySupport;
default: default:
return false; return false;
}; };
...@@ -1228,6 +1236,102 @@ void CD3D9Driver::drawHardwareBuffer(SHWBufferLink *_HWBuffer) ...@@ -1228,6 +1236,102 @@ void CD3D9Driver::drawHardwareBuffer(SHWBufferLink *_HWBuffer)
} }
//! Create occlusion query.
/** Use node for identification and mesh for occlusion test. */
void CD3D9Driver::createOcclusionQuery(scene::ISceneNode* node,
const scene::IMesh* mesh)
{
if (!queryFeature(EVDF_OCCLUSION_QUERY))
return;
CNullDriver::createOcclusionQuery(node, mesh);
const s32 index = OcclusionQueries.linear_search(SOccQuery(node));
if ((index != -1) && (OcclusionQueries[index].ID == 0))
pID3DDevice->CreateQuery(D3DQUERYTYPE_OCCLUSION, reinterpret_cast<IDirect3DQuery9**>(&OcclusionQueries[index].ID));
}
//! Remove occlusion query.
void CD3D9Driver::removeOcclusionQuery(scene::ISceneNode* node)
{
const s32 index = OcclusionQueries.linear_search(SOccQuery(node));
if (index != -1)
{
if (OcclusionQueries[index].ID != 0)
reinterpret_cast<IDirect3DQuery9*>(OcclusionQueries[index].ID)->Release();
CNullDriver::removeOcclusionQuery(node);
}
}
//! Run occlusion query. Draws mesh stored in query.
/** If the mesh shall not be rendered visible, use
overrideMaterial to disable the color and depth buffer. */
void CD3D9Driver::runOcclusionQuery(scene::ISceneNode* node, bool visible)
{
if (!node)
return;
const s32 index = OcclusionQueries.linear_search(SOccQuery(node));
if (index != -1)
{
os::Printer::log("Start query", core::stringc(reinterpret_cast<u32>(OcclusionQueries[index].ID)));
if (OcclusionQueries[index].ID)
reinterpret_cast<IDirect3DQuery9*>(OcclusionQueries[index].ID)->Issue(D3DISSUE_BEGIN);
CNullDriver::runOcclusionQuery(node,visible);
if (OcclusionQueries[index].ID)
reinterpret_cast<IDirect3DQuery9*>(OcclusionQueries[index].ID)->Issue(D3DISSUE_END);
}
}
//! Update occlusion query. Retrieves results from GPU.
/** If the query shall not block, set the flag to false.
Update might not occur in this case, though */
void CD3D9Driver::updateOcclusionQuery(scene::ISceneNode* node, bool block)
{
const s32 index = OcclusionQueries.linear_search(SOccQuery(node));
if (index != -1)
{
// not yet started
if (OcclusionQueries[index].Run==u32(~0))
return;
bool available = block?true:false;
int tmp=0;
if (!block)
available=(reinterpret_cast<IDirect3DQuery9*>(OcclusionQueries[index].ID)->GetData(&tmp, sizeof(DWORD), 0)==S_OK);
else
{
do
{
HRESULT hr = reinterpret_cast<IDirect3DQuery9*>(OcclusionQueries[index].ID)->GetData(&tmp, sizeof(DWORD), D3DGETDATA_FLUSH);
available = (hr == S_OK);
if (hr!=S_FALSE)
break;
} while (!available);
}
if (available)
{
OcclusionQueries[index].Result = tmp;
os::Printer::log("Occ result", core::stringc(tmp));
}
}
}
//! Return query result.
/** Return value is the number of visible pixels/fragments.
The value is a safe approximation, i.e. can be larger than the
actual value of pixels. */
u32 CD3D9Driver::getOcclusionQueryResult(scene::ISceneNode* node) const
{
const s32 index = OcclusionQueries.linear_search(SOccQuery(node));
if (index != -1)
return OcclusionQueries[index].Result;
else
return ~0;
}
//! draws a vertex primitive list //! draws a vertex primitive list
void CD3D9Driver::drawVertexPrimitiveList(const void* vertices, void CD3D9Driver::drawVertexPrimitiveList(const void* vertices,
u32 vertexCount, const void* indexList, u32 primitiveCount, u32 vertexCount, const void* indexList, u32 primitiveCount,
...@@ -2733,9 +2837,17 @@ bool CD3D9Driver::reset() ...@@ -2733,9 +2837,17 @@ bool CD3D9Driver::reset()
} }
for (i=0; i<DepthBuffers.size(); ++i) for (i=0; i<DepthBuffers.size(); ++i)
{ {
if(DepthBuffers[i]->Surface) if (DepthBuffers[i]->Surface)
DepthBuffers[i]->Surface->Release(); DepthBuffers[i]->Surface->Release();
} }
for (i=0; i<OcclusionQueries.size(); ++i)
{
if (OcclusionQueries[i].ID)
{
reinterpret_cast<IDirect3DQuery9*>(OcclusionQueries[i].ID)->Release();
OcclusionQueries[i].ID=0;
}
}
// this does not require a restore in the reset method, it's updated // this does not require a restore in the reset method, it's updated
// automatically in the next render cycle. // automatically in the next render cycle.
removeAllHardwareBuffers(); removeAllHardwareBuffers();
...@@ -2781,6 +2893,10 @@ bool CD3D9Driver::reset() ...@@ -2781,6 +2893,10 @@ bool CD3D9Driver::reset()
&(DepthBuffers[i]->Surface), &(DepthBuffers[i]->Surface),
NULL); NULL);
} }
for (i=0; i<OcclusionQueries.size(); ++i)
{
pID3DDevice->CreateQuery(D3DQUERYTYPE_OCCLUSION, reinterpret_cast<IDirect3DQuery9**>(&OcclusionQueries[i].ID));
}
if (FAILED(hr)) if (FAILED(hr))
{ {
......
...@@ -116,6 +116,30 @@ namespace video ...@@ -116,6 +116,30 @@ namespace video
//! Draw hardware buffer //! Draw hardware buffer
virtual void drawHardwareBuffer(SHWBufferLink *HWBuffer); virtual void drawHardwareBuffer(SHWBufferLink *HWBuffer);
//! Create occlusion query.
/** Use node for identification and mesh for occlusion test. */
virtual void createOcclusionQuery(scene::ISceneNode* node,
const scene::IMesh* mesh=0);
//! Remove occlusion query.
virtual void removeOcclusionQuery(scene::ISceneNode* node);
//! Run occlusion query. Draws mesh stored in query.
/** If the mesh shall not be rendered visible, use
overrideMaterial to disable the color and depth buffer. */
virtual void runOcclusionQuery(scene::ISceneNode* node, bool visible=false);
//! Update occlusion query. Retrieves results from GPU.
/** If the query shall not block, set the flag to false.
Update might not occur in this case, though */
virtual void updateOcclusionQuery(scene::ISceneNode* node, bool block=true);
//! Return query result.
/** Return value is the number of visible pixels/fragments.
The value is a safe approximation, i.e. can be larger then the
actual value of pixels. */
virtual u32 getOcclusionQueryResult(scene::ISceneNode* node) const;
//! draws a vertex primitive list //! draws a vertex primitive list
virtual void drawVertexPrimitiveList(const void* vertices, u32 vertexCount, virtual void drawVertexPrimitiveList(const void* vertices, u32 vertexCount,
const void* indexList, u32 primitiveCount, const void* indexList, u32 primitiveCount,
...@@ -432,6 +456,7 @@ namespace video ...@@ -432,6 +456,7 @@ namespace video
bool DeviceLost; bool DeviceLost;
bool Fullscreen; bool Fullscreen;
bool DriverWasReset; bool DriverWasReset;
bool OcclusionQuerySupport;
bool AlphaToCoverageSupport; bool AlphaToCoverageSupport;
}; };
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "IImageLoader.h" #include "IImageLoader.h"
#include "IImageWriter.h" #include "IImageWriter.h"
#include "IMaterialRenderer.h" #include "IMaterialRenderer.h"
#include "IAnimatedMeshSceneNode.h"
#include "CMeshManipulator.h" #include "CMeshManipulator.h"
#include "CColorConverter.h" #include "CColorConverter.h"
...@@ -274,6 +275,7 @@ bool CNullDriver::endScene() ...@@ -274,6 +275,7 @@ bool CNullDriver::endScene()
{ {
FPSCounter.registerFrame(os::Timer::getRealTime(), PrimitivesDrawn); FPSCounter.registerFrame(os::Timer::getRealTime(), PrimitivesDrawn);
updateAllHardwareBuffers(); updateAllHardwareBuffers();
updateAllOcclusionQueries();
return true; return true;
} }
...@@ -1541,6 +1543,143 @@ bool CNullDriver::isHardwareBufferRecommend(const scene::IMeshBuffer* mb) ...@@ -1541,6 +1543,143 @@ bool CNullDriver::isHardwareBufferRecommend(const scene::IMeshBuffer* mb)
} }
//! Create occlusion query.
/** Use node for identification and mesh for occlusion test. */
void CNullDriver::createOcclusionQuery(scene::ISceneNode* node, const scene::IMesh* mesh)
{
if (!node)
return;
if (!mesh)
{
if ((node->getType() != scene::ESNT_MESH) && (node->getType() != scene::ESNT_ANIMATED_MESH))
return;
else if (node->getType() == scene::ESNT_MESH)
mesh = static_cast<scene::IMeshSceneNode*>(node)->getMesh();
else
mesh = static_cast<scene::IAnimatedMeshSceneNode*>(node)->getMesh()->getMesh(0);
if (!mesh)
return;
}
//search for query
s32 index = OcclusionQueries.linear_search(SOccQuery(node));
if (index != -1)
{
if (OcclusionQueries[index].Mesh != mesh)
{
OcclusionQueries[index].Mesh->drop();
OcclusionQueries[index].Mesh = mesh;
mesh->grab();
}
}
else
{
OcclusionQueries.push_back(SOccQuery(node, mesh));
node->setAutomaticCulling(node->getAutomaticCulling() | scene::EAC_OCC_QUERY);
}
}
//! Remove occlusion query.
void CNullDriver::removeOcclusionQuery(scene::ISceneNode* node)
{
//search for query
s32 index = OcclusionQueries.linear_search(SOccQuery(node));
if (index != -1)
{
node->setAutomaticCulling(node->getAutomaticCulling() & ~scene::EAC_OCC_QUERY);
OcclusionQueries.erase(index);
}
}
//! Remove all occlusion queries.
void CNullDriver::removeAllOcclusionQueries()
{
for (s32 i=OcclusionQueries.size()-1; i>=0; --i)
{
removeOcclusionQuery(OcclusionQueries[i].Node);
}
}
//! Run occlusion query. Draws mesh stored in query.
/** If the mesh shall be rendered visible, use
flag to enable the proper material setting. */
void CNullDriver::runOcclusionQuery(scene::ISceneNode* node, bool visible)
{
if(!node)
return;
s32 index = OcclusionQueries.linear_search(SOccQuery(node));
if (index==-1)
return;
OcclusionQueries[index].Run=0;
if (!visible)
{
SMaterial mat;
mat.Lighting=false;
mat.AntiAliasing=0;
mat.ColorMask=ECP_NONE;
mat.GouraudShading=false;
mat.ZWriteEnable=false;
setMaterial(mat);
}
setTransform(video::ETS_WORLD, node->getAbsoluteTransformation());
const scene::IMesh* mesh = OcclusionQueries[index].Mesh;
for (u32 i=0; i<mesh->getMeshBufferCount(); ++i)
{
if (visible)
setMaterial(mesh->getMeshBuffer(i)->getMaterial());
drawMeshBuffer(mesh->getMeshBuffer(i));
}
}
//! Run all occlusion queries. Draws all meshes stored in queries.
/** If the meshes shall not be rendered visible, use
overrideMaterial to disable the color and depth buffer. */
void CNullDriver::runAllOcclusionQueries(bool visible)
{
for (u32 i=0; i<OcclusionQueries.size(); ++i)
runOcclusionQuery(OcclusionQueries[i].Node, visible);
}
//! Update occlusion query. Retrieves results from GPU.
/** If the query shall not block, set the flag to false.
Update might not occur in this case, though */
void CNullDriver::updateOcclusionQuery(scene::ISceneNode* node, bool block)
{
}
//! Update all occlusion queries. Retrieves results from GPU.
/** If the query shall not block, set the flag to false.
Update might not occur in this case, though */
void CNullDriver::updateAllOcclusionQueries(bool block)
{
for (u32 i=0; i<OcclusionQueries.size(); ++i)
{
if (OcclusionQueries[i].Run==u32(~0))
continue;
updateOcclusionQuery(OcclusionQueries[i].Node, block);
++OcclusionQueries[i].Run;
if (OcclusionQueries[i].Run>1000)
removeOcclusionQuery(OcclusionQueries[i].Node);
}
}
//! Return query result.
/** Return value is the number of visible pixels/fragments.
The value is a safe approximation, i.e. can be larger then the
actual value of pixels. */
u32 CNullDriver::getOcclusionQueryResult(scene::ISceneNode* node) const
{
return ~0;
}
//! Only used by the internal engine. Used to notify the driver that //! Only used by the internal engine. Used to notify the driver that
//! the window was resized. //! the window was resized.
void CNullDriver::OnResize(const core::dimension2d<u32>& size) void CNullDriver::OnResize(const core::dimension2d<u32>& size)
......
...@@ -13,7 +13,9 @@ ...@@ -13,7 +13,9 @@
#include "irrString.h" #include "irrString.h"
#include "irrMap.h" #include "irrMap.h"
#include "IAttributes.h" #include "IAttributes.h"
#include "IMesh.h"
#include "IMeshBuffer.h" #include "IMeshBuffer.h"
#include "IMeshSceneNode.h"
#include "CFPSCounter.h" #include "CFPSCounter.h"
#include "S3DVertex.h" #include "S3DVertex.h"
#include "SVertexIndex.h" #include "SVertexIndex.h"
...@@ -395,18 +397,19 @@ namespace video ...@@ -395,18 +397,19 @@ namespace video
//! updates hardware buffer if needed (only some drivers can) //! updates hardware buffer if needed (only some drivers can)
virtual bool updateHardwareBuffer(SHWBufferLink *HWBuffer) {return false;} virtual bool updateHardwareBuffer(SHWBufferLink *HWBuffer) {return false;}
//! Create hardware buffer from mesh (only some drivers can)
virtual SHWBufferLink *createHardwareBuffer(const scene::IMeshBuffer* mb) {return 0;}
//! Draw hardware buffer (only some drivers can) //! Draw hardware buffer (only some drivers can)
virtual void drawHardwareBuffer(SHWBufferLink *HWBuffer) {} virtual void drawHardwareBuffer(SHWBufferLink *HWBuffer) {}
//! Update all hardware buffers, remove unused ones
virtual void updateAllHardwareBuffers();
//! Delete hardware buffer //! Delete hardware buffer
virtual void deleteHardwareBuffer(SHWBufferLink *HWBuffer); virtual void deleteHardwareBuffer(SHWBufferLink *HWBuffer);
//! Create hardware buffer from mesh (only some drivers can)
virtual SHWBufferLink *createHardwareBuffer(const scene::IMeshBuffer* mb) {return 0;}
public:
//! Update all hardware buffers, remove unused ones
virtual void updateAllHardwareBuffers();
//! Remove hardware buffer //! Remove hardware buffer
virtual void removeHardwareBuffer(const scene::IMeshBuffer* mb); virtual void removeHardwareBuffer(const scene::IMeshBuffer* mb);
...@@ -416,7 +419,43 @@ namespace video ...@@ -416,7 +419,43 @@ namespace video
//! is vbo recommended on this mesh? //! is vbo recommended on this mesh?
virtual bool isHardwareBufferRecommend(const scene::IMeshBuffer* mb); virtual bool isHardwareBufferRecommend(const scene::IMeshBuffer* mb);
public: //! Create occlusion query.
/** Use node for identification and mesh for occlusion test. */
virtual void createOcclusionQuery(scene::ISceneNode* node,
const scene::IMesh* mesh=0);
//! Remove occlusion query.
virtual void removeOcclusionQuery(scene::ISceneNode* node);
//! Remove all occlusion queries.
virtual void removeAllOcclusionQueries();
//! Run occlusion query. Draws mesh stored in query.
/** If the mesh shall not be rendered visible, use
overrideMaterial to disable the color and depth buffer. */
virtual void runOcclusionQuery(scene::ISceneNode* node, bool visible=false);
//! Run all occlusion queries. Draws all meshes stored in queries.
/** If the meshes shall not be rendered visible, use
overrideMaterial to disable the color and depth buffer. */
virtual void runAllOcclusionQueries(bool visible=false);
//! Update occlusion query. Retrieves results from GPU.
/** If the query shall not block, set the flag to false.
Update might not occur in this case, though */
virtual void updateOcclusionQuery(scene::ISceneNode* node, bool block=true);
//! Update all occlusion queries. Retrieves results from GPU.
/** If the query shall not block, set the flag to false.
Update might not occur in this case, though */
virtual void updateAllOcclusionQueries(bool block=true);
//! Return query result.
/** Return value is the number of visible pixels/fragments.
The value is a safe approximation, i.e. can be larger than the
actual value of pixels. */
virtual u32 getOcclusionQueryResult(scene::ISceneNode* node) const;
//! Only used by the engine internally. //! Only used by the engine internally.
/** Used to notify the driver that the window was resized. */ /** Used to notify the driver that the window was resized. */
virtual void OnResize(const core::dimension2d<u32>& size); virtual void OnResize(const core::dimension2d<u32>& size);
...@@ -675,8 +714,61 @@ namespace video ...@@ -675,8 +714,61 @@ namespace video
virtual void regenerateMipMapLevels(void* mipmapData=0) {}; virtual void regenerateMipMapLevels(void* mipmapData=0) {};
core::dimension2d<u32> size; core::dimension2d<u32> size;
}; };
core::array<SSurface> Textures; core::array<SSurface> Textures;
struct SOccQuery
{
SOccQuery(scene::ISceneNode* node, const scene::IMesh* mesh=0) : Node(node), Mesh(mesh), ID(0), Result(~0), Run(~0)
{
if (Node)
Node->grab();
if (Mesh)
Mesh->grab();
}
SOccQuery(const SOccQuery& other) : Node(other.Node), Mesh(other.Mesh), ID(other.ID), Result(other.Result), Run(other.Run)
{
if (Node)
Node->grab();
if (Mesh)
Mesh->grab();
}
~SOccQuery()
{
if (Node)
Node->drop();
if (Mesh)
Mesh->drop();
}
SOccQuery& operator=(const SOccQuery& other)
{
Node=other.Node;
Mesh=other.Mesh;
ID=other.ID;
Result=other.Result;
Run=other.Run;
if (Node)
Node->grab();
if (Mesh)
Mesh->grab();
return *this;
}
bool operator==(const SOccQuery& other) const
{
return other.Node==Node;
}
scene::ISceneNode* Node;
const scene::IMesh* Mesh;
void* ID;
u32 Result;
u32 Run;
};
core::array<SOccQuery> OcclusionQueries;
core::array<video::IImageLoader*> SurfaceLoader; core::array<video::IImageLoader*> SurfaceLoader;
core::array<video::IImageWriter*> SurfaceWriter; core::array<video::IImageWriter*> SurfaceWriter;
core::array<SLight> Lights; core::array<SLight> Lights;
......
...@@ -591,6 +591,8 @@ COpenGLDriver::~COpenGLDriver() ...@@ -591,6 +591,8 @@ COpenGLDriver::~COpenGLDriver()
// textures manually before releasing the dc. Oh how I love this. // textures manually before releasing the dc. Oh how I love this.
deleteAllTextures(); deleteAllTextures();
removeAllOcclusionQueries();
removeAllHardwareBuffers();
#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_ #ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
if (DeviceType == EIDT_WIN32) if (DeviceType == EIDT_WIN32)
...@@ -1227,6 +1229,101 @@ void COpenGLDriver::drawHardwareBuffer(SHWBufferLink *_HWBuffer) ...@@ -1227,6 +1229,101 @@ void COpenGLDriver::drawHardwareBuffer(SHWBufferLink *_HWBuffer)
} }
//! Create occlusion query.
/** Use node for identification and mesh for occlusion test. */
void COpenGLDriver::createOcclusionQuery(scene::ISceneNode* node,
const scene::IMesh* mesh)
{
if (!queryFeature(EVDF_OCCLUSION_QUERY))
return;
CNullDriver::createOcclusionQuery(node, mesh);
const s32 index = OcclusionQueries.linear_search(SOccQuery(node));
if ((index != -1) && (OcclusionQueries[index].ID == 0))
extGlGenQueries(1, reinterpret_cast<GLuint*>(&OcclusionQueries[index].ID));
}
//! Remove occlusion query.
void COpenGLDriver::removeOcclusionQuery(scene::ISceneNode* node)
{
const s32 index = OcclusionQueries.linear_search(SOccQuery(node));
if (index != -1)
{
if (OcclusionQueries[index].ID != 0)
extGlDeleteQueries(1, reinterpret_cast<GLuint*>(&OcclusionQueries[index].ID));
CNullDriver::removeOcclusionQuery(node);
}
}
//! Run occlusion query. Draws mesh stored in query.
/** If the mesh shall not be rendered visible, use
overrideMaterial to disable the color and depth buffer. */
void COpenGLDriver::runOcclusionQuery(scene::ISceneNode* node, bool visible)
{
if (!node)
return;
const s32 index = OcclusionQueries.linear_search(SOccQuery(node));
if (index != -1)
{
os::Printer::log("Start query", core::stringc(reinterpret_cast<GLuint>(OcclusionQueries[index].ID)));
if (OcclusionQueries[index].ID)
extGlBeginQuery(GL_SAMPLES_PASSED_ARB, reinterpret_cast<GLuint>(OcclusionQueries[index].ID));
CNullDriver::runOcclusionQuery(node,visible);
if (OcclusionQueries[index].ID)
extGlEndQuery(GL_SAMPLES_PASSED_ARB);
testGLError();
}
}
//! Update occlusion query. Retrieves results from GPU.
/** If the query shall not block, set the flag to false.
Update might not occur in this case, though */
void COpenGLDriver::updateOcclusionQuery(scene::ISceneNode* node, bool block)
{
const s32 index = OcclusionQueries.linear_search(SOccQuery(node));
if (index != -1)
{
// not yet started
if (OcclusionQueries[index].Run==u32(~0))
return;
GLint available = block?GL_TRUE:GL_FALSE;
if (!block)
extGlGetQueryObjectiv(reinterpret_cast<GLuint>(OcclusionQueries[index].ID),
GL_QUERY_RESULT_AVAILABLE_ARB,
&available);
testGLError();
if (available==GL_TRUE)
{
extGlGetQueryObjectiv(reinterpret_cast<GLuint>(OcclusionQueries[index].ID),
GL_QUERY_RESULT_ARB,
&available);
OcclusionQueries[index].Result = available;
os::Printer::log("Occ result", core::stringc(available));
}
testGLError();
}
}
//! Return query result.
/** Return value is the number of visible pixels/fragments.
The value is a safe approximation, i.e. can be larger than the
actual value of pixels. */
u32 COpenGLDriver::getOcclusionQueryResult(scene::ISceneNode* node) const
{
const s32 index = OcclusionQueries.linear_search(SOccQuery(node));
if (index != -1)
return OcclusionQueries[index].Result;
else
return ~0;
}
// small helper function to create vertex buffer object adress offsets // small helper function to create vertex buffer object adress offsets
static inline u8* buffer_offset(const long offset) static inline u8* buffer_offset(const long offset)
{ {
...@@ -2326,6 +2423,7 @@ bool COpenGLDriver::testGLError() ...@@ -2326,6 +2423,7 @@ bool COpenGLDriver::testGLError()
os::Printer::log("GL_INVALID_FRAMEBUFFER_OPERATION", ELL_ERROR); break; os::Printer::log("GL_INVALID_FRAMEBUFFER_OPERATION", ELL_ERROR); break;
#endif #endif
}; };
_IRR_DEBUG_BREAK_IF(true);
return true; return true;
#else #else
return false; return false;
......
...@@ -99,6 +99,30 @@ namespace video ...@@ -99,6 +99,30 @@ namespace video
//! Draw hardware buffer //! Draw hardware buffer
virtual void drawHardwareBuffer(SHWBufferLink *HWBuffer); virtual void drawHardwareBuffer(SHWBufferLink *HWBuffer);
//! Create occlusion query.
/** Use node for identification and mesh for occlusion test. */
virtual void createOcclusionQuery(scene::ISceneNode* node,
const scene::IMesh* mesh=0);
//! Remove occlusion query.
virtual void removeOcclusionQuery(scene::ISceneNode* node);
//! Run occlusion query. Draws mesh stored in query.
/** If the mesh shall not be rendered visible, use
overrideMaterial to disable the color and depth buffer. */
virtual void runOcclusionQuery(scene::ISceneNode* node, bool visible=false);
//! Update occlusion query. Retrieves results from GPU.
/** If the query shall not block, set the flag to false.
Update might not occur in this case, though */
virtual void updateOcclusionQuery(scene::ISceneNode* node, bool block=true);
//! Return query result.
/** Return value is the number of visible pixels/fragments.
The value is a safe approximation, i.e. can be larger then the
actual value of pixels. */
virtual u32 getOcclusionQueryResult(scene::ISceneNode* node) const;
//! draws a vertex primitive list //! draws a vertex primitive list
virtual void drawVertexPrimitiveList(const void* vertices, u32 vertexCount, virtual void drawVertexPrimitiveList(const void* vertices, u32 vertexCount,
const void* indexList, u32 primitiveCount, const void* indexList, u32 primitiveCount,
......
...@@ -23,7 +23,8 @@ COpenGLExtensionHandler::COpenGLExtensionHandler() : ...@@ -23,7 +23,8 @@ COpenGLExtensionHandler::COpenGLExtensionHandler() :
MaxUserClipPlanes(0), MaxAuxBuffers(0), MaxUserClipPlanes(0), MaxAuxBuffers(0),
MaxMultipleRenderTargets(1), MaxIndices(65535), MaxMultipleRenderTargets(1), MaxIndices(65535),
MaxTextureSize(1), MaxGeometryVerticesOut(0), MaxTextureSize(1), MaxGeometryVerticesOut(0),
MaxTextureLODBias(0.f), Version(0), ShaderLanguageVersion(0) MaxTextureLODBias(0.f), Version(0), ShaderLanguageVersion(0),
OcclusionQuerySupport(false)
#ifdef _IRR_OPENGL_USE_EXTPOINTER_ #ifdef _IRR_OPENGL_USE_EXTPOINTER_
,pGlActiveTextureARB(0), pGlClientActiveTextureARB(0), ,pGlActiveTextureARB(0), pGlClientActiveTextureARB(0),
pGlGenProgramsARB(0), pGlGenProgramsNV(0), pGlGenProgramsARB(0), pGlGenProgramsNV(0),
...@@ -663,6 +664,18 @@ void COpenGLExtensionHandler::initExtensions(bool stencilBuffer) ...@@ -663,6 +664,18 @@ void COpenGLExtensionHandler::initExtensions(bool stencilBuffer)
} }
MaxTextureUnits = core::min_(MaxTextureUnits,static_cast<u8>(MATERIAL_MAX_TEXTURES)); MaxTextureUnits = core::min_(MaxTextureUnits,static_cast<u8>(MATERIAL_MAX_TEXTURES));
#ifdef GL_ARB_occlusion_query
if (FeatureAvailable[IRR_ARB_occlusion_query])
{
extGlGetQueryiv(GL_SAMPLES_PASSED_ARB,GL_QUERY_COUNTER_BITS_ARB,
&num);
OcclusionQuerySupport=(num>0);
}
else
#endif
OcclusionQuerySupport=false;
#ifdef _DEBUG #ifdef _DEBUG
if (FeatureAvailable[IRR_NVX_gpu_memory_info]) if (FeatureAvailable[IRR_NVX_gpu_memory_info])
{ {
...@@ -744,6 +757,8 @@ bool COpenGLExtensionHandler::queryFeature(E_VIDEO_DRIVER_FEATURE feature) const ...@@ -744,6 +757,8 @@ bool COpenGLExtensionHandler::queryFeature(E_VIDEO_DRIVER_FEATURE feature) const
return FeatureAvailable[IRR_EXT_draw_buffers2]; return FeatureAvailable[IRR_EXT_draw_buffers2];
case EVDF_MRT_BLEND_FUNC: case EVDF_MRT_BLEND_FUNC:
return FeatureAvailable[IRR_ARB_draw_buffers_blend] || FeatureAvailable[IRR_AMD_draw_buffers_blend]; return FeatureAvailable[IRR_ARB_draw_buffers_blend] || FeatureAvailable[IRR_AMD_draw_buffers_blend];
case EVDF_OCCLUSION_QUERY:
return FeatureAvailable[IRR_ARB_occlusion_query] && OcclusionQuerySupport;
default: default:
return false; return false;
}; };
......
...@@ -865,6 +865,8 @@ class COpenGLExtensionHandler ...@@ -865,6 +865,8 @@ class COpenGLExtensionHandler
//! GLSL version as Integer: 100*Major+Minor //! GLSL version as Integer: 100*Major+Minor
u16 ShaderLanguageVersion; u16 ShaderLanguageVersion;
bool OcclusionQuerySupport;
// public access to the (loaded) extensions. // public access to the (loaded) extensions.
// general functions // general functions
void extGlActiveTexture(GLenum texture); void extGlActiveTexture(GLenum texture);
......
...@@ -1131,61 +1131,62 @@ bool CSceneManager::isCulled(const ISceneNode* node) const ...@@ -1131,61 +1131,62 @@ bool CSceneManager::isCulled(const ISceneNode* node) const
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
return false; return false;
} }
bool result = false;
switch ( node->getAutomaticCulling() ) // has occlusion query information
if (node->getAutomaticCulling() & scene::EAC_OCC_QUERY)
{ {
// can be seen by a bounding box ? result = (Driver->getOcclusionQueryResult(const_cast<ISceneNode*>(node))==0);
case scene::EAC_BOX: }
{
core::aabbox3d<f32> tbox = node->getBoundingBox();
node->getAbsoluteTransformation().transformBoxEx(tbox);
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
return !(tbox.intersectsWithBox(cam->getViewFrustum()->getBoundingBox() ));
}
// can be seen by a bounding sphere // can be seen by a bounding box ?
case scene::EAC_FRUSTUM_SPHERE: if (!result && (node->getAutomaticCulling() & scene::EAC_BOX))
{ // requires bbox diameter {
} core::aabbox3d<f32> tbox = node->getBoundingBox();
break; node->getAbsoluteTransformation().transformBoxEx(tbox);
result = !(tbox.intersectsWithBox(cam->getViewFrustum()->getBoundingBox() ));
}
// can be seen by cam pyramid planes ? // can be seen by a bounding sphere
case scene::EAC_FRUSTUM_BOX: if (!result && (node->getAutomaticCulling() & scene::EAC_FRUSTUM_SPHERE))
{ { // requires bbox diameter
SViewFrustum frust = *cam->getViewFrustum(); }
// can be seen by cam pyramid planes ?
if (!result && (node->getAutomaticCulling() & scene::EAC_FRUSTUM_BOX))
{
SViewFrustum frust = *cam->getViewFrustum();
//transform the frustum to the node's current absolute transformation //transform the frustum to the node's current absolute transformation
core::matrix4 invTrans(node->getAbsoluteTransformation(), core::matrix4::EM4CONST_INVERSE); core::matrix4 invTrans(node->getAbsoluteTransformation(), core::matrix4::EM4CONST_INVERSE);
//invTrans.makeInverse(); //invTrans.makeInverse();
frust.transform(invTrans); frust.transform(invTrans);
core::vector3df edges[8]; core::vector3df edges[8];
node->getBoundingBox().getEdges(edges); node->getBoundingBox().getEdges(edges);
for (s32 i=0; i<scene::SViewFrustum::VF_PLANE_COUNT; ++i) for (s32 i=0; i<scene::SViewFrustum::VF_PLANE_COUNT; ++i)
{
bool boxInFrustum=false;
for (u32 j=0; j<8; ++j)
{ {
bool boxInFrustum=false; if (frust.planes[i].classifyPointRelation(edges[j]) != core::ISREL3D_FRONT)
for (u32 j=0; j<8; ++j)
{ {
if (frust.planes[i].classifyPointRelation(edges[j]) != core::ISREL3D_FRONT) boxInFrustum=true;
{ break;
boxInFrustum=true;
break;
}
} }
}
if (!boxInFrustum) if (!boxInFrustum)
return true; {
result = true;
break;
} }
} }
break;
case scene::EAC_OFF:
break;
} }
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
return false; return result;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment