diff --git a/WickedEngine/Editor.cpp b/WickedEngine/Editor.cpp index 96dfaebf5..03300029d 100644 --- a/WickedEngine/Editor.cpp +++ b/WickedEngine/Editor.cpp @@ -64,6 +64,7 @@ void Editor::Initialize() //wiRenderer::SetPointLightShadowProps(3, 512); //wiRenderer::SetSpotLightShadowProps(3, 512); wiRenderer::physicsEngine = new wiBULLET(); + wiRenderer::SetOcclusionCullingEnabled(true); wiHairParticle::Settings(400, 1000, 2000); diff --git a/WickedEngine/wiGraphicsDevice_DX11.cpp b/WickedEngine/wiGraphicsDevice_DX11.cpp index c51403b12..1a81deedb 100644 --- a/WickedEngine/wiGraphicsDevice_DX11.cpp +++ b/WickedEngine/wiGraphicsDevice_DX11.cpp @@ -2591,8 +2591,6 @@ HRESULT GraphicsDevice_DX11::CreateQuery(const GPUQueryDesc *pDesc, GPUQuery *pQ { HRESULT hr = E_FAIL; -#ifndef _ARM // phone actually crashes on createquery :( - pQuery->desc = *pDesc; pQuery->async_frameshift = pQuery->desc.async_latency; @@ -2630,8 +2628,6 @@ HRESULT GraphicsDevice_DX11::CreateQuery(const GPUQueryDesc *pDesc, GPUQuery *pQ assert(SUCCEEDED(hr) && "GPUQuery creation failed!"); } -#endif // WINSTORE_SUPPORT - return hr; } diff --git a/WickedEngine/wiLoader.cpp b/WickedEngine/wiLoader.cpp index 08184ad97..0e6826af6 100644 --- a/WickedEngine/wiLoader.cpp +++ b/WickedEngine/wiLoader.cpp @@ -3832,14 +3832,8 @@ void Object::init() color = XMFLOAT3(1, 1, 1); trailDistortTex = nullptr; trailTex = nullptr; - skipOcclusionQuery = true; - - GPUQueryDesc desc; - desc.Type = GPU_QUERY_TYPE_OCCLUSION_PREDICATE; - desc.MiscFlags = 0; - desc.async_latency = 1; - wiRenderer::GetDevice()->CreateQuery(&desc, &occlusionQuery); - occlusionQuery.result_passed = TRUE; + occlusionHistory = 0xFFFFFFFF; + occlusionQueryID = -1; } void Object::EmitTrail(const XMFLOAT3& col, float fadeSpeed) { if (mesh != nullptr) @@ -3968,7 +3962,11 @@ int Object::GetRenderTypes() const } bool Object::IsOccluded() const { - return !skipOcclusionQuery && occlusionQuery.result_passed == FALSE; + // Perform a conservative occlusion test: + // If it is visible in any frames in the history, it is determined visible in this frame + // But if all queries failed in the history, it is occluded. + // If it pops up for a frame after occluded, it is visible again for some frames + return ((occlusionQueryID >= 0) && (occlusionHistory & 0xFFFFFFFF) == 0); } XMMATRIX Object::GetOBB() const { diff --git a/WickedEngine/wiLoader.h b/WickedEngine/wiLoader.h index e79885c10..12fa25d88 100644 --- a/WickedEngine/wiLoader.h +++ b/WickedEngine/wiLoader.h @@ -464,9 +464,10 @@ struct Object : public Streamable, public Transform int physicsObjectID; - // occlusion result - wiGraphicsTypes::GPUQuery occlusionQuery; - bool skipOcclusionQuery; + // occlusion result history bitfield (32 bit->32 frame history) + uint32_t occlusionHistory; + // occlusion query pool index + int occlusionQueryID; // Is it deformed with an armature? bool isArmatureDeformed() const diff --git a/WickedEngine/wiRenderer.cpp b/WickedEngine/wiRenderer.cpp index 80e1892e7..a5353cb5a 100644 --- a/WickedEngine/wiRenderer.cpp +++ b/WickedEngine/wiRenderer.cpp @@ -61,11 +61,12 @@ float wiRenderer::SPECULARAA = 0.0f; float wiRenderer::renderTime = 0, wiRenderer::renderTime_Prev = 0, wiRenderer::deltaTime = 0; XMFLOAT2 wiRenderer::temporalAAJitter = XMFLOAT2(0, 0), wiRenderer::temporalAAJitterPrev = XMFLOAT2(0, 0); float wiRenderer::RESOLUTIONSCALE = 1.0f; +GPUQuery wiRenderer::occlusionQueries[]; Texture2D* wiRenderer::enviroMap,*wiRenderer::colorGrading; float wiRenderer::GameSpeed=1,wiRenderer::overrideGameSpeed=1; bool wiRenderer::debugLightCulling = false; -bool wiRenderer::occlusionCulling = true; +bool wiRenderer::occlusionCulling = false; bool wiRenderer::temporalAA = false, wiRenderer::temporalAADEBUG = false; wiRenderer::VoxelizedSceneData wiRenderer::voxelSceneData = VoxelizedSceneData(); int wiRenderer::visibleCount; @@ -2082,6 +2083,8 @@ void wiRenderer::OcclusionCulling_Render(GRAPHICSTHREAD threadID) wiProfiler::GetInstance().BeginRange("Occlusion Culling Render", wiProfiler::DOMAIN_GPU, threadID); + int queryID = 0; + if (!culledRenderer.empty()) { GetDevice()->EventBegin("Occlusion Culling Render", threadID); @@ -2094,10 +2097,12 @@ void wiRenderer::OcclusionCulling_Render(GRAPHICSTHREAD threadID) GetDevice()->BindPS(nullptr, threadID); GetDevice()->BindPrimitiveTopology(PRIMITIVETOPOLOGY::TRIANGLESTRIP, threadID); + int queryID = 0; + for (CulledCollection::const_iterator iter = culledRenderer.begin(); iter != culledRenderer.end(); ++iter) { Mesh* mesh = iter->first; - if (!mesh->renderable || mesh->softBody) // todo: correct softbody + if (!mesh->renderable) { continue; } @@ -2106,25 +2111,30 @@ void wiRenderer::OcclusionCulling_Render(GRAPHICSTHREAD threadID) MiscCB cb; for (Object* instance : visibleInstances) { - GPUQuery& query = instance->occlusionQuery; + if(queryID >= ARRAYSIZE(occlusionQueries)) + { + instance->occlusionQueryID = -1; // assign an invalid id from the pool + continue; + } + + // If a query could be retrieved from the pool for the instance, the instance can be occluded, so render it + GPUQuery& query = occlusionQueries[queryID]; if (!query.IsValid()) { continue; } - if (instance->bounds.intersects(getCamera()->translation)) + if (instance->bounds.intersects(getCamera()->translation) || mesh->softBody) // todo: correct softbody { - // if the camera is inside the bounding box, then the object is most likely visible, so skip occlusion query and mark it as visible: - query.result_passed = true; - instance->skipOcclusionQuery = true; + // camera is inside the instance, mark it as visible in this frame: + instance->occlusionHistory |= 1; } else { - if (instance->skipOcclusionQuery) - { - instance->skipOcclusionQuery = false; - continue; - } + // only query for occlusion if the camera is outside the instance + instance->occlusionQueryID = queryID; // just assign the id from the pool + queryID++; + // previous frame view*projection because these are drawn against the previous depth buffer: cb.mTransform = XMMatrixTranspose(instance->GetOBB()*prevFrameCam->GetViewProjection()); GetDevice()->UpdateBuffer(constantBuffers[CBTYPE_MISC], &cb, threadID); @@ -2170,13 +2180,29 @@ void wiRenderer::OcclusionCulling_Read() for (Object* instance : visibleInstances) { - GPUQuery& query = instance->occlusionQuery; + instance->occlusionHistory <<= 1; // advance history by 1 frame + if (instance->occlusionQueryID < 0) + { + instance->occlusionHistory |= 1; // mark this frame as visible + continue; + } + GPUQuery& query = occlusionQueries[instance->occlusionQueryID]; if (!query.IsValid()) { + instance->occlusionHistory |= 1; // mark this frame as visible continue; } while (!GetDevice()->QueryRead(&query, GRAPHICSTHREAD_IMMEDIATE)) {} + + if (query.result_passed == TRUE) + { + instance->occlusionHistory |= 1; // mark this frame as visible + } + else + { + // leave this frame as occluded + } } } @@ -5636,7 +5662,7 @@ void wiRenderer::RayIntersectMeshes(const RAY& ray, const CulledList& culledObje { continue; } - if (onlyVisible && object->occlusionQuery.result_passed != TRUE) + if (onlyVisible && object->IsOccluded()) { continue; } @@ -6206,3 +6232,27 @@ void wiRenderer::Remove(EnvironmentProbe* value) value->detach(); } } + + +void wiRenderer::SetOcclusionCullingEnabled(bool value) +{ + static bool initialized = false; + + if (!initialized && value == true) + { + initialized = true; + + GPUQueryDesc desc; + desc.Type = GPU_QUERY_TYPE_OCCLUSION_PREDICATE; + desc.MiscFlags = 0; + desc.async_latency = 1; + + for (int i = 0; i < ARRAYSIZE(occlusionQueries); ++i) + { + wiRenderer::GetDevice()->CreateQuery(&desc, &occlusionQueries[i]); + occlusionQueries[i].result_passed = TRUE; + } + } + + occlusionCulling = value; +} diff --git a/WickedEngine/wiRenderer.h b/WickedEngine/wiRenderer.h index b011a3d55..7c5454662 100644 --- a/WickedEngine/wiRenderer.h +++ b/WickedEngine/wiRenderer.h @@ -307,6 +307,8 @@ protected: {} } static voxelSceneData; + static wiGraphicsTypes::GPUQuery occlusionQueries[256]; + public: static std::string SHADERPATH; @@ -356,7 +358,7 @@ public: static bool GetDebugLightCulling() { return debugLightCulling; } static void SetAdvancedLightCulling(bool enabled) { advancedLightCulling = enabled; } static bool GetAdvancedLightCulling() { return advancedLightCulling; } - static void SetOcclusionCullingEnabled(bool enabled) { occlusionCulling = enabled; } + static void SetOcclusionCullingEnabled(bool enabled); // also inits query pool! static bool GetOcclusionCullingEnabled() { return occlusionCulling; } static void SetTemporalAAEnabled(bool enabled) { temporalAA = enabled; } static bool GetTemporalAAEnabled() { return temporalAA; } diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index e41b32b03..09f06f8fc 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wiVersion // minor features, major updates const int minor = 11; // minor bug fixes, alterations, refactors, updates - const int revision = 60; + const int revision = 61; long GetVersion()