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
EAC_OFF = 0,
EAC_BOX = 1,
EAC_FRUSTUM_BOX = 2,
EAC_FRUSTUM_SPHERE = 4
EAC_FRUSTUM_SPHERE = 4,
EAC_OCC_QUERY = 8
};
//! Names for culling type
......@@ -28,6 +29,7 @@ namespace scene
"box", // camera box against node box
"frustum_box", // camera frustum against node box
"frustum_sphere", // camera frustum against node sphere
"occ_query", // occlusion query
0
};
......
......@@ -106,6 +106,9 @@ namespace video
//! Supports geometry shaders
EVDF_GEOMETRY_SHADER,
//! Supports occlusion queries
EVDF_OCCLUSION_QUERY,
//! Only used for counting the elements of this enum
EVDF_COUNT
};
......
......@@ -521,7 +521,7 @@ namespace scene
their geometry because it is their only reason for existence,
for example the OctreeSceneNode.
\param state The culling state to be used. */
void setAutomaticCulling( E_CULLING_TYPE state)
void setAutomaticCulling( u32 state)
{
AutomaticCullingState = state;
}
......@@ -529,9 +529,8 @@ namespace scene
//! Gets the automatic culling state.
/** \return The automatic culling state. */
E_CULLING_TYPE getAutomaticCulling() const
u32 getAutomaticCulling() const
{
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
return AutomaticCullingState;
}
......@@ -687,7 +686,7 @@ namespace scene
out->addVector3d("Scale", getScale() );
out->addBool ("Visible", IsVisible );
out->addEnum ("AutomaticCulling", AutomaticCullingState, AutomaticCullingNames);
out->addInt ("AutomaticCulling", AutomaticCullingState);
out->addInt ("DebugDataVisible", DebugDataVisible );
out->addBool ("IsDebugObject", IsDebugObject );
}
......@@ -712,8 +711,12 @@ namespace scene
setScale(in->getAttributeAsVector3d("Scale"));
IsVisible = in->getAttributeAsBool("Visible");
AutomaticCullingState = (scene::E_CULLING_TYPE) in->getAttributeAsEnumeration("AutomaticCulling",
s32 tmpState = in->getAttributeAsEnumeration("AutomaticCulling",
scene::AutomaticCullingNames);
if (tmpState != -1)
AutomaticCullingState = (u32)tmpState;
else
AutomaticCullingState = in->getAttributeAsInt("AutomaticCulling");
DebugDataVisible = in->getAttributeAsInt("DebugDataVisible");
IsDebugObject = in->getAttributeAsBool("IsDebugObject");
......@@ -825,7 +828,7 @@ namespace scene
s32 ID;
//! Automatic culling state
E_CULLING_TYPE AutomaticCullingState;
u32 AutomaticCullingState;
//! Flag if debug data should be drawn, such as Bounding Boxes.
s32 DebugDataVisible;
......
......@@ -31,7 +31,9 @@ namespace io
namespace scene
{
class IMeshBuffer;
class IMesh;
class IMeshManipulator;
class ISceneNode;
} // end namespace scene
namespace video
......@@ -429,6 +431,43 @@ namespace video
//! Remove all hardware buffers
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.
/** This makes the texture fully transparent at the texels where
this color key can be found when using for example draw2DImage
......@@ -1066,9 +1105,9 @@ namespace video
virtual void setTextureCreationFlag(E_TEXTURE_CREATION_FLAG flag, bool enabled=true) =0;
//! 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.
\return The current texture creation mode. */
\return The current texture creation flag enabled mode. */
virtual bool getTextureCreationFlag(E_TEXTURE_CREATION_FLAG flag) const =0;
//! Creates a software image from a file.
......@@ -1106,8 +1145,8 @@ namespace video
/** Requires that there is a suitable image writer registered
for writing the image.
\param image Image to write.
\param file An already open io::IWriteFile object. The name will be used to determine
the appropriate image writer to use.
\param file An already open io::IWriteFile object. The name
will be used to determine the appropriate image writer to use.
\param param Control parameter for the backend (e.g. compression
level).
\return True on successful write. */
......
......@@ -35,7 +35,8 @@ CD3D9Driver::CD3D9Driver(const core::dimension2d<u32>& screenSize, HWND window,
MaxTextureUnits(0), MaxUserClipPlanes(0),
MaxLightDistance(0.f), LastSetLight(-1), Cached2DModeSignature(0),
ColorFormat(ECF_A8R8G8B8), DeviceLost(false),
Fullscreen(fullscreen), DriverWasReset(true), AlphaToCoverageSupport(false)
Fullscreen(fullscreen), DriverWasReset(true), OcclusionQuerySupport(false),
AlphaToCoverageSupport(false)
{
#ifdef _DEBUG
setDebugName("CD3D9Driver");
......@@ -72,9 +73,13 @@ CD3D9Driver::~CD3D9Driver()
{
deleteMaterialRenders();
deleteAllTextures();
// drop the main depth buffer
DepthBuffers[0]->drop();
removeAllOcclusionQueries();
removeAllHardwareBuffers();
for (u32 i=0; i<DepthBuffers.size(); ++i)
{
DepthBuffers[i]->drop();
}
DepthBuffers.clear();
// drop d3d9
......@@ -424,6 +429,7 @@ bool CD3D9Driver::initDriver(const core::dimension2d<u32>& screenSize,
MaxTextureUnits = core::min_((u32)Caps.MaxSimultaneousTextures, MATERIAL_MAX_TEXTURES);
MaxUserClipPlanes = (u32)Caps.MaxUserClipPlanes;
OcclusionQuerySupport=(pID3DDevice->CreateQuery(D3DQUERYTYPE_OCCLUSION, NULL) == S_OK);
if (VendorID==0x10DE)//NVidia
AlphaToCoverageSupport = (pID3D->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
......@@ -636,6 +642,8 @@ bool CD3D9Driver::queryFeature(E_VIDEO_DRIVER_FEATURE feature) const
return (Caps.PrimitiveMiscCaps & D3DPMISCCAPS_INDEPENDENTWRITEMASKS) != 0;
case EVDF_MRT_BLEND:
return (Caps.PrimitiveMiscCaps & D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING) != 0;
case EVDF_OCCLUSION_QUERY:
return OcclusionQuerySupport;
default:
return false;
};
......@@ -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
void CD3D9Driver::drawVertexPrimitiveList(const void* vertices,
u32 vertexCount, const void* indexList, u32 primitiveCount,
......@@ -2733,9 +2837,17 @@ bool CD3D9Driver::reset()
}
for (i=0; i<DepthBuffers.size(); ++i)
{
if(DepthBuffers[i]->Surface)
if (DepthBuffers[i]->Surface)
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
// automatically in the next render cycle.
removeAllHardwareBuffers();
......@@ -2781,6 +2893,10 @@ bool CD3D9Driver::reset()
&(DepthBuffers[i]->Surface),
NULL);
}
for (i=0; i<OcclusionQueries.size(); ++i)
{
pID3DDevice->CreateQuery(D3DQUERYTYPE_OCCLUSION, reinterpret_cast<IDirect3DQuery9**>(&OcclusionQueries[i].ID));
}
if (FAILED(hr))
{
......
......@@ -116,6 +116,30 @@ namespace video
//! Draw hardware buffer
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
virtual void drawVertexPrimitiveList(const void* vertices, u32 vertexCount,
const void* indexList, u32 primitiveCount,
......@@ -432,6 +456,7 @@ namespace video
bool DeviceLost;
bool Fullscreen;
bool DriverWasReset;
bool OcclusionQuerySupport;
bool AlphaToCoverageSupport;
};
......
......@@ -11,6 +11,7 @@
#include "IImageLoader.h"
#include "IImageWriter.h"
#include "IMaterialRenderer.h"
#include "IAnimatedMeshSceneNode.h"
#include "CMeshManipulator.h"
#include "CColorConverter.h"
......@@ -274,6 +275,7 @@ bool CNullDriver::endScene()
{
FPSCounter.registerFrame(os::Timer::getRealTime(), PrimitivesDrawn);
updateAllHardwareBuffers();
updateAllOcclusionQueries();
return true;
}
......@@ -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
//! the window was resized.
void CNullDriver::OnResize(const core::dimension2d<u32>& size)
......
......@@ -13,7 +13,9 @@
#include "irrString.h"
#include "irrMap.h"
#include "IAttributes.h"
#include "IMesh.h"
#include "IMeshBuffer.h"
#include "IMeshSceneNode.h"
#include "CFPSCounter.h"
#include "S3DVertex.h"
#include "SVertexIndex.h"
......@@ -395,18 +397,19 @@ namespace video
//! updates hardware buffer if needed (only some drivers can)
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)
virtual void drawHardwareBuffer(SHWBufferLink *HWBuffer) {}
//! Update all hardware buffers, remove unused ones
virtual void updateAllHardwareBuffers();
//! Delete hardware buffer
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
virtual void removeHardwareBuffer(const scene::IMeshBuffer* mb);
......@@ -416,7 +419,43 @@ namespace video
//! is vbo recommended on this mesh?
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.
/** Used to notify the driver that the window was resized. */
virtual void OnResize(const core::dimension2d<u32>& size);
......@@ -675,8 +714,61 @@ namespace video
virtual void regenerateMipMapLevels(void* mipmapData=0) {};
core::dimension2d<u32> size;
};
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::IImageWriter*> SurfaceWriter;
core::array<SLight> Lights;
......
......@@ -591,6 +591,8 @@ COpenGLDriver::~COpenGLDriver()
// textures manually before releasing the dc. Oh how I love this.
deleteAllTextures();
removeAllOcclusionQueries();
removeAllHardwareBuffers();
#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
if (DeviceType == EIDT_WIN32)
......@@ -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
static inline u8* buffer_offset(const long offset)
{
......@@ -2326,6 +2423,7 @@ bool COpenGLDriver::testGLError()
os::Printer::log("GL_INVALID_FRAMEBUFFER_OPERATION", ELL_ERROR); break;
#endif
};
_IRR_DEBUG_BREAK_IF(true);
return true;
#else
return false;
......
......@@ -99,6 +99,30 @@ namespace video
//! Draw hardware buffer
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
virtual void drawVertexPrimitiveList(const void* vertices, u32 vertexCount,
const void* indexList, u32 primitiveCount,
......
......@@ -23,7 +23,8 @@ COpenGLExtensionHandler::COpenGLExtensionHandler() :
MaxUserClipPlanes(0), MaxAuxBuffers(0),
MaxMultipleRenderTargets(1), MaxIndices(65535),
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_
,pGlActiveTextureARB(0), pGlClientActiveTextureARB(0),
pGlGenProgramsARB(0), pGlGenProgramsNV(0),
......@@ -663,6 +664,18 @@ void COpenGLExtensionHandler::initExtensions(bool stencilBuffer)
}
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
if (FeatureAvailable[IRR_NVX_gpu_memory_info])
{
......@@ -744,6 +757,8 @@ bool COpenGLExtensionHandler::queryFeature(E_VIDEO_DRIVER_FEATURE feature) const
return FeatureAvailable[IRR_EXT_draw_buffers2];
case EVDF_MRT_BLEND_FUNC:
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:
return false;
};
......
......@@ -865,6 +865,8 @@ class COpenGLExtensionHandler
//! GLSL version as Integer: 100*Major+Minor
u16 ShaderLanguageVersion;
bool OcclusionQuerySupport;
// public access to the (loaded) extensions.
// general functions
void extGlActiveTexture(GLenum texture);
......
......@@ -1131,61 +1131,62 @@ bool CSceneManager::isCulled(const ISceneNode* node) const
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
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 ?
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() ));
}
result = (Driver->getOcclusionQueryResult(const_cast<ISceneNode*>(node))==0);
}
// can be seen by a bounding sphere
case scene::EAC_FRUSTUM_SPHERE:
{ // requires bbox diameter
}
break;
// can be seen by a bounding box ?
if (!result && (node->getAutomaticCulling() & scene::EAC_BOX))
{
core::aabbox3d<f32> tbox = node->getBoundingBox();
node->getAbsoluteTransformation().transformBoxEx(tbox);
result = !(tbox.intersectsWithBox(cam->getViewFrustum()->getBoundingBox() ));
}
// can be seen by cam pyramid planes ?
case scene::EAC_FRUSTUM_BOX:
{
SViewFrustum frust = *cam->getViewFrustum();
// can be seen by a bounding sphere
if (!result && (node->getAutomaticCulling() & scene::EAC_FRUSTUM_SPHERE))
{ // requires bbox diameter
}
// 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
core::matrix4 invTrans(node->getAbsoluteTransformation(), core::matrix4::EM4CONST_INVERSE);
//invTrans.makeInverse();
frust.transform(invTrans);
//transform the frustum to the node's current absolute transformation
core::matrix4 invTrans(node->getAbsoluteTransformation(), core::matrix4::EM4CONST_INVERSE);
//invTrans.makeInverse();
frust.transform(invTrans);
core::vector3df edges[8];
node->getBoundingBox().getEdges(edges);
core::vector3df edges[8];
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;
for (u32 j=0; j<8; ++j)
if (frust.planes[i].classifyPointRelation(edges[j]) != core::ISREL3D_FRONT)
{
if (frust.planes[i].classifyPointRelation(edges[j]) != core::ISREL3D_FRONT)
{
boxInFrustum=true;
break;
}
boxInFrustum=true;
break;
}
}
if (!boxInFrustum)
return true;
if (!boxInFrustum)
{
result = true;
break;
}
}
break;
case scene::EAC_OFF:
break;
}
_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