Merge pull request #1110 from aws-lumberyard-dev/Atom/dmcdiar/ATOM-15517
[ATOM-15517] Software Occlusion Culling
This commit is contained in:
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"materialType": "Materials\\Types\\StandardPBR.materialtype",
|
||||
"propertyLayoutVersion": 3,
|
||||
"properties": {
|
||||
"general": {
|
||||
"enableShadows": false,
|
||||
"enableDirectionalLights": false,
|
||||
"enablePunctualLights": false,
|
||||
"enableAreaLights": false,
|
||||
"enableIBL": true
|
||||
},
|
||||
"baseColor": {
|
||||
"color": [ 0.0, 1.0, 0.0 ]
|
||||
},
|
||||
"opacity": {
|
||||
"alphaSource": "None",
|
||||
"doubleSided": true,
|
||||
"factor": 0.25,
|
||||
"mode": "TintedTransparent"
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"materialType": "Materials\\Types\\StandardPBR.materialtype",
|
||||
"propertyLayoutVersion": 3,
|
||||
"properties": {
|
||||
"general": {
|
||||
"enableShadows": false,
|
||||
"enableDirectionalLights": false,
|
||||
"enablePunctualLights": false,
|
||||
"enableAreaLights": false,
|
||||
"enableIBL": true
|
||||
},
|
||||
"baseColor": {
|
||||
"color": [ 0.0, 1.0, 0.0 ]
|
||||
},
|
||||
"opacity": {
|
||||
"alphaSource": "None",
|
||||
"doubleSided": true,
|
||||
"factor": 1.0,
|
||||
"mode": "TintedTransparent"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:75cdf73fcb9698a76a38294a1cf927a4fb41a34869e0429e1f02bf8d361a7258
|
||||
size 20400
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/Math/Transform.h>
|
||||
#include <Atom/RPI.Public/FeatureProcessor.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
class OcclusionCullingPlane;
|
||||
|
||||
using OcclusionCullingPlaneHandle = AZStd::shared_ptr<OcclusionCullingPlane>;
|
||||
|
||||
// OcclusionCullingPlaneFeatureProcessorInterface provides an interface to the feature processor for code outside of Atom
|
||||
class OcclusionCullingPlaneFeatureProcessorInterface
|
||||
: public RPI::FeatureProcessor
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(AZ::Render::OcclusionCullingPlaneFeatureProcessorInterface, "{50F6B45E-A622-44EC-B962-DE25FBD44095}");
|
||||
|
||||
virtual OcclusionCullingPlaneHandle AddOcclusionCullingPlane(const AZ::Transform& transform) = 0;
|
||||
virtual void RemoveOcclusionCullingPlane(OcclusionCullingPlaneHandle& handle) = 0;
|
||||
virtual bool IsValidOcclusionCullingPlaneHandle(const OcclusionCullingPlaneHandle& occlusionCullingPlane) const = 0;
|
||||
virtual void SetTransform(const OcclusionCullingPlaneHandle& occlusionCullingPlane, const AZ::Transform& transform) = 0;
|
||||
virtual void SetEnabled(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool enabled) = 0;
|
||||
virtual void ShowVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool showVisualization) = 0;
|
||||
virtual void SetTransparentVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool transparentVisualization) = 0;
|
||||
};
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
@@ -103,6 +103,7 @@
|
||||
#include <ReflectionScreenSpace/ReflectionScreenSpaceBlurPass.h>
|
||||
#include <ReflectionScreenSpace/ReflectionScreenSpaceBlurChildPass.h>
|
||||
#include <ReflectionScreenSpace/ReflectionCopyFrameBufferPass.h>
|
||||
#include <OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
@@ -138,6 +139,7 @@ namespace AZ
|
||||
DiffuseProbeGridFeatureProcessor::Reflect(context);
|
||||
DiffuseGlobalIlluminationFeatureProcessor::Reflect(context);
|
||||
RayTracingFeatureProcessor::Reflect(context);
|
||||
OcclusionCullingPlaneFeatureProcessor::Reflect(context);
|
||||
|
||||
if (SerializeContext* serialize = azrtti_cast<SerializeContext*>(context))
|
||||
{
|
||||
@@ -195,6 +197,7 @@ namespace AZ
|
||||
AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor<DiffuseProbeGridFeatureProcessor>();
|
||||
AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor<DiffuseGlobalIlluminationFeatureProcessor>();
|
||||
AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor<RayTracingFeatureProcessor>();
|
||||
AZ::RPI::FeatureProcessorFactory::Get()->RegisterFeatureProcessor<OcclusionCullingPlaneFeatureProcessor>();
|
||||
|
||||
// Add SkyBox pass
|
||||
auto* passSystem = RPI::PassSystemInterface::Get();
|
||||
@@ -301,6 +304,7 @@ namespace AZ
|
||||
AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor<SkyBoxFeatureProcessor>();
|
||||
AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor<TransformServiceFeatureProcessor>();
|
||||
AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor<AuxGeomFeatureProcessor>();
|
||||
AZ::RPI::FeatureProcessorFactory::Get()->UnregisterFeatureProcessor<OcclusionCullingPlaneFeatureProcessor>();
|
||||
}
|
||||
|
||||
void CommonSystemComponent::LoadPassTemplateMappings()
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h>
|
||||
#include <AzCore/Math/Random.h>
|
||||
#include <Atom/RPI.Public/Scene.h>
|
||||
#include <Atom/RPI.Reflect/Asset/AssetUtils.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
static const char* OcclusionCullingPlaneDrawListTag("occlusioncullingplanevisualization");
|
||||
|
||||
OcclusionCullingPlane::~OcclusionCullingPlane()
|
||||
{
|
||||
Data::AssetBus::MultiHandler::BusDisconnect();
|
||||
m_meshFeatureProcessor->ReleaseMesh(m_visualizationMeshHandle);
|
||||
}
|
||||
|
||||
void OcclusionCullingPlane::Init(RPI::Scene* scene)
|
||||
{
|
||||
AZ_Assert(scene, "OcclusionCullingPlane::Init called with a null Scene pointer");
|
||||
|
||||
m_meshFeatureProcessor = scene->GetFeatureProcessor<Render::MeshFeatureProcessorInterface>();
|
||||
|
||||
// load visualization plane model and material
|
||||
m_visualizationModelAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::ModelAsset>(
|
||||
"Models/OcclusionCullingPlane.azmodel",
|
||||
AZ::RPI::AssetUtils::TraceLevel::Assert);
|
||||
|
||||
m_visualizationMeshHandle = m_meshFeatureProcessor->AcquireMesh(m_visualizationModelAsset);
|
||||
m_meshFeatureProcessor->SetExcludeFromReflectionCubeMaps(m_visualizationMeshHandle, true);
|
||||
m_meshFeatureProcessor->SetRayTracingEnabled(m_visualizationMeshHandle, false);
|
||||
m_meshFeatureProcessor->SetTransform(m_visualizationMeshHandle, AZ::Transform::CreateIdentity());
|
||||
|
||||
SetVisualizationMaterial();
|
||||
}
|
||||
|
||||
void OcclusionCullingPlane::SetVisualizationMaterial()
|
||||
{
|
||||
AZStd::string materialAssetPath;
|
||||
if (m_transparentVisualization)
|
||||
{
|
||||
materialAssetPath = "Materials/OcclusionCullingPlane/OcclusionCullingPlaneTransparentVisualization.azmaterial";
|
||||
}
|
||||
else
|
||||
{
|
||||
materialAssetPath = "Materials/OcclusionCullingPlane/OcclusionCullingPlaneVisualization.azmaterial";
|
||||
}
|
||||
|
||||
RPI::AssetUtils::TraceLevel traceLevel = AZ::RPI::AssetUtils::TraceLevel::Assert;
|
||||
m_visualizationMaterialAsset = AZ::RPI::AssetUtils::GetAssetByProductPath<AZ::RPI::MaterialAsset>(materialAssetPath.c_str(), traceLevel);
|
||||
m_visualizationMaterialAsset.QueueLoad();
|
||||
Data::AssetBus::MultiHandler::BusConnect(m_visualizationMaterialAsset.GetId());
|
||||
}
|
||||
|
||||
void OcclusionCullingPlane::OnAssetReady(Data::Asset<Data::AssetData> asset)
|
||||
{
|
||||
if (m_visualizationMaterialAsset.GetId() == asset.GetId())
|
||||
{
|
||||
m_visualizationMaterialAsset = asset;
|
||||
Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId());
|
||||
|
||||
m_visualizationMaterial = AZ::RPI::Material::FindOrCreate(m_visualizationMaterialAsset);
|
||||
m_meshFeatureProcessor->SetMaterialAssignmentMap(m_visualizationMeshHandle, m_visualizationMaterial);
|
||||
}
|
||||
}
|
||||
|
||||
void OcclusionCullingPlane::OnAssetError(Data::Asset<Data::AssetData> asset)
|
||||
{
|
||||
AZ_Error("OcclusionCullingPlane", false, "Failed to load OcclusionCullingPlane visualization asset %s", asset.ToString<AZStd::string>().c_str());
|
||||
Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId());
|
||||
}
|
||||
|
||||
void OcclusionCullingPlane::SetTransform(const AZ::Transform& transform)
|
||||
{
|
||||
m_transform = transform;
|
||||
|
||||
// update visualization plane transform
|
||||
m_meshFeatureProcessor->SetTransform(m_visualizationMeshHandle, transform);
|
||||
}
|
||||
|
||||
void OcclusionCullingPlane::ShowVisualization(bool showVisualization)
|
||||
{
|
||||
if (m_showVisualization != showVisualization)
|
||||
{
|
||||
m_meshFeatureProcessor->SetVisible(m_visualizationMeshHandle, showVisualization);
|
||||
SetVisualizationMaterial();
|
||||
}
|
||||
}
|
||||
|
||||
void OcclusionCullingPlane::SetTransparentVisualization(bool transparentVisualization)
|
||||
{
|
||||
if (m_transparentVisualization != transparentVisualization)
|
||||
{
|
||||
m_transparentVisualization = transparentVisualization;
|
||||
SetVisualizationMaterial();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h>
|
||||
#include <Atom/Feature/Mesh/MeshFeatureProcessorInterface.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
//! This class represents an OcclusionCullingPlane which is used to cull meshes that are inside the view frustum
|
||||
class OcclusionCullingPlane final
|
||||
: public AZ::Data::AssetBus::MultiHandler
|
||||
{
|
||||
public:
|
||||
OcclusionCullingPlane() = default;
|
||||
~OcclusionCullingPlane();
|
||||
|
||||
void Init(RPI::Scene* scene);
|
||||
|
||||
void SetTransform(const AZ::Transform& transform);
|
||||
const AZ::Transform& GetTransform() const { return m_transform; }
|
||||
|
||||
void SetEnabled(bool enabled) { m_enabled = enabled; }
|
||||
bool GetEnabled() const { return m_enabled; }
|
||||
|
||||
// enables or disables rendering of the visualization plane
|
||||
void ShowVisualization(bool showVisualization);
|
||||
|
||||
// sets the visualization to transparent mode
|
||||
void SetTransparentVisualization(bool transparentVisualization);
|
||||
|
||||
private:
|
||||
|
||||
void SetVisualizationMaterial();
|
||||
|
||||
// AZ::Data::AssetBus::Handler overrides...
|
||||
void OnAssetReady(Data::Asset<Data::AssetData> asset) override;
|
||||
void OnAssetError(Data::Asset<Data::AssetData> asset) override;
|
||||
|
||||
AZ::Transform m_transform;
|
||||
bool m_enabled = true;
|
||||
bool m_showVisualization = true;
|
||||
bool m_transparentVisualization = false;
|
||||
|
||||
// visualization
|
||||
AZ::Render::MeshFeatureProcessorInterface* m_meshFeatureProcessor = nullptr;
|
||||
Data::Asset<RPI::ModelAsset> m_visualizationModelAsset;
|
||||
Data::Asset<RPI::MaterialAsset> m_visualizationMaterialAsset;
|
||||
Data::Instance<RPI::Material> m_visualizationMaterial;
|
||||
AZ::Render::MeshFeatureProcessorInterface::MeshHandle m_visualizationMeshHandle;
|
||||
};
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h>
|
||||
#include <AzCore/std/smart_ptr/make_shared.h>
|
||||
#include <AzCore/std/smart_ptr/intrusive_ptr.h>
|
||||
#include <Atom/RPI.Public/Scene.h>
|
||||
#include <Atom/RPI.Public/Culling.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
void OcclusionCullingPlaneFeatureProcessor::Reflect(ReflectContext* context)
|
||||
{
|
||||
if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
|
||||
{
|
||||
serializeContext
|
||||
->Class<OcclusionCullingPlaneFeatureProcessor, FeatureProcessor>()
|
||||
->Version(0);
|
||||
}
|
||||
}
|
||||
|
||||
void OcclusionCullingPlaneFeatureProcessor::Activate()
|
||||
{
|
||||
m_occlusionCullingPlanes.reserve(InitialOcclusionCullingPlanesAllocationSize);
|
||||
m_rpiOcclusionPlanes.reserve(InitialOcclusionCullingPlanesAllocationSize);
|
||||
|
||||
EnableSceneNotification();
|
||||
}
|
||||
|
||||
void OcclusionCullingPlaneFeatureProcessor::Deactivate()
|
||||
{
|
||||
AZ_Warning("OcclusionCullingPlaneFeatureProcessor", m_occlusionCullingPlanes.size() == 0,
|
||||
"Deactivating the OcclusionCullingPlaneFeatureProcessor, but there are still outstanding occlusion planes. Components\n"
|
||||
"using OcclusionCullingPlaneHandles should free them before the OcclusionCullingPlaneFeatureProcessor is deactivated.\n"
|
||||
);
|
||||
|
||||
DisableSceneNotification();
|
||||
}
|
||||
|
||||
void OcclusionCullingPlaneFeatureProcessor::OnBeginPrepareRender()
|
||||
{
|
||||
if (m_rpiListNeedsUpdate)
|
||||
{
|
||||
// rebuild the RPI occlusion list
|
||||
m_rpiOcclusionPlanes.clear();
|
||||
|
||||
for (auto& occlusionCullingPlane : m_occlusionCullingPlanes)
|
||||
{
|
||||
if (!occlusionCullingPlane->GetEnabled())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
RPI::CullingScene::OcclusionPlane rpiOcclusionPlane;
|
||||
|
||||
static const Vector3 BL = Vector3(-0.5f, 0.0f, -0.5f);
|
||||
static const Vector3 TL = Vector3(-0.5f, 0.0f, 0.5f);
|
||||
static const Vector3 TR = Vector3( 0.5f, 0.0f, 0.5f);
|
||||
static const Vector3 BR = Vector3( 0.5f, 0.0f, -0.5f);
|
||||
|
||||
const AZ::Transform& transform = occlusionCullingPlane->GetTransform();
|
||||
|
||||
// convert corners to world space
|
||||
rpiOcclusionPlane.m_cornerBL = transform.TransformPoint(BL);
|
||||
rpiOcclusionPlane.m_cornerTL = transform.TransformPoint(TL);
|
||||
rpiOcclusionPlane.m_cornerTR = transform.TransformPoint(TR);
|
||||
rpiOcclusionPlane.m_cornerBR = transform.TransformPoint(BR);
|
||||
|
||||
// build world space AABB
|
||||
AZ::Vector3 aabbMin = rpiOcclusionPlane.m_cornerBL.GetMin(rpiOcclusionPlane.m_cornerTR);
|
||||
AZ::Vector3 aabbMax = rpiOcclusionPlane.m_cornerBL.GetMax(rpiOcclusionPlane.m_cornerTR);
|
||||
rpiOcclusionPlane.m_aabb = Aabb::CreateFromMinMax(aabbMin, aabbMax);
|
||||
|
||||
m_rpiOcclusionPlanes.push_back(rpiOcclusionPlane);
|
||||
}
|
||||
|
||||
GetParentScene()->GetCullingScene()->SetOcclusionPlanes(m_rpiOcclusionPlanes);
|
||||
|
||||
m_rpiListNeedsUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
OcclusionCullingPlaneHandle OcclusionCullingPlaneFeatureProcessor::AddOcclusionCullingPlane(const AZ::Transform& transform)
|
||||
{
|
||||
AZStd::shared_ptr<OcclusionCullingPlane> occlusionCullingPlane = AZStd::make_shared<OcclusionCullingPlane>();
|
||||
occlusionCullingPlane->Init(GetParentScene());
|
||||
occlusionCullingPlane->SetTransform(transform);
|
||||
m_occlusionCullingPlanes.push_back(occlusionCullingPlane);
|
||||
m_rpiListNeedsUpdate = true;
|
||||
|
||||
return occlusionCullingPlane;
|
||||
}
|
||||
|
||||
void OcclusionCullingPlaneFeatureProcessor::RemoveOcclusionCullingPlane(OcclusionCullingPlaneHandle& occlusionCullingPlane)
|
||||
{
|
||||
AZ_Assert(occlusionCullingPlane.get(), "RemoveOcclusionCullingPlane called with an invalid handle");
|
||||
|
||||
auto itEntry = AZStd::find_if(m_occlusionCullingPlanes.begin(), m_occlusionCullingPlanes.end(), [&](AZStd::shared_ptr<OcclusionCullingPlane> const& entry)
|
||||
{
|
||||
return (entry == occlusionCullingPlane);
|
||||
});
|
||||
|
||||
AZ_Assert(itEntry != m_occlusionCullingPlanes.end(), "RemoveOcclusionCullingPlane called with an occlusion plane that is not in the occlusion plane list");
|
||||
m_occlusionCullingPlanes.erase(itEntry);
|
||||
occlusionCullingPlane = nullptr;
|
||||
m_rpiListNeedsUpdate = true;
|
||||
}
|
||||
|
||||
void OcclusionCullingPlaneFeatureProcessor::SetTransform(const OcclusionCullingPlaneHandle& occlusionCullingPlane, const AZ::Transform& transform)
|
||||
{
|
||||
AZ_Assert(occlusionCullingPlane.get(), "SetTransform called with an invalid handle");
|
||||
occlusionCullingPlane->SetTransform(transform);
|
||||
m_rpiListNeedsUpdate = true;
|
||||
}
|
||||
|
||||
void OcclusionCullingPlaneFeatureProcessor::SetEnabled(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool enabled)
|
||||
{
|
||||
AZ_Assert(occlusionCullingPlane.get(), "Enable called with an invalid handle");
|
||||
occlusionCullingPlane->SetEnabled(enabled);
|
||||
m_rpiListNeedsUpdate = true;
|
||||
}
|
||||
|
||||
void OcclusionCullingPlaneFeatureProcessor::ShowVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool showVisualization)
|
||||
{
|
||||
AZ_Assert(occlusionCullingPlane.get(), "ShowVisualization called with an invalid handle");
|
||||
occlusionCullingPlane->ShowVisualization(showVisualization);
|
||||
}
|
||||
|
||||
void OcclusionCullingPlaneFeatureProcessor::SetTransparentVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool transparentVisualization)
|
||||
{
|
||||
AZ_Assert(occlusionCullingPlane.get(), "SetTransparentVisualization called with an invalid handle");
|
||||
occlusionCullingPlane->SetTransparentVisualization(transparentVisualization);
|
||||
}
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h>
|
||||
#include <OcclusionCullingPlane/OcclusionCullingPlane.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
//! This class manages OcclusionCullingPlanes which are used to cull meshes that are inside the view frustum
|
||||
class OcclusionCullingPlaneFeatureProcessor final
|
||||
: public OcclusionCullingPlaneFeatureProcessorInterface
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(AZ::Render::OcclusionCullingPlaneFeatureProcessor, "{C3DE91D7-EF7A-4A82-A55F-E22BC52074EA}", OcclusionCullingPlaneFeatureProcessorInterface);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
OcclusionCullingPlaneFeatureProcessor() = default;
|
||||
virtual ~OcclusionCullingPlaneFeatureProcessor() = default;
|
||||
|
||||
// OcclusionCullingPlaneFeatureProcessorInterface overrides
|
||||
OcclusionCullingPlaneHandle AddOcclusionCullingPlane(const AZ::Transform& transform) override;
|
||||
void RemoveOcclusionCullingPlane(OcclusionCullingPlaneHandle& handle) override;
|
||||
bool IsValidOcclusionCullingPlaneHandle(const OcclusionCullingPlaneHandle& occlusionCullingPlane) const override { return (occlusionCullingPlane.get() != nullptr); }
|
||||
void SetTransform(const OcclusionCullingPlaneHandle& occlusionCullingPlane, const AZ::Transform& transform) override;
|
||||
void SetEnabled(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool enable) override;
|
||||
void ShowVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool showVisualization) override;
|
||||
void SetTransparentVisualization(const OcclusionCullingPlaneHandle& occlusionCullingPlane, bool transparentVisualization) override;
|
||||
|
||||
// FeatureProcessor overrides
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
|
||||
// RPI::SceneNotificationBus overrides ...
|
||||
void OnBeginPrepareRender() override;
|
||||
|
||||
// retrieve the full list of occlusion planes
|
||||
using OcclusionCullingPlaneVector = AZStd::vector<AZStd::shared_ptr<OcclusionCullingPlane>>;
|
||||
OcclusionCullingPlaneVector& GetOcclusionCullingPlanes() { return m_occlusionCullingPlanes; }
|
||||
|
||||
private:
|
||||
AZ_DISABLE_COPY_MOVE(OcclusionCullingPlaneFeatureProcessor);
|
||||
|
||||
// list of occlusion planes
|
||||
const size_t InitialOcclusionCullingPlanesAllocationSize = 64;
|
||||
OcclusionCullingPlaneVector m_occlusionCullingPlanes;
|
||||
|
||||
// prebuilt list of RPI scene occlusion planes
|
||||
RPI::CullingScene::OcclusionPlaneVector m_rpiOcclusionPlanes;
|
||||
bool m_rpiListNeedsUpdate = false;
|
||||
};
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
@@ -175,6 +175,10 @@ set(FILES
|
||||
Source/MorphTargets/MorphTargetComputePass.h
|
||||
Source/MorphTargets/MorphTargetDispatchItem.cpp
|
||||
Source/MorphTargets/MorphTargetDispatchItem.h
|
||||
Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.h
|
||||
Source/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessor.cpp
|
||||
Source/OcclusionCullingPlane/OcclusionCullingPlane.h
|
||||
Source/OcclusionCullingPlane/OcclusionCullingPlane.cpp
|
||||
Source/PostProcess/PostProcessBase.cpp
|
||||
Source/PostProcess/PostProcessBase.h
|
||||
Source/PostProcess/PostProcessFeatureProcessor.cpp
|
||||
|
||||
@@ -45,6 +45,7 @@ set(FILES
|
||||
Include/Atom/Feature/ParamMacros/StartParamFunctionsVirtual.inl
|
||||
Include/Atom/Feature/ParamMacros/StartParamMembers.inl
|
||||
Include/Atom/Feature/ParamMacros/StartParamSerializeContext.inl
|
||||
Include/Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h
|
||||
Include/Atom/Feature/PostProcess/PostProcessFeatureProcessorInterface.h
|
||||
Include/Atom/Feature/PostProcess/PostProcessParams.inl
|
||||
Include/Atom/Feature/PostProcess/PostProcessSettings.inl
|
||||
|
||||
@@ -9,6 +9,17 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
|
||||
ly_get_list_relative_pal_filename(pal_source_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/${PAL_PLATFORM_NAME})
|
||||
|
||||
#for PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED and PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
include(${pal_source_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
|
||||
|
||||
if(PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED)
|
||||
set(MASKED_OCCLUSION_CULLING_FILES "atom_rpi_masked_occlusion_files.cmake")
|
||||
else()
|
||||
set(MASKED_OCCLUSION_CULLING_FILES "")
|
||||
endif()
|
||||
|
||||
ly_add_target(
|
||||
NAME Atom_RPI.Public STATIC
|
||||
NAMESPACE Gem
|
||||
@@ -16,11 +27,15 @@ ly_add_target(
|
||||
atom_rpi_reflect_files.cmake
|
||||
atom_rpi_public_files.cmake
|
||||
../Assets/atom_rpi_asset_files.cmake
|
||||
${pal_source_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
|
||||
${MASKED_OCCLUSION_CULLING_FILES}
|
||||
INCLUDE_DIRECTORIES
|
||||
PRIVATE
|
||||
Source
|
||||
${pal_source_dir}
|
||||
PUBLIC
|
||||
Include
|
||||
External
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AtomCore
|
||||
@@ -159,8 +174,6 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
ly_get_list_relative_pal_filename(pal_source_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/${PAL_PLATFORM_NAME})
|
||||
ly_get_list_relative_pal_filename(common_source_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/Common)
|
||||
|
||||
include(${pal_source_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) #for PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED
|
||||
|
||||
if(NOT PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED)
|
||||
|
||||
# Create a stub
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Intel Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
// use this file except in compliance with the License. You may obtain a copy
|
||||
// of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Common shared include file to hide compiler/os specific functions from the rest of the code.
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__)
|
||||
#define __MICROSOFT_COMPILER
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && (defined(_MSC_VER) || defined(__INTEL_COMPILER) || defined(__clang__)) // Windows: MSVC / Intel compiler / clang
|
||||
#include <intrin.h>
|
||||
#include <new.h>
|
||||
|
||||
#define FORCE_INLINE __forceinline
|
||||
|
||||
FORCE_INLINE unsigned long find_clear_lsb(unsigned int *mask)
|
||||
{
|
||||
unsigned long idx;
|
||||
_BitScanForward(&idx, *mask);
|
||||
*mask &= *mask - 1;
|
||||
return idx;
|
||||
}
|
||||
|
||||
FORCE_INLINE void *aligned_alloc(size_t alignment, size_t size)
|
||||
{
|
||||
return _aligned_malloc(size, alignment);
|
||||
}
|
||||
|
||||
FORCE_INLINE void aligned_free(void *ptr)
|
||||
{
|
||||
_aligned_free(ptr);
|
||||
}
|
||||
|
||||
#elif defined(__GNUG__) || defined(__clang__) // G++ or clang
|
||||
#include <cpuid.h>
|
||||
#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
|
||||
#include <malloc/malloc.h> // memalign
|
||||
#else
|
||||
#include <malloc.h> // memalign
|
||||
#endif
|
||||
#include <mm_malloc.h>
|
||||
#include <immintrin.h>
|
||||
#include <new>
|
||||
|
||||
#define FORCE_INLINE inline
|
||||
|
||||
FORCE_INLINE unsigned long find_clear_lsb(unsigned int *mask)
|
||||
{
|
||||
unsigned long idx;
|
||||
idx = __builtin_ctzl(*mask);
|
||||
*mask &= *mask - 1;
|
||||
return idx;
|
||||
}
|
||||
|
||||
FORCE_INLINE void *aligned_alloc(size_t alignment, size_t size)
|
||||
{
|
||||
return memalign(alignment, size);
|
||||
}
|
||||
|
||||
FORCE_INLINE void aligned_free(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
FORCE_INLINE void __cpuidex(int* cpuinfo, int function, int subfunction)
|
||||
{
|
||||
__cpuid_count(function, subfunction, cpuinfo[0], cpuinfo[1], cpuinfo[2], cpuinfo[3]);
|
||||
}
|
||||
|
||||
FORCE_INLINE unsigned long long _xgetbv(unsigned int index)
|
||||
{
|
||||
unsigned int eax, edx;
|
||||
__asm__ __volatile__(
|
||||
"xgetbv;"
|
||||
: "=a" (eax), "=d"(edx)
|
||||
: "c" (index)
|
||||
);
|
||||
return ((unsigned long long)edx << 32) | eax;
|
||||
}
|
||||
|
||||
#else
|
||||
#error Unsupported compiler
|
||||
#endif
|
||||
@@ -0,0 +1,181 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and
|
||||
distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
||||
owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities
|
||||
that control, are controlled by, or are under common control with that entity.
|
||||
For the purposes of this definition, "control" means (i) the power, direct or
|
||||
indirect, to cause the direction or management of such entity, whether by
|
||||
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising
|
||||
permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications, including
|
||||
but not limited to software source code, documentation source, and configuration
|
||||
files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation or
|
||||
translation of a Source form, including but not limited to compiled object code,
|
||||
generated documentation, and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form, made
|
||||
available under the License, as indicated by a copyright notice that is included
|
||||
in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that
|
||||
is based on (or derived from) the Work and for which the editorial revisions,
|
||||
annotations, elaborations, or other modifications represent, as a whole, an
|
||||
original work of authorship. For the purposes of this License, Derivative Works
|
||||
shall not include works that remain separable from, or merely link (or bind by
|
||||
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original version
|
||||
of the Work and any modifications or additions to that Work or Derivative Works
|
||||
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||
on behalf of the copyright owner. For the purposes of this definition,
|
||||
"submitted" means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems, and
|
||||
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||
the purpose of discussing and improving the Work, but excluding communication
|
||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||
owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
||||
of whom a Contribution has been received by Licensor and subsequently
|
||||
incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of this
|
||||
License, each Contributor hereby grants to You a perpetual, worldwide,
|
||||
non-exclusive, no-charge, royalty-free, irrevocable copyright license to
|
||||
reproduce, prepare Derivative Works of, publicly display, publicly perform,
|
||||
sublicense, and distribute the Work and such Derivative Works in Source or
|
||||
Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of this License,
|
||||
each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section) patent
|
||||
license to make, have made, use, offer to sell, sell, import, and otherwise
|
||||
transfer the Work, where such license applies only to those patent claims
|
||||
licensable by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s) with the Work
|
||||
to which such Contribution(s) was submitted. If You institute patent litigation
|
||||
against any entity (including a cross-claim or counterclaim in a lawsuit)
|
||||
alleging that the Work or a Contribution incorporated within the Work
|
||||
constitutes direct or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate as of the date
|
||||
such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the Work or
|
||||
Derivative Works thereof in any medium, with or without modifications, and in
|
||||
Source or Object form, provided that You meet the following conditions:
|
||||
You must give any other recipients of the Work or Derivative Works a copy of
|
||||
this License; and
|
||||
|
||||
|
||||
You must cause any modified files to carry prominent notices stating that You
|
||||
changed the files; and
|
||||
|
||||
|
||||
You must retain, in the Source form of any Derivative Works that You
|
||||
distribute, all copyright, patent, trademark, and attribution notices from the
|
||||
Source form of the Work, excluding those notices that do not pertain to any
|
||||
part of the Derivative Works; and
|
||||
|
||||
|
||||
If the Work includes a "NOTICE" text file as part of its distribution, then
|
||||
any Derivative Works that You distribute must include a readable copy of the
|
||||
attribution notices contained within such NOTICE file, excluding those notices
|
||||
that do not pertain to any part of the Derivative Works, in at least one of
|
||||
the following places: within a NOTICE text file distributed as part of the
|
||||
Derivative Works; within the Source form or documentation, if provided along
|
||||
with the Derivative Works; or, within a display generated by the Derivative
|
||||
Works, if and wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and do not modify the
|
||||
License. You may add Your own attribution notices within Derivative Works that
|
||||
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||
provided that such additional attribution notices cannot be construed as
|
||||
modifying the License.
|
||||
You may add Your own copyright statement to Your modifications and may provide
|
||||
additional or different license terms and conditions for use, reproduction, or
|
||||
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||
with the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise, any
|
||||
Contribution intentionally submitted for inclusion in the Work by You to the
|
||||
Licensor shall be under the terms and conditions of this License, without any
|
||||
additional terms or conditions. Notwithstanding the above, nothing herein shall
|
||||
supersede or modify the terms of any separate license agreement you may have
|
||||
executed with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade names,
|
||||
trademarks, service marks, or product names of the Licensor, except as required
|
||||
for reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in
|
||||
writing, Licensor provides the Work (and each Contributor provides its
|
||||
Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied, including, without limitation, any warranties
|
||||
or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any risks
|
||||
associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory, whether in
|
||||
tort (including negligence), contract, or otherwise, unless required by
|
||||
applicable law (such as deliberate and grossly negligent acts) or agreed to in
|
||||
writing, shall any Contributor be liable to You for damages, including any
|
||||
direct, indirect, special, incidental, or consequential damages of any character
|
||||
arising as a result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill, work stoppage,
|
||||
computer failure or malfunction, or any and all other commercial damages or
|
||||
losses), even if such Contributor has been advised of the possibility of such
|
||||
damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing the Work or
|
||||
Derivative Works thereof, You may choose to offer, and charge a fee for,
|
||||
acceptance of support, warranty, indemnity, or other liability obligations
|
||||
and/or rights consistent with this License. However, in accepting such
|
||||
obligations, You may act only on Your own behalf and on Your sole
|
||||
responsibility, not on behalf of any other Contributor, and only if You agree to
|
||||
indemnify, defend, and hold each Contributor harmless for any liability incurred
|
||||
by, or claims asserted against, such Contributor by reason of your accepting any
|
||||
such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work
|
||||
|
||||
To apply the Apache License to your work, attach the following boilerplate
|
||||
notice, with the fields enclosed by brackets "[]" replaced with your own
|
||||
identifying information. (Don't include the brackets!) The text should be
|
||||
enclosed in the appropriate comment syntax for the file format. We also
|
||||
recommend that a file or class name and description of purpose be included on
|
||||
the same "printed page" as the copyright notice for easier identification within
|
||||
third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner] Licensed under the Apache License,
|
||||
Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or
|
||||
agreed to in writing, software distributed under the License is distributed on
|
||||
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
or implied. See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+456
@@ -0,0 +1,456 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Intel Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
// use this file except in compliance with the License. You may obtain a copy
|
||||
// of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#include <vector>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <float.h>
|
||||
#include "MaskedOcclusionCulling.h"
|
||||
#include "CompilerSpecific.inl"
|
||||
|
||||
#if MOC_RECORDER_ENABLE
|
||||
#include "FrameRecorder.h"
|
||||
#endif
|
||||
|
||||
#if defined(__AVX__) || defined(__AVX2__)
|
||||
// For performance reasons, the MaskedOcclusionCullingAVX2/512.cpp files should be compiled with VEX encoding for SSE instructions (to avoid
|
||||
// AVX-SSE transition penalties, see https://software.intel.com/en-us/articles/avoiding-avx-sse-transition-penalties). However, this file
|
||||
// _must_ be compiled without VEX encoding to allow backwards compatibility. Best practice is to use lowest supported target platform
|
||||
// (/arch:SSE2) as project default, and elevate only the MaskedOcclusionCullingAVX2/512.cpp files.
|
||||
#error The MaskedOcclusionCulling.cpp should be compiled with lowest supported target platform, e.g. /arch:SSE2
|
||||
#endif
|
||||
|
||||
static MaskedOcclusionCulling::Implementation DetectCPUFeatures(MaskedOcclusionCulling::pfnAlignedAlloc alignedAlloc, MaskedOcclusionCulling::pfnAlignedFree alignedFree)
|
||||
{
|
||||
struct CpuInfo { int regs[4]; };
|
||||
|
||||
// Get regular CPUID values
|
||||
int regs[4];
|
||||
__cpuidex(regs, 0, 0);
|
||||
|
||||
// MOCVectorAllocator<CpuInfo> mocalloc( alignedAlloc, alignedFree );
|
||||
// std::vector<CpuInfo, MOCVectorAllocator<CpuInfo>> cpuId( mocalloc ), cpuIdEx( mocalloc );
|
||||
// cpuId.resize( regs[0] );
|
||||
size_t cpuIdCount = regs[0];
|
||||
CpuInfo * cpuId = (CpuInfo*)alignedAlloc( 64, sizeof(CpuInfo) * cpuIdCount );
|
||||
|
||||
for (size_t i = 0; i < cpuIdCount; ++i)
|
||||
__cpuidex(cpuId[i].regs, (int)i, 0);
|
||||
|
||||
// Get extended CPUID values
|
||||
__cpuidex(regs, 0x80000000, 0);
|
||||
|
||||
//cpuIdEx.resize(regs[0] - 0x80000000);
|
||||
size_t cpuIdExCount = regs[0] - 0x80000000;
|
||||
CpuInfo * cpuIdEx = (CpuInfo*)alignedAlloc( 64, sizeof( CpuInfo ) * cpuIdExCount );
|
||||
|
||||
for (size_t i = 0; i < cpuIdExCount; ++i)
|
||||
__cpuidex(cpuIdEx[i].regs, 0x80000000 + (int)i, 0);
|
||||
|
||||
#define TEST_BITS(A, B) (((A) & (B)) == (B))
|
||||
#define TEST_FMA_MOVE_OXSAVE (cpuIdCount >= 1 && TEST_BITS(cpuId[1].regs[2], (1 << 12) | (1 << 22) | (1 << 27)))
|
||||
#define TEST_LZCNT (cpuIdExCount >= 1 && TEST_BITS(cpuIdEx[1].regs[2], 0x20))
|
||||
#define TEST_SSE41 (cpuIdCount >= 1 && TEST_BITS(cpuId[1].regs[2], (1 << 19)))
|
||||
#define TEST_XMM_YMM (cpuIdCount >= 1 && TEST_BITS(_xgetbv(0), (1 << 2) | (1 << 1)))
|
||||
#define TEST_OPMASK_ZMM (cpuIdCount >= 1 && TEST_BITS(_xgetbv(0), (1 << 7) | (1 << 6) | (1 << 5)))
|
||||
#define TEST_BMI1_BMI2_AVX2 (cpuIdCount >= 7 && TEST_BITS(cpuId[7].regs[1], (1 << 3) | (1 << 5) | (1 << 8)))
|
||||
#define TEST_AVX512_F_BW_DQ (cpuIdCount >= 7 && TEST_BITS(cpuId[7].regs[1], (1 << 16) | (1 << 17) | (1 << 30)))
|
||||
|
||||
MaskedOcclusionCulling::Implementation retVal = MaskedOcclusionCulling::SSE2;
|
||||
if (TEST_FMA_MOVE_OXSAVE && TEST_LZCNT && TEST_SSE41)
|
||||
{
|
||||
if (TEST_XMM_YMM && TEST_OPMASK_ZMM && TEST_BMI1_BMI2_AVX2 && TEST_AVX512_F_BW_DQ)
|
||||
retVal = MaskedOcclusionCulling::AVX512;
|
||||
else if (TEST_XMM_YMM && TEST_BMI1_BMI2_AVX2)
|
||||
retVal = MaskedOcclusionCulling::AVX2;
|
||||
}
|
||||
else if (TEST_SSE41)
|
||||
retVal = MaskedOcclusionCulling::SSE41;
|
||||
alignedFree( cpuId );
|
||||
alignedFree( cpuIdEx );
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Utility functions (not directly related to the algorithm/rasterizer)
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void MaskedOcclusionCulling::TransformVertices(const float *mtx, const float *inVtx, float *xfVtx, unsigned int nVtx, const VertexLayout &vtxLayout)
|
||||
{
|
||||
// This function pretty slow, about 10-20% slower than if the vertices are stored in aligned SOA form.
|
||||
if (nVtx == 0)
|
||||
return;
|
||||
|
||||
// Load matrix and swizzle out the z component. For post-multiplication (OGL), the matrix is assumed to be column
|
||||
// major, with one column per SSE register. For pre-multiplication (DX), the matrix is assumed to be row major.
|
||||
__m128 mtxCol0 = _mm_loadu_ps(mtx);
|
||||
__m128 mtxCol1 = _mm_loadu_ps(mtx + 4);
|
||||
__m128 mtxCol2 = _mm_loadu_ps(mtx + 8);
|
||||
__m128 mtxCol3 = _mm_loadu_ps(mtx + 12);
|
||||
|
||||
int stride = vtxLayout.mStride;
|
||||
const char *vPtr = (const char *)inVtx;
|
||||
float *outPtr = xfVtx;
|
||||
|
||||
// Iterate through all vertices and transform
|
||||
for (unsigned int vtx = 0; vtx < nVtx; ++vtx)
|
||||
{
|
||||
__m128 xVal = _mm_load1_ps((float*)(vPtr));
|
||||
__m128 yVal = _mm_load1_ps((float*)(vPtr + vtxLayout.mOffsetY));
|
||||
__m128 zVal = _mm_load1_ps((float*)(vPtr + vtxLayout.mOffsetZ));
|
||||
|
||||
__m128 xform = _mm_add_ps(_mm_mul_ps(mtxCol0, xVal), _mm_add_ps(_mm_mul_ps(mtxCol1, yVal), _mm_add_ps(_mm_mul_ps(mtxCol2, zVal), mtxCol3)));
|
||||
_mm_storeu_ps(outPtr, xform);
|
||||
vPtr += stride;
|
||||
outPtr += 4;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Typedefs
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef MaskedOcclusionCulling::pfnAlignedAlloc pfnAlignedAlloc;
|
||||
typedef MaskedOcclusionCulling::pfnAlignedFree pfnAlignedFree;
|
||||
typedef MaskedOcclusionCulling::VertexLayout VertexLayout;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Common SSE2/SSE4.1 defines
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define SIMD_LANES 4
|
||||
#define TILE_HEIGHT_SHIFT 2
|
||||
|
||||
#define SIMD_LANE_IDX _mm_setr_epi32(0, 1, 2, 3)
|
||||
|
||||
#define SIMD_SUB_TILE_COL_OFFSET _mm_setr_epi32(0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3)
|
||||
#define SIMD_SUB_TILE_ROW_OFFSET _mm_setzero_si128()
|
||||
#define SIMD_SUB_TILE_COL_OFFSET_F _mm_setr_ps(0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3)
|
||||
#define SIMD_SUB_TILE_ROW_OFFSET_F _mm_setzero_ps()
|
||||
|
||||
#define SIMD_LANE_YCOORD_I _mm_setr_epi32(128, 384, 640, 896)
|
||||
#define SIMD_LANE_YCOORD_F _mm_setr_ps(128.0f, 384.0f, 640.0f, 896.0f)
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Common SSE2/SSE4.1 functions
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef __m128 __mw;
|
||||
typedef __m128i __mwi;
|
||||
|
||||
#define _mmw_set1_ps _mm_set1_ps
|
||||
#define _mmw_setzero_ps _mm_setzero_ps
|
||||
#define _mmw_and_ps _mm_and_ps
|
||||
#define _mmw_or_ps _mm_or_ps
|
||||
#define _mmw_xor_ps _mm_xor_ps
|
||||
#define _mmw_not_ps(a) _mm_xor_ps((a), _mm_castsi128_ps(_mm_set1_epi32(~0)))
|
||||
#define _mmw_andnot_ps _mm_andnot_ps
|
||||
#define _mmw_neg_ps(a) _mm_xor_ps((a), _mm_set1_ps(-0.0f))
|
||||
#define _mmw_abs_ps(a) _mm_and_ps((a), _mm_castsi128_ps(_mm_set1_epi32(0x7FFFFFFF)))
|
||||
#define _mmw_add_ps _mm_add_ps
|
||||
#define _mmw_sub_ps _mm_sub_ps
|
||||
#define _mmw_mul_ps _mm_mul_ps
|
||||
#define _mmw_div_ps _mm_div_ps
|
||||
#define _mmw_min_ps _mm_min_ps
|
||||
#define _mmw_max_ps _mm_max_ps
|
||||
#define _mmw_movemask_ps _mm_movemask_ps
|
||||
#define _mmw_cmpge_ps(a,b) _mm_cmpge_ps(a, b)
|
||||
#define _mmw_cmpgt_ps(a,b) _mm_cmpgt_ps(a, b)
|
||||
#define _mmw_cmpeq_ps(a,b) _mm_cmpeq_ps(a, b)
|
||||
#define _mmw_fmadd_ps(a,b,c) _mm_add_ps(_mm_mul_ps(a,b), c)
|
||||
#define _mmw_fmsub_ps(a,b,c) _mm_sub_ps(_mm_mul_ps(a,b), c)
|
||||
#define _mmw_shuffle_ps _mm_shuffle_ps
|
||||
#define _mmw_insertf32x4_ps(a,b,c) (b)
|
||||
#define _mmw_cvtepi32_ps _mm_cvtepi32_ps
|
||||
#define _mmw_blendv_epi32(a,b,c) simd_cast<__mwi>(_mmw_blendv_ps(simd_cast<__mw>(a), simd_cast<__mw>(b), simd_cast<__mw>(c)))
|
||||
|
||||
#define _mmw_set1_epi32 _mm_set1_epi32
|
||||
#define _mmw_setzero_epi32 _mm_setzero_si128
|
||||
#define _mmw_and_epi32 _mm_and_si128
|
||||
#define _mmw_or_epi32 _mm_or_si128
|
||||
#define _mmw_xor_epi32 _mm_xor_si128
|
||||
#define _mmw_not_epi32(a) _mm_xor_si128((a), _mm_set1_epi32(~0))
|
||||
#define _mmw_andnot_epi32 _mm_andnot_si128
|
||||
#define _mmw_neg_epi32(a) _mm_sub_epi32(_mm_set1_epi32(0), (a))
|
||||
#define _mmw_add_epi32 _mm_add_epi32
|
||||
#define _mmw_sub_epi32 _mm_sub_epi32
|
||||
#define _mmw_subs_epu16 _mm_subs_epu16
|
||||
#define _mmw_cmpeq_epi32 _mm_cmpeq_epi32
|
||||
#define _mmw_cmpgt_epi32 _mm_cmpgt_epi32
|
||||
#define _mmw_srai_epi32 _mm_srai_epi32
|
||||
#define _mmw_srli_epi32 _mm_srli_epi32
|
||||
#define _mmw_slli_epi32 _mm_slli_epi32
|
||||
#define _mmw_cvtps_epi32 _mm_cvtps_epi32
|
||||
#define _mmw_cvttps_epi32 _mm_cvttps_epi32
|
||||
|
||||
#define _mmx_fmadd_ps _mmw_fmadd_ps
|
||||
#define _mmx_max_epi32 _mmw_max_epi32
|
||||
#define _mmx_min_epi32 _mmw_min_epi32
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SIMD casting functions
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename T, typename Y> FORCE_INLINE T simd_cast(Y A);
|
||||
template<> FORCE_INLINE __m128 simd_cast<__m128>(float A) { return _mm_set1_ps(A); }
|
||||
template<> FORCE_INLINE __m128 simd_cast<__m128>(__m128i A) { return _mm_castsi128_ps(A); }
|
||||
template<> FORCE_INLINE __m128 simd_cast<__m128>(__m128 A) { return A; }
|
||||
template<> FORCE_INLINE __m128i simd_cast<__m128i>(int A) { return _mm_set1_epi32(A); }
|
||||
template<> FORCE_INLINE __m128i simd_cast<__m128i>(__m128 A) { return _mm_castps_si128(A); }
|
||||
template<> FORCE_INLINE __m128i simd_cast<__m128i>(__m128i A) { return A; }
|
||||
|
||||
#define MAKE_ACCESSOR(name, simd_type, base_type, is_const, elements) \
|
||||
FORCE_INLINE is_const base_type * name(is_const simd_type &a) { \
|
||||
union accessor { simd_type m_native; base_type m_array[elements]; }; \
|
||||
is_const accessor *acs = reinterpret_cast<is_const accessor*>(&a); \
|
||||
return acs->m_array; \
|
||||
}
|
||||
|
||||
MAKE_ACCESSOR(simd_f32, __m128, float, , 4)
|
||||
MAKE_ACCESSOR(simd_f32, __m128, float, const, 4)
|
||||
MAKE_ACCESSOR(simd_i32, __m128i, int, , 4)
|
||||
MAKE_ACCESSOR(simd_i32, __m128i, int, const, 4)
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Specialized SSE input assembly function for general vertex gather
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FORCE_INLINE void GatherVertices(__m128 *vtxX, __m128 *vtxY, __m128 *vtxW, const float *inVtx, const unsigned int *inTrisPtr, int numLanes, const VertexLayout &vtxLayout)
|
||||
{
|
||||
for (int lane = 0; lane < numLanes; lane++)
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
char *vPtrX = (char *)inVtx + inTrisPtr[lane * 3 + i] * vtxLayout.mStride;
|
||||
char *vPtrY = vPtrX + vtxLayout.mOffsetY;
|
||||
char *vPtrW = vPtrX + vtxLayout.mOffsetW;
|
||||
|
||||
simd_f32(vtxX[i])[lane] = *((float*)vPtrX);
|
||||
simd_f32(vtxY[i])[lane] = *((float*)vPtrY);
|
||||
simd_f32(vtxW[i])[lane] = *((float*)vPtrW);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SSE4.1 version
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace MaskedOcclusionCullingSSE41
|
||||
{
|
||||
FORCE_INLINE __m128i _mmw_mullo_epi32(const __m128i &a, const __m128i &b) { return _mm_mullo_epi32(a, b); }
|
||||
FORCE_INLINE __m128i _mmw_min_epi32(const __m128i &a, const __m128i &b) { return _mm_min_epi32(a, b); }
|
||||
FORCE_INLINE __m128i _mmw_max_epi32(const __m128i &a, const __m128i &b) { return _mm_max_epi32(a, b); }
|
||||
FORCE_INLINE __m128i _mmw_abs_epi32(const __m128i &a) { return _mm_abs_epi32(a); }
|
||||
FORCE_INLINE __m128 _mmw_blendv_ps(const __m128 &a, const __m128 &b, const __m128 &c) { return _mm_blendv_ps(a, b, c); }
|
||||
FORCE_INLINE int _mmw_testz_epi32(const __m128i &a, const __m128i &b) { return _mm_testz_si128(a, b); }
|
||||
FORCE_INLINE __m128 _mmx_dp4_ps(const __m128 &a, const __m128 &b) { return _mm_dp_ps(a, b, 0xFF); }
|
||||
FORCE_INLINE __m128 _mmw_floor_ps(const __m128 &a) { return _mm_round_ps(a, _MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC); }
|
||||
FORCE_INLINE __m128 _mmw_ceil_ps(const __m128 &a) { return _mm_round_ps(a, _MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC); }
|
||||
FORCE_INLINE __m128i _mmw_transpose_epi8(const __m128i &a)
|
||||
{
|
||||
const __m128i shuff = _mm_setr_epi8(0x0, 0x4, 0x8, 0xC, 0x1, 0x5, 0x9, 0xD, 0x2, 0x6, 0xA, 0xE, 0x3, 0x7, 0xB, 0xF);
|
||||
return _mm_shuffle_epi8(a, shuff);
|
||||
}
|
||||
FORCE_INLINE __m128i _mmw_sllv_ones(const __m128i &ishift)
|
||||
{
|
||||
__m128i shift = _mm_min_epi32(ishift, _mm_set1_epi32(32));
|
||||
|
||||
// Uses lookup tables and _mm_shuffle_epi8 to perform _mm_sllv_epi32(~0, shift)
|
||||
const __m128i byteShiftLUT = _mm_setr_epi8((char)0xFF, (char)0xFE, (char)0xFC, (char)0xF8, (char)0xF0, (char)0xE0, (char)0xC0, (char)0x80, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
const __m128i byteShiftOffset = _mm_setr_epi8(0, 8, 16, 24, 0, 8, 16, 24, 0, 8, 16, 24, 0, 8, 16, 24);
|
||||
const __m128i byteShiftShuffle = _mm_setr_epi8(0x0, 0x0, 0x0, 0x0, 0x4, 0x4, 0x4, 0x4, 0x8, 0x8, 0x8, 0x8, 0xC, 0xC, 0xC, 0xC);
|
||||
|
||||
__m128i byteShift = _mm_shuffle_epi8(shift, byteShiftShuffle);
|
||||
byteShift = _mm_min_epi8(_mm_subs_epu8(byteShift, byteShiftOffset), _mm_set1_epi8(8));
|
||||
__m128i retMask = _mm_shuffle_epi8(byteShiftLUT, byteShift);
|
||||
|
||||
return retMask;
|
||||
}
|
||||
|
||||
static MaskedOcclusionCulling::Implementation gInstructionSet = MaskedOcclusionCulling::SSE41;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Include common algorithm implementation (general, SIMD independent code)
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "MaskedOcclusionCullingCommon.inl"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Utility function to create a new object using the allocator callbacks
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree)
|
||||
{
|
||||
MaskedOcclusionCullingPrivate *object = (MaskedOcclusionCullingPrivate *)alignedAlloc(64, sizeof(MaskedOcclusionCullingPrivate));
|
||||
new (object) MaskedOcclusionCullingPrivate(alignedAlloc, alignedFree);
|
||||
return object;
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SSE2 version
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace MaskedOcclusionCullingSSE2
|
||||
{
|
||||
FORCE_INLINE __m128i _mmw_mullo_epi32(const __m128i &a, const __m128i &b)
|
||||
{
|
||||
// Do products for even / odd lanes & merge the result
|
||||
__m128i even = _mm_and_si128(_mm_mul_epu32(a, b), _mm_setr_epi32(~0, 0, ~0, 0));
|
||||
__m128i odd = _mm_slli_epi64(_mm_mul_epu32(_mm_srli_epi64(a, 32), _mm_srli_epi64(b, 32)), 32);
|
||||
return _mm_or_si128(even, odd);
|
||||
}
|
||||
FORCE_INLINE __m128i _mmw_min_epi32(const __m128i &a, const __m128i &b)
|
||||
{
|
||||
__m128i cond = _mm_cmpgt_epi32(a, b);
|
||||
return _mm_or_si128(_mm_andnot_si128(cond, a), _mm_and_si128(cond, b));
|
||||
}
|
||||
FORCE_INLINE __m128i _mmw_max_epi32(const __m128i &a, const __m128i &b)
|
||||
{
|
||||
__m128i cond = _mm_cmpgt_epi32(b, a);
|
||||
return _mm_or_si128(_mm_andnot_si128(cond, a), _mm_and_si128(cond, b));
|
||||
}
|
||||
FORCE_INLINE __m128i _mmw_abs_epi32(const __m128i &a)
|
||||
{
|
||||
__m128i mask = _mm_cmplt_epi32(a, _mm_setzero_si128());
|
||||
return _mm_add_epi32(_mm_xor_si128(a, mask), _mm_srli_epi32(mask, 31));
|
||||
}
|
||||
FORCE_INLINE int _mmw_testz_epi32(const __m128i &a, const __m128i &b)
|
||||
{
|
||||
return _mm_movemask_epi8(_mm_cmpeq_epi8(_mm_and_si128(a, b), _mm_setzero_si128())) == 0xFFFF;
|
||||
}
|
||||
FORCE_INLINE __m128 _mmw_blendv_ps(const __m128 &a, const __m128 &b, const __m128 &c)
|
||||
{
|
||||
__m128 cond = _mm_castsi128_ps(_mm_srai_epi32(_mm_castps_si128(c), 31));
|
||||
return _mm_or_ps(_mm_andnot_ps(cond, a), _mm_and_ps(cond, b));
|
||||
}
|
||||
FORCE_INLINE __m128 _mmx_dp4_ps(const __m128 &a, const __m128 &b)
|
||||
{
|
||||
// Product and two shuffle/adds pairs (similar to hadd_ps)
|
||||
__m128 prod = _mm_mul_ps(a, b);
|
||||
__m128 dp = _mm_add_ps(prod, _mm_shuffle_ps(prod, prod, _MM_SHUFFLE(2, 3, 0, 1)));
|
||||
dp = _mm_add_ps(dp, _mm_shuffle_ps(dp, dp, _MM_SHUFFLE(0, 1, 2, 3)));
|
||||
return dp;
|
||||
}
|
||||
FORCE_INLINE __m128 _mmw_floor_ps(const __m128 &a)
|
||||
{
|
||||
int originalMode = _MM_GET_ROUNDING_MODE();
|
||||
_MM_SET_ROUNDING_MODE(_MM_ROUND_DOWN);
|
||||
__m128 rounded = _mm_cvtepi32_ps(_mm_cvtps_epi32(a));
|
||||
_MM_SET_ROUNDING_MODE(originalMode);
|
||||
return rounded;
|
||||
}
|
||||
FORCE_INLINE __m128 _mmw_ceil_ps(const __m128 &a)
|
||||
{
|
||||
int originalMode = _MM_GET_ROUNDING_MODE();
|
||||
_MM_SET_ROUNDING_MODE(_MM_ROUND_UP);
|
||||
__m128 rounded = _mm_cvtepi32_ps(_mm_cvtps_epi32(a));
|
||||
_MM_SET_ROUNDING_MODE(originalMode);
|
||||
return rounded;
|
||||
}
|
||||
FORCE_INLINE __m128i _mmw_transpose_epi8(const __m128i &a)
|
||||
{
|
||||
// Perform transpose through two 16->8 bit pack and byte shifts
|
||||
__m128i res = a;
|
||||
const __m128i mask = _mm_setr_epi8(~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0, ~0, 0);
|
||||
res = _mm_packus_epi16(_mm_and_si128(res, mask), _mm_srli_epi16(res, 8));
|
||||
res = _mm_packus_epi16(_mm_and_si128(res, mask), _mm_srli_epi16(res, 8));
|
||||
return res;
|
||||
}
|
||||
FORCE_INLINE __m128i _mmw_sllv_ones(const __m128i &ishift)
|
||||
{
|
||||
__m128i shift = _mmw_min_epi32(ishift, _mm_set1_epi32(32));
|
||||
|
||||
// Uses scalar approach to perform _mm_sllv_epi32(~0, shift)
|
||||
static const unsigned int maskLUT[33] = {
|
||||
~0U << 0, ~0U << 1, ~0U << 2 , ~0U << 3, ~0U << 4, ~0U << 5, ~0U << 6 , ~0U << 7, ~0U << 8, ~0U << 9, ~0U << 10 , ~0U << 11, ~0U << 12, ~0U << 13, ~0U << 14 , ~0U << 15,
|
||||
~0U << 16, ~0U << 17, ~0U << 18 , ~0U << 19, ~0U << 20, ~0U << 21, ~0U << 22 , ~0U << 23, ~0U << 24, ~0U << 25, ~0U << 26 , ~0U << 27, ~0U << 28, ~0U << 29, ~0U << 30 , ~0U << 31,
|
||||
0U };
|
||||
|
||||
__m128i retMask;
|
||||
simd_i32(retMask)[0] = (int)maskLUT[simd_i32(shift)[0]];
|
||||
simd_i32(retMask)[1] = (int)maskLUT[simd_i32(shift)[1]];
|
||||
simd_i32(retMask)[2] = (int)maskLUT[simd_i32(shift)[2]];
|
||||
simd_i32(retMask)[3] = (int)maskLUT[simd_i32(shift)[3]];
|
||||
return retMask;
|
||||
}
|
||||
|
||||
static MaskedOcclusionCulling::Implementation gInstructionSet = MaskedOcclusionCulling::SSE2;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Include common algorithm implementation (general, SIMD independent code)
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "MaskedOcclusionCullingCommon.inl"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Utility function to create a new object using the allocator callbacks
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree)
|
||||
{
|
||||
MaskedOcclusionCullingPrivate *object = (MaskedOcclusionCullingPrivate *)alignedAlloc(64, sizeof(MaskedOcclusionCullingPrivate));
|
||||
new (object) MaskedOcclusionCullingPrivate(alignedAlloc, alignedFree);
|
||||
return object;
|
||||
}
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Object construction and allocation
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
namespace MaskedOcclusionCullingAVX512
|
||||
{
|
||||
extern MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree);
|
||||
}
|
||||
|
||||
namespace MaskedOcclusionCullingAVX2
|
||||
{
|
||||
extern MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree);
|
||||
}
|
||||
|
||||
MaskedOcclusionCulling *MaskedOcclusionCulling::Create(Implementation RequestedSIMD)
|
||||
{
|
||||
return Create(RequestedSIMD, aligned_alloc, aligned_free);
|
||||
}
|
||||
|
||||
MaskedOcclusionCulling *MaskedOcclusionCulling::Create(Implementation RequestedSIMD, pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree)
|
||||
{
|
||||
MaskedOcclusionCulling *object = nullptr;
|
||||
|
||||
MaskedOcclusionCulling::Implementation impl = DetectCPUFeatures(alignedAlloc, alignedFree);
|
||||
|
||||
if (RequestedSIMD < impl)
|
||||
impl = RequestedSIMD;
|
||||
|
||||
// Return best supported version
|
||||
if (object == nullptr && impl >= AVX512)
|
||||
object = MaskedOcclusionCullingAVX512::CreateMaskedOcclusionCulling(alignedAlloc, alignedFree); // Use AVX512 version
|
||||
if (object == nullptr && impl >= AVX2)
|
||||
object = MaskedOcclusionCullingAVX2::CreateMaskedOcclusionCulling(alignedAlloc, alignedFree); // Use AVX2 version
|
||||
if (object == nullptr && impl >= SSE41)
|
||||
object = MaskedOcclusionCullingSSE41::CreateMaskedOcclusionCulling(alignedAlloc, alignedFree); // Use SSE4.1 version
|
||||
if (object == nullptr)
|
||||
object = MaskedOcclusionCullingSSE2::CreateMaskedOcclusionCulling(alignedAlloc, alignedFree); // Use SSE2 (slow) version
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
void MaskedOcclusionCulling::Destroy(MaskedOcclusionCulling *moc)
|
||||
{
|
||||
pfnAlignedFree alignedFreeCallback = moc->mAlignedFreeCallback;
|
||||
moc->~MaskedOcclusionCulling();
|
||||
alignedFreeCallback(moc);
|
||||
}
|
||||
@@ -0,0 +1,592 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Intel Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
// use this file except in compliance with the License. You may obtain a copy
|
||||
// of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma once
|
||||
|
||||
/*!
|
||||
* \file MaskedOcclusionCulling.h
|
||||
* \brief Masked Occlusion Culling
|
||||
*
|
||||
* General information
|
||||
* - Input to all API functions are (x,y,w) clip-space coordinates (x positive left, y positive up, w positive away from camera).
|
||||
* We entirely skip the z component and instead compute it as 1 / w, see next bullet. For TestRect the input is NDC (x/w, y/w).
|
||||
* - We use a simple z = 1 / w transform, which is a bit faster than OGL/DX depth transforms. Thus, depth is REVERSED and z = 0 at
|
||||
* the far plane and z = inf at w = 0. We also have to use a GREATER depth function, which explains why all the conservative
|
||||
* tests will be reversed compared to what you might be used to (for example zMaxTri >= zMinBuffer is a visibility test)
|
||||
* - We support different layouts for vertex data (basic AoS and SoA), but note that it's beneficial to store the position data
|
||||
* as tightly in memory as possible to reduce cache misses. Big strides are bad, so it's beneficial to keep position as a separate
|
||||
* stream (rather than bundled with attributes) or to keep a copy of the position data for the occlusion culling system.
|
||||
* - The resolution width must be a multiple of 8 and height a multiple of 4.
|
||||
* - The hierarchical Z buffer is stored OpenGL-style with the y axis pointing up. This includes the scissor box.
|
||||
* - This code is only tested with Visual Studio 2015, but should hopefully be easy to port to other compilers.
|
||||
*/
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Defines used to configure the implementation
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef QUICK_MASK
|
||||
/*!
|
||||
* Configure the algorithm used for updating and merging hierarchical z buffer entries. If QUICK_MASK
|
||||
* is defined to 1, use the algorithm from the paper "Masked Software Occlusion Culling", which has good
|
||||
* balance between performance and low leakage. If QUICK_MASK is defined to 0, use the algorithm from
|
||||
* "Masked Depth Culling for Graphics Hardware" which has less leakage, but also lower performance.
|
||||
*/
|
||||
#define QUICK_MASK 1
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef USE_D3D
|
||||
/*!
|
||||
* Configures the library for use with Direct3D (default) or OpenGL rendering. This changes whether the
|
||||
* screen space Y axis points downwards (D3D) or upwards (OGL), and is primarily important in combination
|
||||
* with the PRECISE_COVERAGE define, where this is important to ensure correct rounding and tie-breaker
|
||||
* behaviour. It also affects the ScissorRect screen space coordinates.
|
||||
*/
|
||||
#define USE_D3D 1
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef PRECISE_COVERAGE
|
||||
/*!
|
||||
* Define PRECISE_COVERAGE to 1 to more closely match GPU rasterization rules. The increased precision comes
|
||||
* at a cost of slightly lower performance.
|
||||
*/
|
||||
#define PRECISE_COVERAGE 1
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef USE_AVX512
|
||||
/*!
|
||||
* Define USE_AVX512 to 1 to enable experimental AVX-512 support. It's currently mostly untested and only
|
||||
* validated on simple examples using Intel SDE. Older compilers may not support AVX-512 intrinsics.
|
||||
*/
|
||||
#define USE_AVX512 0
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef CLIPPING_PRESERVES_ORDER
|
||||
/*!
|
||||
* Define CLIPPING_PRESERVES_ORDER to 1 to prevent clipping from reordering triangle rasterization
|
||||
* order; This comes at a cost (approx 3-4%) but removes one source of temporal frame-to-frame instability.
|
||||
*/
|
||||
#define CLIPPING_PRESERVES_ORDER 1
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef ENABLE_STATS
|
||||
/*!
|
||||
* Define ENABLE_STATS to 1 to gather various statistics during occlusion culling. Can be used for profiling
|
||||
* and debugging. Note that enabling this function will reduce performance significantly.
|
||||
*/
|
||||
#define ENABLE_STATS 0
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef MOC_RECORDER_ENABLE
|
||||
/*!
|
||||
* Define MOC_RECORDER_ENABLE to 1 to enable frame recorder (see FrameRecorder.h/cpp for details)
|
||||
*/
|
||||
#define MOC_RECORDER_ENABLE 0
|
||||
|
||||
#endif
|
||||
|
||||
#if MOC_RECORDER_ENABLE
|
||||
#ifndef MOC_RECORDER_ENABLE_PLAYBACK
|
||||
/*!
|
||||
* Define MOC_RECORDER_ENABLE_PLAYBACK to 1 to enable compilation of the playback code (not needed
|
||||
for recording)
|
||||
*/
|
||||
#define MOC_RECORDER_ENABLE_PLAYBACK 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#if MOC_RECORDER_ENABLE
|
||||
|
||||
#include <mutex>
|
||||
|
||||
class FrameRecorder;
|
||||
|
||||
#endif // #if MOC_RECORDER_ENABLE
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Masked occlusion culling class
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class MaskedOcclusionCulling
|
||||
{
|
||||
public:
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Memory management callback functions
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef void *(*pfnAlignedAlloc)(size_t alignment, size_t size);
|
||||
typedef void (*pfnAlignedFree) (void *ptr);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Enums
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
enum Implementation
|
||||
{
|
||||
SSE2 = 0,
|
||||
SSE41 = 1,
|
||||
AVX2 = 2,
|
||||
AVX512 = 3
|
||||
};
|
||||
|
||||
enum BackfaceWinding
|
||||
{
|
||||
BACKFACE_NONE = 0,
|
||||
BACKFACE_CW = 1,
|
||||
BACKFACE_CCW = 2,
|
||||
};
|
||||
|
||||
enum CullingResult
|
||||
{
|
||||
VISIBLE = 0x0,
|
||||
OCCLUDED = 0x1,
|
||||
VIEW_CULLED = 0x3
|
||||
};
|
||||
|
||||
enum ClipPlanes
|
||||
{
|
||||
CLIP_PLANE_NONE = 0x00,
|
||||
CLIP_PLANE_NEAR = 0x01,
|
||||
CLIP_PLANE_LEFT = 0x02,
|
||||
CLIP_PLANE_RIGHT = 0x04,
|
||||
CLIP_PLANE_BOTTOM = 0x08,
|
||||
CLIP_PLANE_TOP = 0x10,
|
||||
CLIP_PLANE_SIDES = (CLIP_PLANE_LEFT | CLIP_PLANE_RIGHT | CLIP_PLANE_BOTTOM | CLIP_PLANE_TOP),
|
||||
CLIP_PLANE_ALL = (CLIP_PLANE_LEFT | CLIP_PLANE_RIGHT | CLIP_PLANE_BOTTOM | CLIP_PLANE_TOP | CLIP_PLANE_NEAR)
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Structs
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*!
|
||||
* Used to specify custom vertex layout. Memory offsets to y and z coordinates are set through
|
||||
* mOffsetY and mOffsetW, and vertex stride is given by mStride. It's possible to configure both
|
||||
* AoS and SoA layouts. Note that large strides may cause more cache misses and decrease
|
||||
* performance. It is advisable to store position data as compactly in memory as possible.
|
||||
*/
|
||||
struct VertexLayout
|
||||
{
|
||||
VertexLayout() {}
|
||||
VertexLayout(int stride, int offsetY, int offsetZW) :
|
||||
mStride(stride), mOffsetY(offsetY), mOffsetW(offsetZW) {}
|
||||
|
||||
int mStride; //!< byte stride between vertices
|
||||
int mOffsetY; //!< byte offset from X to Y coordinate
|
||||
union {
|
||||
int mOffsetZ; //!< byte offset from X to Z coordinate
|
||||
int mOffsetW; //!< byte offset from X to W coordinate
|
||||
};
|
||||
};
|
||||
|
||||
/*!
|
||||
* Used to control scissoring during rasterization. Note that we only provide coarse scissor support.
|
||||
* The scissor box x coordinates must be a multiple of 32, and the y coordinates a multiple of 8.
|
||||
* Scissoring is mainly meant as a means of enabling binning (sort middle) rasterizers in case
|
||||
* application developers want to use that approach for multithreading.
|
||||
*/
|
||||
struct ScissorRect
|
||||
{
|
||||
ScissorRect() {}
|
||||
ScissorRect(int minX, int minY, int maxX, int maxY) :
|
||||
mMinX(minX), mMinY(minY), mMaxX(maxX), mMaxY(maxY) {}
|
||||
|
||||
int mMinX; //!< Screen space X coordinate for left side of scissor rect, inclusive and must be a multiple of 32
|
||||
int mMinY; //!< Screen space Y coordinate for bottom side of scissor rect, inclusive and must be a multiple of 8
|
||||
int mMaxX; //!< Screen space X coordinate for right side of scissor rect, <B>non</B> inclusive and must be a multiple of 32
|
||||
int mMaxY; //!< Screen space Y coordinate for top side of scissor rect, <B>non</B> inclusive and must be a multiple of 8
|
||||
};
|
||||
|
||||
/*!
|
||||
* Used to specify storage area for a binlist, containing triangles. This struct is used for binning
|
||||
* and multithreading. The host application is responsible for allocating memory for the binlists.
|
||||
*/
|
||||
struct TriList
|
||||
{
|
||||
unsigned int mNumTriangles; //!< Maximum number of triangles that may be stored in mPtr
|
||||
unsigned int mTriIdx; //!< Index of next triangle to be written, clear before calling BinTriangles to start from the beginning of the list
|
||||
float *mPtr; //!< Scratchpad buffer allocated by the host application
|
||||
};
|
||||
|
||||
/*!
|
||||
* Statistics that can be gathered during occluder rendering and visibility to aid debugging
|
||||
* and profiling. Must be enabled by changing the ENABLE_STATS define.
|
||||
*/
|
||||
struct OcclusionCullingStatistics
|
||||
{
|
||||
struct
|
||||
{
|
||||
long long mNumProcessedTriangles; //!< Number of occluder triangles processed in total
|
||||
long long mNumRasterizedTriangles; //!< Number of occluder triangles passing view frustum and backface culling
|
||||
long long mNumTilesTraversed; //!< Number of tiles traversed by the rasterizer
|
||||
long long mNumTilesUpdated; //!< Number of tiles where the hierarchical z buffer was updated
|
||||
long long mNumTilesMerged; //!< Number of tiles where the hierarchical z buffer was updated
|
||||
} mOccluders;
|
||||
|
||||
struct
|
||||
{
|
||||
long long mNumProcessedRectangles; //!< Number of rects processed (TestRect())
|
||||
long long mNumProcessedTriangles; //!< Number of ocludee triangles processed (TestTriangles())
|
||||
long long mNumRasterizedTriangles; //!< Number of ocludee triangle passing view frustum and backface culling
|
||||
long long mNumTilesTraversed; //!< Number of tiles traversed by triangle & rect rasterizers
|
||||
} mOccludees;
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Functions
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*!
|
||||
* \brief Creates a new object with default state, no z buffer attached/allocated.
|
||||
*/
|
||||
static MaskedOcclusionCulling *Create(Implementation RequestedSIMD = AVX512);
|
||||
|
||||
/*!
|
||||
* \brief Creates a new object with default state, no z buffer attached/allocated.
|
||||
* \param alignedAlloc Pointer to a callback function used when allocating memory
|
||||
* \param alignedFree Pointer to a callback function used when freeing memory
|
||||
*/
|
||||
static MaskedOcclusionCulling *Create(Implementation RequestedSIMD, pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree);
|
||||
|
||||
/*!
|
||||
* \brief Destroys an object and frees the z buffer memory. Note that you cannot
|
||||
* use the delete operator, and should rather use this function to free up memory.
|
||||
*/
|
||||
static void Destroy(MaskedOcclusionCulling *moc);
|
||||
|
||||
/*!
|
||||
* \brief Sets the resolution of the hierarchical depth buffer. This function will
|
||||
* re-allocate the current depth buffer (if present). The contents of the
|
||||
* buffer is undefined until ClearBuffer() is called.
|
||||
*
|
||||
* \param witdh The width of the buffer in pixels, must be a multiple of 8
|
||||
* \param height The height of the buffer in pixels, must be a multiple of 4
|
||||
*/
|
||||
virtual void SetResolution(unsigned int width, unsigned int height) = 0;
|
||||
|
||||
/*!
|
||||
* \brief Gets the resolution of the hierarchical depth buffer.
|
||||
*
|
||||
* \param witdh Output: The width of the buffer in pixels
|
||||
* \param height Output: The height of the buffer in pixels
|
||||
*/
|
||||
virtual void GetResolution(unsigned int &width, unsigned int &height) const = 0;
|
||||
|
||||
/*!
|
||||
* \brief Returns the tile size for the current implementation.
|
||||
*
|
||||
* \param nBinsW Number of vertical bins, the screen is divided into nBinsW x nBinsH
|
||||
* rectangular bins.
|
||||
* \param nBinsH Number of horizontal bins, the screen is divided into nBinsW x nBinsH
|
||||
* rectangular bins.
|
||||
* \param outBinWidth Output: The width of the single bin in pixels (except for the
|
||||
* rightmost bin width, which is extended to resolution width)
|
||||
* \param outBinHeight Output: The height of the single bin in pixels (except for the
|
||||
* bottommost bin height, which is extended to resolution height)
|
||||
*/
|
||||
virtual void ComputeBinWidthHeight(unsigned int nBinsW, unsigned int nBinsH, unsigned int & outBinWidth, unsigned int & outBinHeight) = 0;
|
||||
|
||||
/*!
|
||||
* \brief Sets the distance for the near clipping plane. Default is nearDist = 0.
|
||||
*
|
||||
* \param nearDist The distance to the near clipping plane, given as clip space w
|
||||
*/
|
||||
virtual void SetNearClipPlane(float nearDist) = 0;
|
||||
|
||||
/*!
|
||||
* \brief Gets the distance for the near clipping plane.
|
||||
*/
|
||||
virtual float GetNearClipPlane() const = 0;
|
||||
|
||||
/*!
|
||||
* \brief Clears the hierarchical depth buffer.
|
||||
*/
|
||||
virtual void ClearBuffer() = 0;
|
||||
|
||||
/*!
|
||||
* \brief Merge a second hierarchical depth buffer into the main buffer.
|
||||
*/
|
||||
virtual void MergeBuffer(MaskedOcclusionCulling* BufferB) = 0;
|
||||
|
||||
/*!
|
||||
* \brief Renders a mesh of occluder triangles and updates the hierarchical z buffer
|
||||
* with conservative depth values.
|
||||
*
|
||||
* This function is optimized for vertex layouts with stride 16 and y and w
|
||||
* offsets of 4 and 12 bytes, respectively.
|
||||
*
|
||||
* \param inVtx Pointer to an array of input vertices, should point to the x component
|
||||
* of the first vertex. The input vertices are given as (x,y,w) coordinates
|
||||
* in clip space. The memory layout can be changed using vtxLayout.
|
||||
* \param inTris Pointer to an array of vertex indices. Each triangle is created
|
||||
* from three indices consecutively fetched from the array.
|
||||
* \param nTris The number of triangles to render (inTris must contain atleast 3*nTris
|
||||
* entries)
|
||||
* \param modelToClipMatrix all vertices will be transformed by this matrix before
|
||||
* performing projection. If nullptr is passed the transform step will be skipped
|
||||
* \param bfWinding Sets triangle winding order to consider backfacing, must be one one
|
||||
* of (BACKFACE_NONE, BACKFACE_CW and BACKFACE_CCW). Back-facing triangles are culled
|
||||
* and will not be rasterized. You may use BACKFACE_NONE to disable culling for
|
||||
* double sided geometry
|
||||
* \param clipPlaneMask A mask indicating which clip planes should be considered by the
|
||||
* triangle clipper. Can be used as an optimization if your application can
|
||||
* determine (for example during culling) that a group of triangles does not
|
||||
* intersect a certain frustum plane. However, setting an incorrect mask may
|
||||
* cause out of bounds memory accesses.
|
||||
* \param vtxLayout A struct specifying the vertex layout (see struct for detailed
|
||||
* description). For best performance, it is advisable to store position data
|
||||
* as compactly in memory as possible.
|
||||
* \return Will return VIEW_CULLED if all triangles are either outside the frustum or
|
||||
* backface culled, returns VISIBLE otherwise.
|
||||
*/
|
||||
virtual CullingResult RenderTriangles(const float *inVtx, const unsigned int *inTris, int nTris, const float *modelToClipMatrix = nullptr, BackfaceWinding bfWinding = BACKFACE_CW, ClipPlanes clipPlaneMask = CLIP_PLANE_ALL, const VertexLayout &vtxLayout = VertexLayout(16, 4, 12)) = 0;
|
||||
|
||||
/*!
|
||||
* \brief Occlusion query for a rectangle with a given depth. The rectangle is given
|
||||
* in normalized device coordinates where (x,y) coordinates between [-1,1] map
|
||||
* to the visible screen area. The query uses a GREATER_EQUAL (reversed) depth
|
||||
* test meaning that depth values equal to the contents of the depth buffer are
|
||||
* counted as visible.
|
||||
*
|
||||
* \param xmin NDC coordinate of the left side of the rectangle.
|
||||
* \param ymin NDC coordinate of the bottom side of the rectangle.
|
||||
* \param xmax NDC coordinate of the right side of the rectangle.
|
||||
* \param ymax NDC coordinate of the top side of the rectangle.
|
||||
* \param ymax NDC coordinate of the top side of the rectangle.
|
||||
* \param wmin Clip space W coordinate for the rectangle.
|
||||
* \return The query will return VISIBLE if the rectangle may be visible, OCCLUDED
|
||||
* if the rectangle is occluded by a previously rendered object, or VIEW_CULLED
|
||||
* if the rectangle is outside the view frustum.
|
||||
*/
|
||||
virtual CullingResult TestRect(float xmin, float ymin, float xmax, float ymax, float wmin) const = 0;
|
||||
|
||||
/*!
|
||||
* \brief This function is similar to RenderTriangles(), but performs an occlusion
|
||||
* query instead and does not update the hierarchical z buffer. The query uses
|
||||
* a GREATER_EQUAL (reversed) depth test meaning that depth values equal to the
|
||||
* contents of the depth buffer are counted as visible.
|
||||
*
|
||||
* This function is optimized for vertex layouts with stride 16 and y and w
|
||||
* offsets of 4 and 12 bytes, respectively.
|
||||
*
|
||||
* \param inVtx Pointer to an array of input vertices, should point to the x component
|
||||
* of the first vertex. The input vertices are given as (x,y,w) coordinates
|
||||
* in clip space. The memory layout can be changed using vtxLayout.
|
||||
* \param inTris Pointer to an array of triangle indices. Each triangle is created
|
||||
* from three indices consecutively fetched from the array.
|
||||
* \param nTris The number of triangles to render (inTris must contain atleast 3*nTris
|
||||
* entries)
|
||||
* \param modelToClipMatrix all vertices will be transformed by this matrix before
|
||||
* performing projection. If nullptr is passed the transform step will be skipped
|
||||
* \param bfWinding Sets triangle winding order to consider backfacing, must be one one
|
||||
* of (BACKFACE_NONE, BACKFACE_CW and BACKFACE_CCW). Back-facing triangles are culled
|
||||
* and will not be occlusion tested. You may use BACKFACE_NONE to disable culling
|
||||
* for double sided geometry
|
||||
* \param clipPlaneMask A mask indicating which clip planes should be considered by the
|
||||
* triangle clipper. Can be used as an optimization if your application can
|
||||
* determine (for example during culling) that a group of triangles does not
|
||||
* intersect a certain frustum plane. However, setting an incorrect mask may
|
||||
* cause out of bounds memory accesses.
|
||||
* \param vtxLayout A struct specifying the vertex layout (see struct for detailed
|
||||
* description). For best performance, it is advisable to store position data
|
||||
* as compactly in memory as possible.
|
||||
* \return The query will return VISIBLE if the triangle mesh may be visible, OCCLUDED
|
||||
* if the mesh is occluded by a previously rendered object, or VIEW_CULLED if all
|
||||
* triangles are entirely outside the view frustum or backface culled.
|
||||
*/
|
||||
virtual CullingResult TestTriangles(const float *inVtx, const unsigned int *inTris, int nTris, const float *modelToClipMatrix = nullptr, BackfaceWinding bfWinding = BACKFACE_CW, ClipPlanes clipPlaneMask = CLIP_PLANE_ALL, const VertexLayout &vtxLayout = VertexLayout(16, 4, 12)) = 0;
|
||||
|
||||
/*!
|
||||
* \brief Perform input assembly, clipping , projection, triangle setup, and write
|
||||
* triangles to the screen space bins they overlap. This function can be used to
|
||||
* distribute work for threading (See the CullingThreadpool class for an example)
|
||||
*
|
||||
* \param inVtx Pointer to an array of input vertices, should point to the x component
|
||||
* of the first vertex. The input vertices are given as (x,y,w) coordinates
|
||||
* in clip space. The memory layout can be changed using vtxLayout.
|
||||
* \param inTris Pointer to an array of vertex indices. Each triangle is created
|
||||
* from three indices consecutively fetched from the array.
|
||||
* \param nTris The number of triangles to render (inTris must contain atleast 3*nTris
|
||||
* entries)
|
||||
* \param triLists Pointer to an array of TriList objects with one TriList object per
|
||||
* bin. If a triangle overlaps a bin, it will be written to the corresponding
|
||||
* trilist. Note that this method appends the triangles to the current list, to
|
||||
* start writing from the beginning of the list, set triList.mTriIdx = 0
|
||||
* \param nBinsW Number of vertical bins, the screen is divided into nBinsW x nBinsH
|
||||
* rectangular bins.
|
||||
* \param nBinsH Number of horizontal bins, the screen is divided into nBinsW x nBinsH
|
||||
* rectangular bins.
|
||||
* \param modelToClipMatrix all vertices will be transformed by this matrix before
|
||||
* performing projection. If nullptr is passed the transform step will be skipped
|
||||
* \param clipPlaneMask A mask indicating which clip planes should be considered by the
|
||||
* triangle clipper. Can be used as an optimization if your application can
|
||||
* determine (for example during culling) that a group of triangles does not
|
||||
* intersect a certain frustum plane. However, setting an incorrect mask may
|
||||
* cause out of bounds memory accesses.
|
||||
* \param vtxLayout A struct specifying the vertex layout (see struct for detailed
|
||||
* description). For best performance, it is advisable to store position data
|
||||
* as compactly in memory as possible.
|
||||
* \param bfWinding Sets triangle winding order to consider backfacing, must be one one
|
||||
* of (BACKFACE_NONE, BACKFACE_CW and BACKFACE_CCW). Back-facing triangles are culled
|
||||
* and will not be binned / rasterized. You may use BACKFACE_NONE to disable culling
|
||||
* for double sided geometry
|
||||
*/
|
||||
virtual void BinTriangles(const float *inVtx, const unsigned int *inTris, int nTris, TriList *triLists, unsigned int nBinsW, unsigned int nBinsH, const float *modelToClipMatrix = nullptr, BackfaceWinding bfWinding = BACKFACE_CW, ClipPlanes clipPlaneMask = CLIP_PLANE_ALL, const VertexLayout &vtxLayout = VertexLayout(16, 4, 12)) = 0;
|
||||
|
||||
/*!
|
||||
* \brief Renders all occluder triangles in a trilist. This function can be used in
|
||||
* combination with BinTriangles() to create a threded (binning) rasterizer. The
|
||||
* bins can be processed independently by different threads without risking writing
|
||||
* to overlapping memory regions.
|
||||
*
|
||||
* \param triLists A triangle list, filled using the BinTriangles() function that is to
|
||||
* be rendered.
|
||||
* \param scissor A scissor box limiting the rendering region to the bin. The size of each
|
||||
* bin must be a multiple of 32x8 pixels due to implementation constraints. For a
|
||||
* render target with (width, height) resolution and (nBinsW, nBinsH) bins, the
|
||||
* size of a bin is:
|
||||
* binWidth = (width / nBinsW) - (width / nBinsW) % 32;
|
||||
* binHeight = (height / nBinsH) - (height / nBinsH) % 8;
|
||||
* The last row and column of tiles have a different size:
|
||||
* lastColBinWidth = width - (nBinsW-1)*binWidth;
|
||||
* lastRowBinHeight = height - (nBinsH-1)*binHeight;
|
||||
*/
|
||||
virtual void RenderTrilist(const TriList &triList, const ScissorRect *scissor) = 0;
|
||||
|
||||
/*!
|
||||
* \brief Creates a per-pixel depth buffer from the hierarchical z buffer representation.
|
||||
* Intended for visualizing the hierarchical depth buffer for debugging. The
|
||||
* buffer is written in scanline order, from the top to bottom (D3D) or bottom to
|
||||
* top (OGL) of the surface. See the USE_D3D define.
|
||||
*
|
||||
* \param depthData Pointer to memory where the per-pixel depth data is written. Must
|
||||
* hold storage for atleast width*height elements as set by setResolution.
|
||||
*/
|
||||
virtual void ComputePixelDepthBuffer(float *depthData, bool flipY) = 0;
|
||||
|
||||
/*!
|
||||
* \brief Fetch occlusion culling statistics, returns zeroes if ENABLE_STATS define is
|
||||
* not defined. The statistics can be used for profiling or debugging.
|
||||
*/
|
||||
virtual OcclusionCullingStatistics GetStatistics() = 0;
|
||||
|
||||
/*!
|
||||
* \brief Returns the implementation (CPU instruction set) version of this object.
|
||||
*/
|
||||
virtual Implementation GetImplementation() = 0;
|
||||
|
||||
/*!
|
||||
* \brief Utility function for transforming vertices and outputting them to an (x,y,z,w)
|
||||
* format suitable for the occluder rasterization and occludee testing functions.
|
||||
*
|
||||
* \param mtx Pointer to matrix data. The matrix should column major for post
|
||||
* multiplication (OGL) and row major for pre-multiplication (DX). This is
|
||||
* consistent with OpenGL / DirectX behavior.
|
||||
* \param inVtx Pointer to an array of input vertices. The input vertices are given as
|
||||
* (x,y,z) coordinates. The memory layout can be changed using vtxLayout.
|
||||
* \param xfVtx Pointer to an array to store transformed vertices. The transformed
|
||||
* vertices are always stored as array of structs (AoS) (x,y,z,w) packed in memory.
|
||||
* \param nVtx Number of vertices to transform.
|
||||
* \param vtxLayout A struct specifying the vertex layout (see struct for detailed
|
||||
* description). For best performance, it is advisable to store position data
|
||||
* as compactly in memory as possible. Note that for this function, the
|
||||
* w-component is assumed to be 1.0.
|
||||
*/
|
||||
static void TransformVertices(const float *mtx, const float *inVtx, float *xfVtx, unsigned int nVtx, const VertexLayout &vtxLayout = VertexLayout(12, 4, 8));
|
||||
|
||||
/*!
|
||||
* \brief Get used memory alloc/free callbacks.
|
||||
*/
|
||||
void GetAllocFreeCallback( pfnAlignedAlloc & allocCallback, pfnAlignedFree & freeCallback ) { allocCallback = mAlignedAllocCallback, freeCallback = mAlignedFreeCallback; }
|
||||
|
||||
#if MOC_RECORDER_ENABLE
|
||||
/*!
|
||||
* \brief Start recording subsequent rasterization and testing calls using the FrameRecorder.
|
||||
* The function calls that are recorded are:
|
||||
* - ClearBuffer
|
||||
* - RenderTriangles
|
||||
* - TestTriangles
|
||||
* - TestRect
|
||||
* All inputs and outputs are recorded, which can be used for correctness validation
|
||||
* and performance testing.
|
||||
*
|
||||
* \param outputFilePath Pointer to name of the output file.
|
||||
* \return 'true' if recording was started successfully, 'false' otherwise (file access error).
|
||||
*/
|
||||
bool RecorderStart( const char * outputFilePath ) const;
|
||||
|
||||
/*!
|
||||
* \brief Stop recording, flush output and release used memory.
|
||||
*/
|
||||
void RecorderStop( ) const;
|
||||
|
||||
/*!
|
||||
* \brief Manually record triangles. This is called automatically from MaskedOcclusionCulling::RenderTriangles
|
||||
* if the recording is started, but not from BinTriangles/RenderTrilist (used in multithreaded codepath), in
|
||||
* which case it has to be called manually.
|
||||
*
|
||||
* \param inVtx Pointer to an array of input vertices, should point to the x component
|
||||
* of the first vertex. The input vertices are given as (x,y,w) coordinates
|
||||
* in clip space. The memory layout can be changed using vtxLayout.
|
||||
* \param inTris Pointer to an array of triangle indices. Each triangle is created
|
||||
* from three indices consecutively fetched from the array.
|
||||
* \param nTris The number of triangles to render (inTris must contain atleast 3*nTris
|
||||
* entries)
|
||||
* \param modelToClipMatrix all vertices will be transformed by this matrix before
|
||||
* performing projection. If nullptr is passed the transform step will be skipped
|
||||
* \param bfWinding Sets triangle winding order to consider backfacing, must be one one
|
||||
* of (BACKFACE_NONE, BACKFACE_CW and BACKFACE_CCW). Back-facing triangles are culled
|
||||
* and will not be occlusion tested. You may use BACKFACE_NONE to disable culling
|
||||
* for double sided geometry
|
||||
* \param clipPlaneMask A mask indicating which clip planes should be considered by the
|
||||
* triangle clipper. Can be used as an optimization if your application can
|
||||
* determine (for example during culling) that a group of triangles does not
|
||||
* intersect a certain frustum plane. However, setting an incorrect mask may
|
||||
* cause out of bounds memory accesses.
|
||||
* \param vtxLayout A struct specifying the vertex layout (see struct for detailed
|
||||
* description). For best performance, it is advisable to store position data
|
||||
* as compactly in memory as possible.
|
||||
* \param cullingResult cull result value expected to be returned by executing the
|
||||
* RenderTriangles call with recorded parameters.
|
||||
*/
|
||||
//
|
||||
// merge the binned data back into original layout; in this case, call it manually from your Threadpool implementation (already added to CullingThreadpool).
|
||||
// If recording is not enabled, calling this function will do nothing.
|
||||
void RecordRenderTriangles( const float *inVtx, const unsigned int *inTris, int nTris, const float *modelToClipMatrix = nullptr, ClipPlanes clipPlaneMask = CLIP_PLANE_ALL, BackfaceWinding bfWinding = BACKFACE_CW, const VertexLayout &vtxLayout = VertexLayout( 16, 4, 12 ), CullingResult cullingResult = (CullingResult)-1 );
|
||||
#endif // #if MOC_RECORDER_ENABLE
|
||||
|
||||
protected:
|
||||
pfnAlignedAlloc mAlignedAllocCallback;
|
||||
pfnAlignedFree mAlignedFreeCallback;
|
||||
|
||||
mutable OcclusionCullingStatistics mStats;
|
||||
|
||||
#if MOC_RECORDER_ENABLE
|
||||
mutable FrameRecorder * mRecorder;
|
||||
mutable std::mutex mRecorderMutex;
|
||||
#endif // #if MOC_RECORDER_ENABLE
|
||||
|
||||
virtual ~MaskedOcclusionCulling() {}
|
||||
};
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Intel Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
// use this file except in compliance with the License. You may obtain a copy
|
||||
// of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <float.h>
|
||||
#include "MaskedOcclusionCulling.h"
|
||||
#include "CompilerSpecific.inl"
|
||||
|
||||
#if MOC_RECORDER_ENABLE
|
||||
#include "FrameRecorder.h"
|
||||
#endif
|
||||
|
||||
#if defined(__MICROSOFT_COMPILER) && _MSC_VER < 1900
|
||||
// If you remove/comment this error, the code will compile & use the SSE41 version instead.
|
||||
#error Older versions than visual studio 2015 not supported due to compiler bug(s)
|
||||
#endif
|
||||
|
||||
#if !defined(__MICROSOFT_COMPILER) || _MSC_VER >= 1900
|
||||
|
||||
// For performance reasons, the MaskedOcclusionCullingAVX2.cpp file should be compiled with VEX encoding for SSE instructions (to avoid
|
||||
// AVX-SSE transition penalties, see https://software.intel.com/en-us/articles/avoiding-avx-sse-transition-penalties). However, the SSE
|
||||
// version in MaskedOcclusionCulling.cpp _must_ be compiled without VEX encoding to allow backwards compatibility. Best practice is to
|
||||
// use lowest supported target platform (e.g. /arch:SSE2) as project default, and elevate only the MaskedOcclusionCullingAVX2/512.cpp files.
|
||||
#ifndef __AVX2__
|
||||
#error For best performance, MaskedOcclusionCullingAVX2.cpp should be compiled with /arch:AVX2
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// AVX specific defines and constants
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define SIMD_LANES 8
|
||||
#define TILE_HEIGHT_SHIFT 3
|
||||
|
||||
#define SIMD_LANE_IDX _mm256_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7)
|
||||
|
||||
#define SIMD_SUB_TILE_COL_OFFSET _mm256_setr_epi32(0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3)
|
||||
#define SIMD_SUB_TILE_ROW_OFFSET _mm256_setr_epi32(0, 0, 0, 0, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT)
|
||||
#define SIMD_SUB_TILE_COL_OFFSET_F _mm256_setr_ps(0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3)
|
||||
#define SIMD_SUB_TILE_ROW_OFFSET_F _mm256_setr_ps(0, 0, 0, 0, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT)
|
||||
|
||||
#define SIMD_SHUFFLE_SCANLINE_TO_SUBTILES _mm256_setr_epi8(0x0, 0x4, 0x8, 0xC, 0x1, 0x5, 0x9, 0xD, 0x2, 0x6, 0xA, 0xE, 0x3, 0x7, 0xB, 0xF, 0x0, 0x4, 0x8, 0xC, 0x1, 0x5, 0x9, 0xD, 0x2, 0x6, 0xA, 0xE, 0x3, 0x7, 0xB, 0xF)
|
||||
|
||||
#define SIMD_LANE_YCOORD_I _mm256_setr_epi32(128, 384, 640, 896, 1152, 1408, 1664, 1920)
|
||||
#define SIMD_LANE_YCOORD_F _mm256_setr_ps(128.0f, 384.0f, 640.0f, 896.0f, 1152.0f, 1408.0f, 1664.0f, 1920.0f)
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// AVX specific typedefs and functions
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef __m256 __mw;
|
||||
typedef __m256i __mwi;
|
||||
|
||||
#define _mmw_set1_ps _mm256_set1_ps
|
||||
#define _mmw_setzero_ps _mm256_setzero_ps
|
||||
#define _mmw_and_ps _mm256_and_ps
|
||||
#define _mmw_or_ps _mm256_or_ps
|
||||
#define _mmw_xor_ps _mm256_xor_ps
|
||||
#define _mmw_not_ps(a) _mm256_xor_ps((a), _mm256_castsi256_ps(_mm256_set1_epi32(~0)))
|
||||
#define _mmw_andnot_ps _mm256_andnot_ps
|
||||
#define _mmw_neg_ps(a) _mm256_xor_ps((a), _mm256_set1_ps(-0.0f))
|
||||
#define _mmw_abs_ps(a) _mm256_and_ps((a), _mm256_castsi256_ps(_mm256_set1_epi32(0x7FFFFFFF)))
|
||||
#define _mmw_add_ps _mm256_add_ps
|
||||
#define _mmw_sub_ps _mm256_sub_ps
|
||||
#define _mmw_mul_ps _mm256_mul_ps
|
||||
#define _mmw_div_ps _mm256_div_ps
|
||||
#define _mmw_min_ps _mm256_min_ps
|
||||
#define _mmw_max_ps _mm256_max_ps
|
||||
#define _mmw_fmadd_ps _mm256_fmadd_ps
|
||||
#define _mmw_fmsub_ps _mm256_fmsub_ps
|
||||
#define _mmw_movemask_ps _mm256_movemask_ps
|
||||
#define _mmw_blendv_ps _mm256_blendv_ps
|
||||
#define _mmw_cmpge_ps(a,b) _mm256_cmp_ps(a, b, _CMP_GE_OQ)
|
||||
#define _mmw_cmpgt_ps(a,b) _mm256_cmp_ps(a, b, _CMP_GT_OQ)
|
||||
#define _mmw_cmpeq_ps(a,b) _mm256_cmp_ps(a, b, _CMP_EQ_OQ)
|
||||
#define _mmw_floor_ps(x) _mm256_round_ps(x, _MM_FROUND_TO_NEG_INF | _MM_FROUND_NO_EXC)
|
||||
#define _mmw_ceil_ps(x) _mm256_round_ps(x, _MM_FROUND_TO_POS_INF | _MM_FROUND_NO_EXC)
|
||||
#define _mmw_shuffle_ps _mm256_shuffle_ps
|
||||
#define _mmw_insertf32x4_ps _mm256_insertf128_ps
|
||||
#define _mmw_cvtepi32_ps _mm256_cvtepi32_ps
|
||||
#define _mmw_blendv_epi32(a,b,c) simd_cast<__mwi>(_mmw_blendv_ps(simd_cast<__mw>(a), simd_cast<__mw>(b), simd_cast<__mw>(c)))
|
||||
|
||||
#define _mmw_set1_epi32 _mm256_set1_epi32
|
||||
#define _mmw_setzero_epi32 _mm256_setzero_si256
|
||||
#define _mmw_and_epi32 _mm256_and_si256
|
||||
#define _mmw_or_epi32 _mm256_or_si256
|
||||
#define _mmw_xor_epi32 _mm256_xor_si256
|
||||
#define _mmw_not_epi32(a) _mm256_xor_si256((a), _mm256_set1_epi32(~0))
|
||||
#define _mmw_andnot_epi32 _mm256_andnot_si256
|
||||
#define _mmw_neg_epi32(a) _mm256_sub_epi32(_mm256_set1_epi32(0), (a))
|
||||
#define _mmw_add_epi32 _mm256_add_epi32
|
||||
#define _mmw_sub_epi32 _mm256_sub_epi32
|
||||
#define _mmw_min_epi32 _mm256_min_epi32
|
||||
#define _mmw_max_epi32 _mm256_max_epi32
|
||||
#define _mmw_subs_epu16 _mm256_subs_epu16
|
||||
#define _mmw_mullo_epi32 _mm256_mullo_epi32
|
||||
#define _mmw_cmpeq_epi32 _mm256_cmpeq_epi32
|
||||
#define _mmw_testz_epi32 _mm256_testz_si256
|
||||
#define _mmw_cmpgt_epi32 _mm256_cmpgt_epi32
|
||||
#define _mmw_srai_epi32 _mm256_srai_epi32
|
||||
#define _mmw_srli_epi32 _mm256_srli_epi32
|
||||
#define _mmw_slli_epi32 _mm256_slli_epi32
|
||||
#define _mmw_sllv_ones(x) _mm256_sllv_epi32(SIMD_BITS_ONE, x)
|
||||
#define _mmw_transpose_epi8(x) _mm256_shuffle_epi8(x, SIMD_SHUFFLE_SCANLINE_TO_SUBTILES)
|
||||
#define _mmw_abs_epi32 _mm256_abs_epi32
|
||||
#define _mmw_cvtps_epi32 _mm256_cvtps_epi32
|
||||
#define _mmw_cvttps_epi32 _mm256_cvttps_epi32
|
||||
|
||||
#define _mmx_dp4_ps(a, b) _mm_dp_ps(a, b, 0xFF)
|
||||
#define _mmx_fmadd_ps _mm_fmadd_ps
|
||||
#define _mmx_max_epi32 _mm_max_epi32
|
||||
#define _mmx_min_epi32 _mm_min_epi32
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SIMD casting functions
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename T, typename Y> FORCE_INLINE T simd_cast(Y A);
|
||||
template<> FORCE_INLINE __m128 simd_cast<__m128>(float A) { return _mm_set1_ps(A); }
|
||||
template<> FORCE_INLINE __m128 simd_cast<__m128>(__m128i A) { return _mm_castsi128_ps(A); }
|
||||
template<> FORCE_INLINE __m128 simd_cast<__m128>(__m128 A) { return A; }
|
||||
template<> FORCE_INLINE __m128i simd_cast<__m128i>(int A) { return _mm_set1_epi32(A); }
|
||||
template<> FORCE_INLINE __m128i simd_cast<__m128i>(__m128 A) { return _mm_castps_si128(A); }
|
||||
template<> FORCE_INLINE __m128i simd_cast<__m128i>(__m128i A) { return A; }
|
||||
template<> FORCE_INLINE __m256 simd_cast<__m256>(float A) { return _mm256_set1_ps(A); }
|
||||
template<> FORCE_INLINE __m256 simd_cast<__m256>(__m256i A) { return _mm256_castsi256_ps(A); }
|
||||
template<> FORCE_INLINE __m256 simd_cast<__m256>(__m256 A) { return A; }
|
||||
template<> FORCE_INLINE __m256i simd_cast<__m256i>(int A) { return _mm256_set1_epi32(A); }
|
||||
template<> FORCE_INLINE __m256i simd_cast<__m256i>(__m256 A) { return _mm256_castps_si256(A); }
|
||||
template<> FORCE_INLINE __m256i simd_cast<__m256i>(__m256i A) { return A; }
|
||||
|
||||
#define MAKE_ACCESSOR(name, simd_type, base_type, is_const, elements) \
|
||||
FORCE_INLINE is_const base_type * name(is_const simd_type &a) { \
|
||||
union accessor { simd_type m_native; base_type m_array[elements]; }; \
|
||||
is_const accessor *acs = reinterpret_cast<is_const accessor*>(&a); \
|
||||
return acs->m_array; \
|
||||
}
|
||||
|
||||
MAKE_ACCESSOR(simd_f32, __m128, float, , 4)
|
||||
MAKE_ACCESSOR(simd_f32, __m128, float, const, 4)
|
||||
MAKE_ACCESSOR(simd_i32, __m128i, int, , 4)
|
||||
MAKE_ACCESSOR(simd_i32, __m128i, int, const, 4)
|
||||
|
||||
MAKE_ACCESSOR(simd_f32, __m256, float, , 8)
|
||||
MAKE_ACCESSOR(simd_f32, __m256, float, const, 8)
|
||||
MAKE_ACCESSOR(simd_i32, __m256i, int, , 8)
|
||||
MAKE_ACCESSOR(simd_i32, __m256i, int, const, 8)
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Specialized AVX input assembly function for general vertex gather
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef MaskedOcclusionCulling::VertexLayout VertexLayout;
|
||||
|
||||
FORCE_INLINE void GatherVertices(__m256 *vtxX, __m256 *vtxY, __m256 *vtxW, const float *inVtx, const unsigned int *inTrisPtr, int numLanes, const VertexLayout &vtxLayout)
|
||||
{
|
||||
assert(numLanes >= 1);
|
||||
|
||||
const __m256i SIMD_TRI_IDX_OFFSET = _mm256_setr_epi32(0, 3, 6, 9, 12, 15, 18, 21);
|
||||
static const __m256i SIMD_LANE_MASK[9] = {
|
||||
_mm256_setr_epi32( 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
_mm256_setr_epi32(~0, 0, 0, 0, 0, 0, 0, 0),
|
||||
_mm256_setr_epi32(~0, ~0, 0, 0, 0, 0, 0, 0),
|
||||
_mm256_setr_epi32(~0, ~0, ~0, 0, 0, 0, 0, 0),
|
||||
_mm256_setr_epi32(~0, ~0, ~0, ~0, 0, 0, 0, 0),
|
||||
_mm256_setr_epi32(~0, ~0, ~0, ~0, ~0, 0, 0, 0),
|
||||
_mm256_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, 0, 0),
|
||||
_mm256_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, 0),
|
||||
_mm256_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0)
|
||||
};
|
||||
|
||||
// Compute per-lane index list offset that guards against out of bounds memory accesses
|
||||
__m256i safeTriIdxOffset = _mm256_and_si256(SIMD_TRI_IDX_OFFSET, SIMD_LANE_MASK[numLanes]);
|
||||
|
||||
// Fetch triangle indices.
|
||||
__m256i vtxIdx[3];
|
||||
vtxIdx[0] = _mmw_mullo_epi32(_mm256_i32gather_epi32((const int*)inTrisPtr + 0, safeTriIdxOffset, 4), _mmw_set1_epi32(vtxLayout.mStride));
|
||||
vtxIdx[1] = _mmw_mullo_epi32(_mm256_i32gather_epi32((const int*)inTrisPtr + 1, safeTriIdxOffset, 4), _mmw_set1_epi32(vtxLayout.mStride));
|
||||
vtxIdx[2] = _mmw_mullo_epi32(_mm256_i32gather_epi32((const int*)inTrisPtr + 2, safeTriIdxOffset, 4), _mmw_set1_epi32(vtxLayout.mStride));
|
||||
|
||||
char *vPtr = (char *)inVtx;
|
||||
|
||||
// Fetch triangle vertices
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
vtxX[i] = _mm256_i32gather_ps((float *)vPtr, vtxIdx[i], 1);
|
||||
vtxY[i] = _mm256_i32gather_ps((float *)(vPtr + vtxLayout.mOffsetY), vtxIdx[i], 1);
|
||||
vtxW[i] = _mm256_i32gather_ps((float *)(vPtr + vtxLayout.mOffsetW), vtxIdx[i], 1);
|
||||
}
|
||||
}
|
||||
|
||||
namespace MaskedOcclusionCullingAVX2
|
||||
{
|
||||
static MaskedOcclusionCulling::Implementation gInstructionSet = MaskedOcclusionCulling::AVX2;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Include common algorithm implementation (general, SIMD independent code)
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "MaskedOcclusionCullingCommon.inl"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Utility function to create a new object using the allocator callbacks
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef MaskedOcclusionCulling::pfnAlignedAlloc pfnAlignedAlloc;
|
||||
typedef MaskedOcclusionCulling::pfnAlignedFree pfnAlignedFree;
|
||||
|
||||
MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree)
|
||||
{
|
||||
MaskedOcclusionCullingPrivate *object = (MaskedOcclusionCullingPrivate *)alignedAlloc(64, sizeof(MaskedOcclusionCullingPrivate));
|
||||
new (object) MaskedOcclusionCullingPrivate(alignedAlloc, alignedFree);
|
||||
return object;
|
||||
}
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
namespace MaskedOcclusionCullingAVX2
|
||||
{
|
||||
typedef MaskedOcclusionCulling::pfnAlignedAlloc pfnAlignedAlloc;
|
||||
typedef MaskedOcclusionCulling::pfnAlignedFree pfnAlignedFree;
|
||||
|
||||
MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
+309
@@ -0,0 +1,309 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright 2017 Intel Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
// use this file except in compliance with the License. You may obtain a copy
|
||||
// of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <float.h>
|
||||
#include "MaskedOcclusionCulling.h"
|
||||
#include "CompilerSpecific.inl"
|
||||
|
||||
#if MOC_RECORDER_ENABLE
|
||||
#include "FrameRecorder.h"
|
||||
#endif
|
||||
|
||||
// Make sure compiler supports AVX-512 intrinsics: Visual Studio 2017 (Update 3) || Intel C++ Compiler 16.0 || Clang 4.0 || GCC 5.0
|
||||
#if USE_AVX512 != 0 && ((defined(_MSC_VER) && _MSC_VER >= 1911) || (defined(__INTEL_COMPILER) && __INTEL_COMPILER >= 1600) || (defined(__clang__) && __clang_major__ >= 4) || (defined(__GNUC__) && __GNUC__ >= 5))
|
||||
|
||||
// The MaskedOcclusionCullingAVX512.cpp file should be compiled avx2/avx512 architecture options turned on in the compiler. However, the SSE
|
||||
// version in MaskedOcclusionCulling.cpp _must_ be compiled with SSE2 architecture allow backwards compatibility. Best practice is to
|
||||
// use lowest supported target platform (e.g. /arch:SSE2) as project default, and elevate only the MaskedOcclusionCullingAVX2/512.cpp files.
|
||||
#ifndef __AVX2__
|
||||
#error For best performance, MaskedOcclusionCullingAVX512.cpp should be compiled with /arch:AVX2
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// AVX specific defines and constants
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define SIMD_LANES 16
|
||||
#define TILE_HEIGHT_SHIFT 4
|
||||
|
||||
#define SIMD_LANE_IDX _mm512_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
|
||||
|
||||
#define SIMD_SUB_TILE_COL_OFFSET _mm512_setr_epi32(0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3)
|
||||
#define SIMD_SUB_TILE_ROW_OFFSET _mm512_setr_epi32(0, 0, 0, 0, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 3, SUB_TILE_HEIGHT * 3, SUB_TILE_HEIGHT * 3, SUB_TILE_HEIGHT * 3)
|
||||
#define SIMD_SUB_TILE_COL_OFFSET_F _mm512_setr_ps(0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3, 0, SUB_TILE_WIDTH, SUB_TILE_WIDTH * 2, SUB_TILE_WIDTH * 3)
|
||||
#define SIMD_SUB_TILE_ROW_OFFSET_F _mm512_setr_ps(0, 0, 0, 0, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 2, SUB_TILE_HEIGHT * 3, SUB_TILE_HEIGHT * 3, SUB_TILE_HEIGHT * 3, SUB_TILE_HEIGHT * 3)
|
||||
|
||||
#define SIMD_SHUFFLE_SCANLINE_TO_SUBTILES _mm512_set_epi32(0x0F0B0703, 0x0E0A0602, 0x0D090501, 0x0C080400, 0x0F0B0703, 0x0E0A0602, 0x0D090501, 0x0C080400, 0x0F0B0703, 0x0E0A0602, 0x0D090501, 0x0C080400, 0x0F0B0703, 0x0E0A0602, 0x0D090501, 0x0C080400)
|
||||
|
||||
#define SIMD_LANE_YCOORD_I _mm512_setr_epi32(128, 384, 640, 896, 1152, 1408, 1664, 1920, 2176, 2432, 2688, 2944, 3200, 3456, 3712, 3968)
|
||||
#define SIMD_LANE_YCOORD_F _mm512_setr_ps(128.0f, 384.0f, 640.0f, 896.0f, 1152.0f, 1408.0f, 1664.0f, 1920.0f, 2176.0f, 2432.0f, 2688.0f, 2944.0f, 3200.0f, 3456.0f, 3712.0f, 3968.0f)
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// AVX specific typedefs and functions
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef __m512 __mw;
|
||||
typedef __m512i __mwi;
|
||||
|
||||
#define _mmw_set1_ps _mm512_set1_ps
|
||||
#define _mmw_setzero_ps _mm512_setzero_ps
|
||||
#define _mmw_and_ps _mm512_and_ps
|
||||
#define _mmw_or_ps _mm512_or_ps
|
||||
#define _mmw_xor_ps _mm512_xor_ps
|
||||
#define _mmw_not_ps(a) _mm512_xor_ps((a), _mm512_castsi512_ps(_mm512_set1_epi32(~0)))
|
||||
#define _mmw_andnot_ps _mm512_andnot_ps
|
||||
#define _mmw_neg_ps(a) _mm512_xor_ps((a), _mm512_set1_ps(-0.0f))
|
||||
#define _mmw_abs_ps(a) _mm512_and_ps((a), _mm512_castsi512_ps(_mm512_set1_epi32(0x7FFFFFFF)))
|
||||
#define _mmw_add_ps _mm512_add_ps
|
||||
#define _mmw_sub_ps _mm512_sub_ps
|
||||
#define _mmw_mul_ps _mm512_mul_ps
|
||||
#define _mmw_div_ps _mm512_div_ps
|
||||
#define _mmw_min_ps _mm512_min_ps
|
||||
#define _mmw_max_ps _mm512_max_ps
|
||||
#define _mmw_fmadd_ps _mm512_fmadd_ps
|
||||
#define _mmw_fmsub_ps _mm512_fmsub_ps
|
||||
#define _mmw_shuffle_ps _mm512_shuffle_ps
|
||||
#define _mmw_insertf32x4_ps _mm512_insertf32x4
|
||||
#define _mmw_cvtepi32_ps _mm512_cvtepi32_ps
|
||||
#define _mmw_blendv_epi32(a,b,c) simd_cast<__mwi>(_mmw_blendv_ps(simd_cast<__mw>(a), simd_cast<__mw>(b), simd_cast<__mw>(c)))
|
||||
|
||||
#define _mmw_set1_epi32 _mm512_set1_epi32
|
||||
#define _mmw_setzero_epi32 _mm512_setzero_si512
|
||||
#define _mmw_and_epi32 _mm512_and_si512
|
||||
#define _mmw_or_epi32 _mm512_or_si512
|
||||
#define _mmw_xor_epi32 _mm512_xor_si512
|
||||
#define _mmw_not_epi32(a) _mm512_xor_si512((a), _mm512_set1_epi32(~0))
|
||||
#define _mmw_andnot_epi32 _mm512_andnot_si512
|
||||
#define _mmw_neg_epi32(a) _mm512_sub_epi32(_mm512_set1_epi32(0), (a))
|
||||
#define _mmw_add_epi32 _mm512_add_epi32
|
||||
#define _mmw_sub_epi32 _mm512_sub_epi32
|
||||
#define _mmw_min_epi32 _mm512_min_epi32
|
||||
#define _mmw_max_epi32 _mm512_max_epi32
|
||||
#define _mmw_subs_epu16 _mm512_subs_epu16
|
||||
#define _mmw_mullo_epi32 _mm512_mullo_epi32
|
||||
#define _mmw_srai_epi32 _mm512_srai_epi32
|
||||
#define _mmw_srli_epi32 _mm512_srli_epi32
|
||||
#define _mmw_slli_epi32 _mm512_slli_epi32
|
||||
#define _mmw_sllv_ones(x) _mm512_sllv_epi32(SIMD_BITS_ONE, x)
|
||||
#define _mmw_transpose_epi8(x) _mm512_shuffle_epi8(x, SIMD_SHUFFLE_SCANLINE_TO_SUBTILES)
|
||||
#define _mmw_abs_epi32 _mm512_abs_epi32
|
||||
#define _mmw_cvtps_epi32 _mm512_cvtps_epi32
|
||||
#define _mmw_cvttps_epi32 _mm512_cvttps_epi32
|
||||
|
||||
#define _mmx_dp4_ps(a, b) _mm_dp_ps(a, b, 0xFF)
|
||||
#define _mmx_fmadd_ps _mm_fmadd_ps
|
||||
#define _mmx_max_epi32 _mm_max_epi32
|
||||
#define _mmx_min_epi32 _mm_min_epi32
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SIMD casting functions
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename T, typename Y> FORCE_INLINE T simd_cast(Y A);
|
||||
template<> FORCE_INLINE __m128 simd_cast<__m128>(float A) { return _mm_set1_ps(A); }
|
||||
template<> FORCE_INLINE __m128 simd_cast<__m128>(__m128i A) { return _mm_castsi128_ps(A); }
|
||||
template<> FORCE_INLINE __m128 simd_cast<__m128>(__m128 A) { return A; }
|
||||
template<> FORCE_INLINE __m128i simd_cast<__m128i>(int A) { return _mm_set1_epi32(A); }
|
||||
template<> FORCE_INLINE __m128i simd_cast<__m128i>(__m128 A) { return _mm_castps_si128(A); }
|
||||
template<> FORCE_INLINE __m128i simd_cast<__m128i>(__m128i A) { return A; }
|
||||
template<> FORCE_INLINE __m256 simd_cast<__m256>(float A) { return _mm256_set1_ps(A); }
|
||||
template<> FORCE_INLINE __m256 simd_cast<__m256>(__m256i A) { return _mm256_castsi256_ps(A); }
|
||||
template<> FORCE_INLINE __m256 simd_cast<__m256>(__m256 A) { return A; }
|
||||
template<> FORCE_INLINE __m256i simd_cast<__m256i>(int A) { return _mm256_set1_epi32(A); }
|
||||
template<> FORCE_INLINE __m256i simd_cast<__m256i>(__m256 A) { return _mm256_castps_si256(A); }
|
||||
template<> FORCE_INLINE __m256i simd_cast<__m256i>(__m256i A) { return A; }
|
||||
template<> FORCE_INLINE __m512 simd_cast<__m512>(float A) { return _mm512_set1_ps(A); }
|
||||
template<> FORCE_INLINE __m512 simd_cast<__m512>(__m512i A) { return _mm512_castsi512_ps(A); }
|
||||
template<> FORCE_INLINE __m512 simd_cast<__m512>(__m512 A) { return A; }
|
||||
template<> FORCE_INLINE __m512i simd_cast<__m512i>(int A) { return _mm512_set1_epi32(A); }
|
||||
template<> FORCE_INLINE __m512i simd_cast<__m512i>(__m512 A) { return _mm512_castps_si512(A); }
|
||||
template<> FORCE_INLINE __m512i simd_cast<__m512i>(__m512i A) { return A; }
|
||||
|
||||
#define MAKE_ACCESSOR(name, simd_type, base_type, is_const, elements) \
|
||||
FORCE_INLINE is_const base_type * name(is_const simd_type &a) { \
|
||||
union accessor { simd_type m_native; base_type m_array[elements]; }; \
|
||||
is_const accessor *acs = reinterpret_cast<is_const accessor*>(&a); \
|
||||
return acs->m_array; \
|
||||
}
|
||||
|
||||
MAKE_ACCESSOR(simd_f32, __m128, float, , 4)
|
||||
MAKE_ACCESSOR(simd_f32, __m128, float, const, 4)
|
||||
MAKE_ACCESSOR(simd_i32, __m128i, int, , 4)
|
||||
MAKE_ACCESSOR(simd_i32, __m128i, int, const, 4)
|
||||
|
||||
MAKE_ACCESSOR(simd_f32, __m256, float, , 8)
|
||||
MAKE_ACCESSOR(simd_f32, __m256, float, const, 8)
|
||||
MAKE_ACCESSOR(simd_i32, __m256i, int, , 8)
|
||||
MAKE_ACCESSOR(simd_i32, __m256i, int, const, 8)
|
||||
|
||||
MAKE_ACCESSOR(simd_f32, __m512, float, , 16)
|
||||
MAKE_ACCESSOR(simd_f32, __m512, float, const, 16)
|
||||
MAKE_ACCESSOR(simd_i32, __m512i, int, , 16)
|
||||
MAKE_ACCESSOR(simd_i32, __m512i, int, const, 16)
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Specialized AVX input assembly function for general vertex gather
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef MaskedOcclusionCulling::VertexLayout VertexLayout;
|
||||
|
||||
FORCE_INLINE void GatherVertices(__m512 *vtxX, __m512 *vtxY, __m512 *vtxW, const float *inVtx, const unsigned int *inTrisPtr, int numLanes, const VertexLayout &vtxLayout)
|
||||
{
|
||||
assert(numLanes >= 1);
|
||||
|
||||
const __m512i SIMD_TRI_IDX_OFFSET = _mm512_setr_epi32(0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45);
|
||||
static const __m512i SIMD_LANE_MASK[17] = {
|
||||
_mm512_setr_epi32( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
_mm512_setr_epi32(~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
_mm512_setr_epi32(~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
_mm512_setr_epi32(~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
_mm512_setr_epi32(~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
_mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
_mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
_mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
_mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
_mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0, 0),
|
||||
_mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0, 0),
|
||||
_mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0, 0),
|
||||
_mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0, 0),
|
||||
_mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0, 0),
|
||||
_mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0, 0),
|
||||
_mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 0),
|
||||
_mm512_setr_epi32(~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0)
|
||||
};
|
||||
|
||||
// Compute per-lane index list offset that guards against out of bounds memory accesses
|
||||
__m512i safeTriIdxOffset = _mm512_and_si512(SIMD_TRI_IDX_OFFSET, SIMD_LANE_MASK[numLanes]);
|
||||
|
||||
// Fetch triangle indices.
|
||||
__m512i vtxIdx[3];
|
||||
vtxIdx[0] = _mmw_mullo_epi32(_mm512_i32gather_epi32(safeTriIdxOffset, (const int*)inTrisPtr + 0, 4), _mmw_set1_epi32(vtxLayout.mStride));
|
||||
vtxIdx[1] = _mmw_mullo_epi32(_mm512_i32gather_epi32(safeTriIdxOffset, (const int*)inTrisPtr + 1, 4), _mmw_set1_epi32(vtxLayout.mStride));
|
||||
vtxIdx[2] = _mmw_mullo_epi32(_mm512_i32gather_epi32(safeTriIdxOffset, (const int*)inTrisPtr + 2, 4), _mmw_set1_epi32(vtxLayout.mStride));
|
||||
|
||||
char *vPtr = (char *)inVtx;
|
||||
|
||||
// Fetch triangle vertices
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
vtxX[i] = _mm512_i32gather_ps(vtxIdx[i], (float *)vPtr, 1);
|
||||
vtxY[i] = _mm512_i32gather_ps(vtxIdx[i], (float *)(vPtr + vtxLayout.mOffsetY), 1);
|
||||
vtxW[i] = _mm512_i32gather_ps(vtxIdx[i], (float *)(vPtr + vtxLayout.mOffsetW), 1);
|
||||
}
|
||||
}
|
||||
|
||||
namespace MaskedOcclusionCullingAVX512
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Poorly implemented functions. TODO: fix common (maskedOcclusionCullingCommon.inl) code to improve perf
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FORCE_INLINE __m512 _mmw_floor_ps(__m512 x)
|
||||
{
|
||||
return _mm512_roundscale_ps(x, 1); // 1 = floor
|
||||
}
|
||||
|
||||
FORCE_INLINE __m512 _mmw_ceil_ps(__m512 x)
|
||||
{
|
||||
return _mm512_roundscale_ps(x, 2); // 2 = ceil
|
||||
}
|
||||
|
||||
FORCE_INLINE __m512i _mmw_cmpeq_epi32(__m512i a, __m512i b)
|
||||
{
|
||||
__mmask16 mask = _mm512_cmpeq_epi32_mask(a, b);
|
||||
return _mm512_mask_mov_epi32(_mm512_set1_epi32(0), mask, _mm512_set1_epi32(~0));
|
||||
}
|
||||
|
||||
FORCE_INLINE __m512i _mmw_cmpgt_epi32(__m512i a, __m512i b)
|
||||
{
|
||||
__mmask16 mask = _mm512_cmpgt_epi32_mask(a, b);
|
||||
return _mm512_mask_mov_epi32(_mm512_set1_epi32(0), mask, _mm512_set1_epi32(~0));
|
||||
}
|
||||
|
||||
FORCE_INLINE bool _mmw_testz_epi32(__m512i a, __m512i b)
|
||||
{
|
||||
__mmask16 mask = _mm512_cmpeq_epi32_mask(_mm512_and_si512(a, b), _mm512_set1_epi32(0));
|
||||
return mask == 0xFFFF;
|
||||
}
|
||||
|
||||
FORCE_INLINE __m512 _mmw_cmpge_ps(__m512 a, __m512 b)
|
||||
{
|
||||
__mmask16 mask = _mm512_cmp_ps_mask(a, b, _CMP_GE_OQ);
|
||||
return _mm512_castsi512_ps(_mm512_mask_mov_epi32(_mm512_set1_epi32(0), mask, _mm512_set1_epi32(~0)));
|
||||
}
|
||||
|
||||
FORCE_INLINE __m512 _mmw_cmpgt_ps(__m512 a, __m512 b)
|
||||
{
|
||||
__mmask16 mask = _mm512_cmp_ps_mask(a, b, _CMP_GT_OQ);
|
||||
return _mm512_castsi512_ps(_mm512_mask_mov_epi32(_mm512_set1_epi32(0), mask, _mm512_set1_epi32(~0)));
|
||||
}
|
||||
|
||||
FORCE_INLINE __m512 _mmw_cmpeq_ps(__m512 a, __m512 b)
|
||||
{
|
||||
__mmask16 mask = _mm512_cmp_ps_mask(a, b, _CMP_EQ_OQ);
|
||||
return _mm512_castsi512_ps(_mm512_mask_mov_epi32(_mm512_set1_epi32(0), mask, _mm512_set1_epi32(~0)));
|
||||
}
|
||||
|
||||
FORCE_INLINE __mmask16 _mmw_movemask_ps(const __m512 &a)
|
||||
{
|
||||
__mmask16 mask = _mm512_cmp_epi32_mask(_mm512_and_si512(_mm512_castps_si512(a), _mm512_set1_epi32(0x80000000)), _mm512_set1_epi32(0), 4); // a & 0x8000000 != 0
|
||||
return mask;
|
||||
}
|
||||
|
||||
FORCE_INLINE __m512 _mmw_blendv_ps(const __m512 &a, const __m512 &b, const __m512 &c)
|
||||
{
|
||||
__mmask16 mask = _mmw_movemask_ps(c);
|
||||
return _mm512_mask_mov_ps(a, mask, b);
|
||||
}
|
||||
|
||||
static MaskedOcclusionCulling::Implementation gInstructionSet = MaskedOcclusionCulling::AVX512;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Include common algorithm implementation (general, SIMD independent code)
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "MaskedOcclusionCullingCommon.inl"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Utility function to create a new object using the allocator callbacks
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef MaskedOcclusionCulling::pfnAlignedAlloc pfnAlignedAlloc;
|
||||
typedef MaskedOcclusionCulling::pfnAlignedFree pfnAlignedFree;
|
||||
|
||||
MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree)
|
||||
{
|
||||
MaskedOcclusionCullingPrivate *object = (MaskedOcclusionCullingPrivate *)alignedAlloc(64, sizeof(MaskedOcclusionCullingPrivate));
|
||||
new (object) MaskedOcclusionCullingPrivate(alignedAlloc, alignedFree);
|
||||
return object;
|
||||
}
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
namespace MaskedOcclusionCullingAVX512
|
||||
{
|
||||
typedef MaskedOcclusionCulling::pfnAlignedAlloc pfnAlignedAlloc;
|
||||
typedef MaskedOcclusionCulling::pfnAlignedFree pfnAlignedFree;
|
||||
|
||||
MaskedOcclusionCulling *CreateMaskedOcclusionCulling(pfnAlignedAlloc alignedAlloc, pfnAlignedFree alignedFree)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
+2053
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"PackageName": "Masked Occlusion Culling",
|
||||
"URL": "https://software.intel.com/content/www/us/en/develop/articles/masked-software-occlusion-culling.html",
|
||||
"License": "Apache 2.0",
|
||||
"LicenseFile": "LICENSE.txt"
|
||||
}
|
||||
@@ -31,7 +31,6 @@
|
||||
#include <AzFramework/Visibility/IVisibilitySystem.h>
|
||||
|
||||
#include <Atom/RPI.Public/View.h>
|
||||
|
||||
#include <Atom/RHI/DrawList.h>
|
||||
|
||||
#include <AtomCore/std/parallel/concurrency_checker.h>
|
||||
@@ -97,6 +96,9 @@ namespace AZ
|
||||
};
|
||||
LodData m_lodData;
|
||||
|
||||
//! Flag indicating if the object is visible, i.e., was not culled out in the last frame
|
||||
bool m_isVisible = true;
|
||||
|
||||
void SetDebugName([[maybe_unused]] const AZ::Name& debugName)
|
||||
{
|
||||
#ifdef AZ_CULL_DEBUG_ENABLED
|
||||
@@ -213,6 +215,21 @@ namespace AZ
|
||||
void Activate(const class Scene* parentScene);
|
||||
void Deactivate();
|
||||
|
||||
struct OcclusionPlane
|
||||
{
|
||||
// World space corners of the occluson plane
|
||||
Vector3 m_cornerBL;
|
||||
Vector3 m_cornerTL;
|
||||
Vector3 m_cornerTR;
|
||||
Vector3 m_cornerBR;
|
||||
|
||||
Aabb m_aabb;
|
||||
};
|
||||
using OcclusionPlaneVector = AZStd::vector<OcclusionPlane>;
|
||||
|
||||
//! Sets a list of occlusion planes to be used during the culling process.
|
||||
void SetOcclusionPlanes(const OcclusionPlaneVector& occlusionPlanes) { m_occlusionPlanes = occlusionPlanes; }
|
||||
|
||||
//! Notifies the CullingScene that culling will begin for this frame.
|
||||
void BeginCulling(const AZStd::vector<ViewPtr>& views);
|
||||
|
||||
@@ -251,12 +268,9 @@ namespace AZ
|
||||
|
||||
const Scene* m_parentScene = nullptr;
|
||||
AzFramework::IVisibilityScene* m_visScene = nullptr;
|
||||
|
||||
CullingDebugContext m_debugCtx;
|
||||
|
||||
AZStd::concurrency_checker m_cullDataConcurrencyCheck;
|
||||
|
||||
AZStd::mutex m_mutex;
|
||||
OcclusionPlaneVector m_occlusionPlanes;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/Name/Name.h>
|
||||
|
||||
class MaskedOcclusionCulling;
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RHI
|
||||
@@ -57,7 +59,7 @@ namespace AZ
|
||||
//! Only use this function to create a new view object. And force using smart pointer to manage view's life time
|
||||
static ViewPtr CreateView(const AZ::Name& name, UsageFlags usage);
|
||||
|
||||
~View() = default;
|
||||
~View();
|
||||
|
||||
void SetDrawListMask(const RHI::DrawListMask& drawListMask);
|
||||
RHI::DrawListMask GetDrawListMask() const { return m_drawListMask; }
|
||||
@@ -126,6 +128,12 @@ namespace AZ
|
||||
//! Notifies consumers when the world to clip matrix has changed.
|
||||
void ConnectWorldToClipMatrixChangedHandler(MatrixChangedEvent::Handler& handler);
|
||||
|
||||
//! Prepare for view culling
|
||||
void BeginCulling();
|
||||
|
||||
//! Returns the masked occlusion culling interface
|
||||
MaskedOcclusionCulling* GetMaskedOcclusionCulling();
|
||||
|
||||
private:
|
||||
View() = delete;
|
||||
View(const AZ::Name& name, UsageFlags usage);
|
||||
@@ -193,6 +201,9 @@ namespace AZ
|
||||
|
||||
MatrixChangedEvent m_onWorldToClipMatrixChange;
|
||||
MatrixChangedEvent m_onWorldToViewMatrixChange;
|
||||
|
||||
// Masked Occlusion Culling interface
|
||||
MaskedOcclusionCulling* m_maskedOcclusionCulling = nullptr;
|
||||
};
|
||||
|
||||
AZ_DEFINE_ENUM_BITWISE_OPERATORS(View::UsageFlags);
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#define AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED 0
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "Atom_RPI_Traits_Android.h"
|
||||
@@ -0,0 +1,13 @@
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
|
||||
set (PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED FALSE)
|
||||
set (PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED FALSE)
|
||||
@@ -0,0 +1,15 @@
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Atom_RPI_Traits_Platform.h
|
||||
Atom_RPI_Traits_Android.h
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#define AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED 0
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "Atom_RPI_Traits_Linux.h"
|
||||
@@ -10,3 +10,4 @@
|
||||
#
|
||||
|
||||
set (PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED FALSE)
|
||||
set (PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED FALSE)
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Atom_RPI_Traits_Platform.h
|
||||
Atom_RPI_Traits_Linux.h
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#define AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED 0
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "Atom_RPI_Traits_Mac.h"
|
||||
@@ -10,3 +10,4 @@
|
||||
#
|
||||
|
||||
set (PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED TRUE)
|
||||
set (PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED FALSE)
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Atom_RPI_Traits_Platform.h
|
||||
Atom_RPI_Traits_Mac.h
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "Atom_RPI_Traits_Windows.h"
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#define AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED 1
|
||||
@@ -10,3 +10,17 @@
|
||||
#
|
||||
|
||||
set (PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED TRUE)
|
||||
set (PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED TRUE)
|
||||
|
||||
ly_add_source_properties(
|
||||
SOURCES External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp
|
||||
PROPERTY COMPILE_OPTIONS
|
||||
VALUES /arch:AVX2 /W3
|
||||
)
|
||||
ly_add_source_properties(
|
||||
SOURCES
|
||||
External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp
|
||||
External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp
|
||||
PROPERTY COMPILE_OPTIONS
|
||||
VALUES /W3
|
||||
)
|
||||
@@ -0,0 +1,15 @@
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Atom_RPI_Traits_Platform.h
|
||||
Atom_RPI_Traits_Windows.h
|
||||
)
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "Atom_RPI_Traits_iOS.h"
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#define AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED 0
|
||||
@@ -0,0 +1,13 @@
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
|
||||
set (PAL_TRAIT_BUILD_ATOM_RPI_ASSETS_SUPPORTED FALSE)
|
||||
set (PAL_TRAIT_BUILD_ATOM_RPI_MASKED_OCCLUSION_CULLING_SUPPORTED FALSE)
|
||||
@@ -0,0 +1,15 @@
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Atom_RPI_Traits_Platform.h
|
||||
Atom_RPI_Traits_iOS.h
|
||||
)
|
||||
@@ -20,15 +20,20 @@
|
||||
|
||||
#include <Atom/RHI/CpuProfiler.h>
|
||||
|
||||
#include <AzCore/Math/MatrixUtils.h>
|
||||
#include <AzCore/Math/ShapeIntersection.h>
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
|
||||
#include <AzCore/std/parallel/lock.h>
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
#include <AzCore/Debug/EventTrace.h>
|
||||
#include <AzCore/Debug/Timer.h>
|
||||
#include <AzCore/Jobs/JobFunction.h>
|
||||
#include <AzCore/Jobs/Job.h>
|
||||
#include <Atom_RPI_Traits_Platform.h>
|
||||
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
#include <MaskedOcclusionCulling/MaskedOcclusionCulling.h>
|
||||
#endif
|
||||
|
||||
//Enables more inner-loop profiling scopes (can create high overhead in RadTelemetry if there are many-many objects in a scene)
|
||||
//#define AZ_CULL_PROFILE_DETAILED
|
||||
@@ -272,21 +277,26 @@ namespace AZ
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(AddObjectsToViewJob, ThreadPoolAllocator, 0);
|
||||
|
||||
struct JobData
|
||||
{
|
||||
CullingDebugContext* m_debugCtx = nullptr;
|
||||
const Scene* m_scene = nullptr;
|
||||
View* m_view = nullptr;
|
||||
Frustum m_frustum;
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
MaskedOcclusionCulling* m_maskedOcclusionCulling = nullptr;
|
||||
#endif
|
||||
};
|
||||
|
||||
private:
|
||||
CullingDebugContext* m_debugCtx;
|
||||
const Scene* m_scene;
|
||||
View* m_view;
|
||||
Frustum m_frustum;
|
||||
const AZStd::shared_ptr<JobData> m_jobData;
|
||||
CullingScene::WorkListType m_worklist;
|
||||
|
||||
public:
|
||||
AddObjectsToViewJob(CullingDebugContext& debugCtx, const Scene& scene, View& view, Frustum& frustum, CullingScene::WorkListType& worklist)
|
||||
AddObjectsToViewJob(const AZStd::shared_ptr<AddObjectsToViewJob::JobData>& jobData, CullingScene::WorkListType& worklist)
|
||||
: Job(true, nullptr) //auto-deletes, no JobContext
|
||||
, m_debugCtx(&debugCtx)
|
||||
, m_scene(&scene)
|
||||
, m_view(&view)
|
||||
, m_frustum(frustum) //capture by value
|
||||
, m_worklist(AZStd::move(worklist)) //capture by value
|
||||
, m_jobData(jobData)
|
||||
, m_worklist(worklist)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -295,37 +305,50 @@ namespace AZ
|
||||
{
|
||||
AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender);
|
||||
|
||||
const View::UsageFlags viewFlags = m_view->GetUsageFlags();
|
||||
const RHI::DrawListMask drawListMask = m_view->GetDrawListMask();
|
||||
const View::UsageFlags viewFlags = m_jobData->m_view->GetUsageFlags();
|
||||
const RHI::DrawListMask drawListMask = m_jobData->m_view->GetDrawListMask();
|
||||
uint32_t numDrawPackets = 0;
|
||||
uint32_t numVisibleCullables = 0;
|
||||
|
||||
for (const AzFramework::IVisibilityScene::NodeData& nodeData : m_worklist)
|
||||
{
|
||||
//If a node is entirely contained within the frustum, then we can skip the fine grained culling.
|
||||
bool nodeIsContainedInFrustum = ShapeIntersection::Contains(m_frustum, nodeData.m_bounds);
|
||||
bool nodeIsContainedInFrustum = ShapeIntersection::Contains(m_jobData->m_frustum, nodeData.m_bounds);
|
||||
|
||||
#ifdef AZ_CULL_PROFILE_VERBOSE
|
||||
AZ_PROFILE_SCOPE_DYNAMIC(Debug::ProfileCategory::AzRender, "process node (view: %s, skip fine cull: %d",
|
||||
m_view->GetName().GetCStr(), nodeIsContainedInFrustum ? 1 : 0);
|
||||
#endif
|
||||
|
||||
if (nodeIsContainedInFrustum || !m_debugCtx->m_enableFrustumCulling)
|
||||
if (nodeIsContainedInFrustum || !m_jobData->m_debugCtx->m_enableFrustumCulling)
|
||||
{
|
||||
//Add all objects within this node to the view, without any extra culling
|
||||
for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries)
|
||||
{
|
||||
if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable)
|
||||
{
|
||||
Cullable* c = static_cast<Cullable*>(visibleEntry->m_userData);
|
||||
if ((c->m_cullData.m_drawListMask & drawListMask).none() ||
|
||||
c->m_cullData.m_hideFlags & viewFlags ||
|
||||
c->m_cullData.m_scene != m_scene) //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this
|
||||
if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable)
|
||||
{
|
||||
continue;
|
||||
Cullable* c = static_cast<Cullable*>(visibleEntry->m_userData);
|
||||
|
||||
// reset visibility flag to false, update to true if all culling checks pass
|
||||
c->m_isVisible = false;
|
||||
|
||||
if ((c->m_cullData.m_drawListMask & drawListMask).none() ||
|
||||
c->m_cullData.m_hideFlags & viewFlags ||
|
||||
c->m_cullData.m_scene != m_jobData->m_scene) //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
if (TestOcclusionCulling(visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE)
|
||||
#endif
|
||||
{
|
||||
numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_jobData->m_view);
|
||||
++numVisibleCullables;
|
||||
c->m_isVisible = true;
|
||||
}
|
||||
}
|
||||
numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_view);
|
||||
++numVisibleCullables;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -337,68 +360,78 @@ namespace AZ
|
||||
if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable)
|
||||
{
|
||||
Cullable* c = static_cast<Cullable*>(visibleEntry->m_userData);
|
||||
|
||||
// reset visibility flag to false, update to true if all culling checks pass
|
||||
c->m_isVisible = false;
|
||||
|
||||
if ((c->m_cullData.m_drawListMask & drawListMask).none() ||
|
||||
c->m_cullData.m_hideFlags & viewFlags ||
|
||||
c->m_cullData.m_scene != m_scene) //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this
|
||||
c->m_cullData.m_scene != m_jobData->m_scene) //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
IntersectResult res = ShapeIntersection::Classify(m_frustum, c->m_cullData.m_boundingSphere);
|
||||
IntersectResult res = ShapeIntersection::Classify(m_jobData->m_frustum, c->m_cullData.m_boundingSphere);
|
||||
if (res == IntersectResult::Exterior)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (res == IntersectResult::Interior || ShapeIntersection::Overlaps(m_frustum, c->m_cullData.m_boundingObb))
|
||||
else if (res == IntersectResult::Interior || ShapeIntersection::Overlaps(m_jobData->m_frustum, c->m_cullData.m_boundingObb))
|
||||
{
|
||||
numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_view);
|
||||
++numVisibleCullables;
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
if (TestOcclusionCulling(visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE)
|
||||
#endif
|
||||
{
|
||||
numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_jobData->m_view);
|
||||
++numVisibleCullables;
|
||||
c->m_isVisible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_debugCtx->m_debugDraw && (m_view->GetName() == m_debugCtx->m_currentViewSelectionName))
|
||||
if (m_jobData->m_debugCtx->m_debugDraw && (m_jobData->m_view->GetName() == m_jobData->m_debugCtx->m_currentViewSelectionName))
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Debug::ProfileCategory::AzRender, "debug draw culling");
|
||||
|
||||
AuxGeomDrawPtr auxGeomPtr = AuxGeomFeatureProcessorInterface::GetDrawQueueForScene(m_scene);
|
||||
AuxGeomDrawPtr auxGeomPtr = AuxGeomFeatureProcessorInterface::GetDrawQueueForScene(m_jobData->m_scene);
|
||||
if (auxGeomPtr)
|
||||
{
|
||||
//Draw the node bounds
|
||||
// "Fully visible" nodes are nodes that are fully inside the frustum. "Partially visible" nodes intersect the edges of the frustum.
|
||||
// Since the nodes of an octree have lots of overlapping boxes with coplanar edges, it's easier to view these separately, so
|
||||
// we have a few debug booleans to toggle which ones to draw.
|
||||
if (nodeIsContainedInFrustum && m_debugCtx->m_drawFullyVisibleNodes)
|
||||
if (nodeIsContainedInFrustum && m_jobData->m_debugCtx->m_drawFullyVisibleNodes)
|
||||
{
|
||||
auxGeomPtr->DrawAabb(nodeData.m_bounds, Colors::Lime, RPI::AuxGeomDraw::DrawStyle::Line, RPI::AuxGeomDraw::DepthTest::Off);
|
||||
}
|
||||
else if (!nodeIsContainedInFrustum && m_debugCtx->m_drawPartiallyVisibleNodes)
|
||||
else if (!nodeIsContainedInFrustum && m_jobData->m_debugCtx->m_drawPartiallyVisibleNodes)
|
||||
{
|
||||
auxGeomPtr->DrawAabb(nodeData.m_bounds, Colors::Yellow, RPI::AuxGeomDraw::DrawStyle::Line, RPI::AuxGeomDraw::DepthTest::Off);
|
||||
}
|
||||
|
||||
//Draw bounds on individual objects
|
||||
if (m_debugCtx->m_drawBoundingBoxes || m_debugCtx->m_drawBoundingSpheres || m_debugCtx->m_drawLodRadii)
|
||||
if (m_jobData->m_debugCtx->m_drawBoundingBoxes || m_jobData->m_debugCtx->m_drawBoundingSpheres || m_jobData->m_debugCtx->m_drawLodRadii)
|
||||
{
|
||||
for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries)
|
||||
{
|
||||
if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable)
|
||||
{
|
||||
Cullable* c = static_cast<Cullable*>(visibleEntry->m_userData);
|
||||
if (m_debugCtx->m_drawBoundingBoxes)
|
||||
if (m_jobData->m_debugCtx->m_drawBoundingBoxes)
|
||||
{
|
||||
auxGeomPtr->DrawObb(c->m_cullData.m_boundingObb, Matrix3x4::Identity(),
|
||||
nodeIsContainedInFrustum ? Colors::Lime : Colors::Yellow, AuxGeomDraw::DrawStyle::Line);
|
||||
}
|
||||
|
||||
if (m_debugCtx->m_drawBoundingSpheres)
|
||||
if (m_jobData->m_debugCtx->m_drawBoundingSpheres)
|
||||
{
|
||||
auxGeomPtr->DrawSphere(c->m_cullData.m_boundingSphere.GetCenter(), c->m_cullData.m_boundingSphere.GetRadius(),
|
||||
Color(0.5f, 0.5f, 0.5f, 0.3f), AuxGeomDraw::DrawStyle::Shaded);
|
||||
}
|
||||
|
||||
if (m_debugCtx->m_drawLodRadii)
|
||||
if (m_jobData->m_debugCtx->m_drawLodRadii)
|
||||
{
|
||||
auxGeomPtr->DrawSphere(c->m_cullData.m_boundingSphere.GetCenter(),
|
||||
c->m_lodData.m_lodSelectionRadius,
|
||||
@@ -411,9 +444,9 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
if (m_debugCtx->m_enableStats)
|
||||
if (m_jobData->m_debugCtx->m_enableStats)
|
||||
{
|
||||
CullingDebugContext::CullStats& cullStats = m_debugCtx->GetCullStatsForView(m_view);
|
||||
CullingDebugContext::CullStats& cullStats = m_jobData->m_debugCtx->GetCullStatsForView(m_jobData->m_view);
|
||||
|
||||
//no need for mutex here since these are all atomics
|
||||
cullStats.m_numVisibleDrawPackets += numDrawPackets;
|
||||
@@ -421,6 +454,63 @@ namespace AZ
|
||||
++cullStats.m_numJobs;
|
||||
}
|
||||
}
|
||||
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
MaskedOcclusionCulling::CullingResult TestOcclusionCulling(AzFramework::VisibilityEntry* visibleEntry)
|
||||
{
|
||||
if (!m_jobData->m_maskedOcclusionCulling)
|
||||
{
|
||||
return MaskedOcclusionCulling::CullingResult::VISIBLE;
|
||||
}
|
||||
|
||||
if (visibleEntry->m_boundingVolume.Contains(m_jobData->m_view->GetCameraTransform().GetTranslation()))
|
||||
{
|
||||
// camera is inside bounding volume
|
||||
return MaskedOcclusionCulling::CullingResult::VISIBLE;
|
||||
}
|
||||
|
||||
const Vector3& minBound = visibleEntry->m_boundingVolume.GetMin();
|
||||
const Vector3& maxBound = visibleEntry->m_boundingVolume.GetMax();
|
||||
|
||||
// compute bounding volume corners
|
||||
Vector4 corners[8];
|
||||
corners[0] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), minBound.GetY(), minBound.GetZ(), 1.0f);
|
||||
corners[1] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), minBound.GetY(), maxBound.GetZ(), 1.0f);
|
||||
corners[2] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), minBound.GetY(), maxBound.GetZ(), 1.0f);
|
||||
corners[3] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), minBound.GetY(), minBound.GetZ(), 1.0f);
|
||||
corners[4] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), maxBound.GetY(), minBound.GetZ(), 1.0f);
|
||||
corners[5] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), maxBound.GetY(), maxBound.GetZ(), 1.0f);
|
||||
corners[6] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), maxBound.GetY(), maxBound.GetZ(), 1.0f);
|
||||
corners[7] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), maxBound.GetY(), minBound.GetZ(), 1.0f);
|
||||
|
||||
// find min clip-space depth and NDC min/max
|
||||
float minDepth = FLT_MAX;
|
||||
float ndcMinX = FLT_MAX;
|
||||
float ndcMinY = FLT_MAX;
|
||||
float ndcMaxX = -FLT_MAX;
|
||||
float ndcMaxY = -FLT_MAX;
|
||||
for (uint32_t index = 0; index < 8; ++index)
|
||||
{
|
||||
minDepth = AZStd::min(minDepth, corners[index].GetW());
|
||||
|
||||
// convert to NDC
|
||||
corners[index] /= corners[index].GetW();
|
||||
|
||||
ndcMinX = AZStd::min(ndcMinX, corners[index].GetX());
|
||||
ndcMinY = AZStd::min(ndcMinY, corners[index].GetY());
|
||||
ndcMaxX = AZStd::max(ndcMaxX, corners[index].GetX());
|
||||
ndcMaxY = AZStd::max(ndcMaxY, corners[index].GetY());
|
||||
}
|
||||
|
||||
if (minDepth < 0.00000001f)
|
||||
{
|
||||
return MaskedOcclusionCulling::VISIBLE;
|
||||
}
|
||||
|
||||
// test against the occlusion buffer, which contains only the manually placed occlusion planes
|
||||
return m_jobData->m_maskedOcclusionCulling->TestRect(ndcMinX, ndcMinY, ndcMaxX, ndcMaxY, minDepth);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
void CullingScene::ProcessCullables(const Scene& scene, View& view, AZ::Job& parentJob)
|
||||
@@ -454,8 +544,67 @@ namespace AZ
|
||||
cullStats.m_cameraViewToWorld = view.GetViewToWorldMatrix();
|
||||
}
|
||||
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
// setup occlusion culling, if necessary
|
||||
MaskedOcclusionCulling* maskedOcclusionCulling = m_occlusionPlanes.empty() ? nullptr : view.GetMaskedOcclusionCulling();
|
||||
if (maskedOcclusionCulling)
|
||||
{
|
||||
// frustum cull occlusion planes
|
||||
using VisibleOcclusionPlane = AZStd::pair<OcclusionPlane, float>;
|
||||
AZStd::vector<VisibleOcclusionPlane> visibleOccluders;
|
||||
for (const auto& occlusionPlane : m_occlusionPlanes)
|
||||
{
|
||||
if (ShapeIntersection::Overlaps(frustum, occlusionPlane.m_aabb))
|
||||
{
|
||||
// occluder is visible, compute view space distance and add to list
|
||||
float depth = (view.GetWorldToViewMatrix() * occlusionPlane.m_aabb.GetMin()).GetZ();
|
||||
depth = AZStd::min(depth, (view.GetWorldToViewMatrix() * occlusionPlane.m_aabb.GetMax()).GetZ());
|
||||
|
||||
visibleOccluders.push_back(AZStd::make_pair(occlusionPlane, depth));
|
||||
}
|
||||
}
|
||||
|
||||
// sort the occlusion planes by view space distance, front-to-back
|
||||
AZStd::sort(visibleOccluders.begin(), visibleOccluders.end(), [](const VisibleOcclusionPlane& LHS, const VisibleOcclusionPlane& RHS)
|
||||
{
|
||||
return LHS.second > RHS.second;
|
||||
});
|
||||
|
||||
for (const VisibleOcclusionPlane& occlusionPlane: visibleOccluders)
|
||||
{
|
||||
// convert to clip-space
|
||||
Vector4 projectedBL = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerBL);
|
||||
Vector4 projectedTL = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerTL);
|
||||
Vector4 projectedTR = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerTR);
|
||||
Vector4 projectedBR = view.GetWorldToClipMatrix() * Vector4(occlusionPlane.first.m_cornerBR);
|
||||
|
||||
// store to float array
|
||||
float verts[16];
|
||||
projectedBL.StoreToFloat4(&verts[0]);
|
||||
projectedTL.StoreToFloat4(&verts[4]);
|
||||
projectedTR.StoreToFloat4(&verts[8]);
|
||||
projectedBR.StoreToFloat4(&verts[12]);
|
||||
|
||||
static uint32_t indices[6] = { 0, 1, 2, 2, 3, 0 };
|
||||
|
||||
// render into the occlusion buffer, specifying BACKFACE_NONE so it functions as a double-sided occluder
|
||||
maskedOcclusionCulling->RenderTriangles((float*)verts, indices, 2, nullptr, MaskedOcclusionCulling::BACKFACE_NONE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
WorkListType worklist;
|
||||
auto nodeVisitorLambda = [this, &scene, &view, &parentJob, &frustum, &worklist](const AzFramework::IVisibilityScene::NodeData& nodeData) -> void
|
||||
|
||||
AZStd::shared_ptr<AddObjectsToViewJob::JobData> jobData = AZStd::make_shared<AddObjectsToViewJob::JobData>();
|
||||
jobData->m_debugCtx = &m_debugCtx;
|
||||
jobData->m_scene = &scene;
|
||||
jobData->m_view = &view;
|
||||
jobData->m_frustum = frustum;
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
jobData->m_maskedOcclusionCulling = maskedOcclusionCulling;
|
||||
#endif
|
||||
|
||||
auto nodeVisitorLambda = [this, jobData, &parentJob, &frustum, &worklist](const AzFramework::IVisibilityScene::NodeData& nodeData) -> void
|
||||
{
|
||||
AZ_PROFILE_SCOPE(Debug::ProfileCategory::AzRender, "nodeVisitorLambda()");
|
||||
AZ_Assert(nodeData.m_entries.size() > 0, "should not get called with 0 entries");
|
||||
@@ -468,7 +617,7 @@ namespace AZ
|
||||
if (worklist.size() == worklist.capacity())
|
||||
{
|
||||
//Kick off a job to process the (full) worklist
|
||||
AddObjectsToViewJob* job = aznew AddObjectsToViewJob(m_debugCtx, scene, view, frustum, worklist); //pool allocated (cheap), auto-deletes when job finishes
|
||||
AddObjectsToViewJob* job = aznew AddObjectsToViewJob(jobData, worklist); //pool allocated (cheap), auto-deletes when job finishes
|
||||
worklist.clear();
|
||||
parentJob.SetContinuation(job);
|
||||
job->Start();
|
||||
@@ -486,8 +635,16 @@ namespace AZ
|
||||
|
||||
if (worklist.size() > 0)
|
||||
{
|
||||
AZStd::shared_ptr<AddObjectsToViewJob::JobData> remainingJobData = AZStd::make_shared<AddObjectsToViewJob::JobData>();
|
||||
remainingJobData->m_debugCtx = &m_debugCtx;
|
||||
remainingJobData->m_scene = &scene;
|
||||
remainingJobData->m_view = &view;
|
||||
remainingJobData->m_frustum = frustum;
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
remainingJobData->m_maskedOcclusionCulling = maskedOcclusionCulling;
|
||||
#endif
|
||||
//Kick off a job to process any remaining workitems
|
||||
AddObjectsToViewJob* job = aznew AddObjectsToViewJob(m_debugCtx, scene, view, frustum, worklist); //pool allocated (cheap), auto-deletes when job finishes
|
||||
AddObjectsToViewJob* job = aznew AddObjectsToViewJob(remainingJobData, worklist); //pool allocated (cheap), auto-deletes when job finishes
|
||||
parentJob.SetContinuation(job);
|
||||
job->Start();
|
||||
}
|
||||
@@ -576,6 +733,11 @@ namespace AZ
|
||||
m_debugCtx.ResetCullStats();
|
||||
m_debugCtx.m_numCullablesInScene = GetNumCullables();
|
||||
|
||||
for (auto& view : views)
|
||||
{
|
||||
view->BeginCulling();
|
||||
}
|
||||
|
||||
AuxGeomDrawPtr auxGeom;
|
||||
if (m_debugCtx.m_debugDraw)
|
||||
{
|
||||
|
||||
@@ -15,18 +15,28 @@
|
||||
#include <Atom/RPI.Public/RPISystemInterface.h>
|
||||
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
|
||||
#include <Atom/RPI.Public/Culling.h>
|
||||
|
||||
#include <Atom/RPI.Public/RenderPipeline.h>
|
||||
#include <Atom/RPI.Public/Pass/Specific/SwapChainPass.h>
|
||||
#include <Atom/RHI/DrawListTagRegistry.h>
|
||||
|
||||
#include <AzCore/Casting/lossy_cast.h>
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Math/MatrixUtils.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <Atom_RPI_Traits_Platform.h>
|
||||
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
#include <MaskedOcclusionCulling/MaskedOcclusionCulling.h>
|
||||
#endif
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace RPI
|
||||
{
|
||||
// fixed-size software occlusion culling buffer
|
||||
const uint32_t MaskedSoftwareOcclusionCullingWidth = 1920;
|
||||
const uint32_t MaskedSoftwareOcclusionCullingHeight = 1080;
|
||||
|
||||
ViewPtr View::CreateView(const AZ::Name& name, UsageFlags usage)
|
||||
{
|
||||
View* view = aznew View(name, usage);
|
||||
@@ -51,6 +61,21 @@ namespace AZ
|
||||
{
|
||||
m_shaderResourceGroup = ShaderResourceGroup::Create(viewSrgAsset);
|
||||
}
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
m_maskedOcclusionCulling = MaskedOcclusionCulling::Create();
|
||||
m_maskedOcclusionCulling->SetResolution(MaskedSoftwareOcclusionCullingWidth, MaskedSoftwareOcclusionCullingHeight);
|
||||
#endif
|
||||
}
|
||||
|
||||
View::~View()
|
||||
{
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
if (m_maskedOcclusionCulling)
|
||||
{
|
||||
MaskedOcclusionCulling::Destroy(m_maskedOcclusionCulling);
|
||||
m_maskedOcclusionCulling = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void View::SetDrawListMask(const RHI::DrawListMask& drawListMask)
|
||||
@@ -374,5 +399,17 @@ namespace AZ
|
||||
m_shaderResourceGroup->Compile();
|
||||
m_needBuildSrg = false;
|
||||
}
|
||||
|
||||
void View::BeginCulling()
|
||||
{
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
m_maskedOcclusionCulling->ClearBuffer();
|
||||
#endif
|
||||
}
|
||||
|
||||
MaskedOcclusionCulling* View::GetMaskedOcclusionCulling()
|
||||
{
|
||||
return m_maskedOcclusionCulling;
|
||||
}
|
||||
} // namespace RPI
|
||||
} // namespace AZ
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
#
|
||||
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
# its licensors.
|
||||
#
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this
|
||||
# distribution (the "License"). All use of this software is governed by the License,
|
||||
# or, if provided, by the license below or the license accompanying this file. Do not
|
||||
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
#
|
||||
|
||||
set(FILES
|
||||
External/MaskedOcclusionCulling/MaskedOcclusionCulling.h
|
||||
External/MaskedOcclusionCulling/MaskedOcclusionCullingCommon.inl
|
||||
External/MaskedOcclusionCulling/MaskedOcclusionCulling.cpp
|
||||
External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX2.cpp
|
||||
External/MaskedOcclusionCulling/MaskedOcclusionCullingAVX512.cpp
|
||||
)
|
||||
@@ -180,4 +180,4 @@ set(FILES
|
||||
Source/RPI.Public/GpuQuery/Query.cpp
|
||||
Source/RPI.Public/GpuQuery/QueryPool.cpp
|
||||
Source/RPI.Public/GpuQuery/TimestampQueryPool.cpp
|
||||
)
|
||||
)
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <Material/MaterialComponent.h>
|
||||
#include <Mesh/MeshComponent.h>
|
||||
#include <ReflectionProbe/ReflectionProbeComponent.h>
|
||||
#include <OcclusionCullingPlane/OcclusionCullingPlaneComponent.h>
|
||||
#include <PostProcess/PostFxLayerComponent.h>
|
||||
#include <PostProcess/Bloom/BloomComponent.h>
|
||||
#include <PostProcess/DepthOfField/DepthOfFieldComponent.h>
|
||||
@@ -57,6 +58,7 @@
|
||||
#include <Mesh/EditorMeshComponent.h>
|
||||
#include <Mesh/EditorMeshSystemComponent.h>
|
||||
#include <ReflectionProbe/EditorReflectionProbeComponent.h>
|
||||
#include <OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.h>
|
||||
#include <PostProcess/EditorPostFxLayerComponent.h>
|
||||
#include <PostProcess/Bloom/EditorBloomComponent.h>
|
||||
#include <PostProcess/DepthOfField/EditorDepthOfFieldComponent.h>
|
||||
@@ -117,6 +119,7 @@ namespace AZ
|
||||
DeferredFogComponent::CreateDescriptor(),
|
||||
SurfaceData::SurfaceDataMeshComponent::CreateDescriptor(),
|
||||
AttachmentComponent::CreateDescriptor(),
|
||||
OcclusionCullingPlaneComponent::CreateDescriptor(),
|
||||
|
||||
#ifdef ATOMLYINTEGRATION_FEATURE_COMMON_EDITOR
|
||||
EditorAreaLightComponent::CreateDescriptor(),
|
||||
@@ -149,6 +152,7 @@ namespace AZ
|
||||
EditorDeferredFogComponent::CreateDescriptor(),
|
||||
SurfaceData::EditorSurfaceDataMeshComponent::CreateDescriptor(),
|
||||
EditorAttachmentComponent::CreateDescriptor(),
|
||||
EditorOcclusionCullingPlaneComponent::CreateDescriptor(),
|
||||
#endif
|
||||
});
|
||||
}
|
||||
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
#include <AzToolsFramework/Entity/EditorEntityInfoBus.h>
|
||||
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
void EditorOcclusionCullingPlaneComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
BaseClass::Reflect(context);
|
||||
|
||||
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<EditorOcclusionCullingPlaneComponent, BaseClass>()
|
||||
->Version(1, ConvertToEditorRenderComponentAdapter<1>)
|
||||
;
|
||||
|
||||
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
|
||||
{
|
||||
editContext->Class<EditorOcclusionCullingPlaneComponent>(
|
||||
"Occlusion Culling Plane", "The OcclusionCullingPlane component is used to cull meshes that are inside the view frustum and behind the occlusion plane")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::Category, "Atom")
|
||||
->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Component_Placeholder.svg")
|
||||
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Component_Placeholder.png")
|
||||
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
;
|
||||
|
||||
editContext->Class<OcclusionCullingPlaneComponentController>(
|
||||
"OcclusionCullingPlaneComponentController", "")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->DataElement(AZ::Edit::UIHandlers::Default, &OcclusionCullingPlaneComponentController::m_configuration, "Configuration", "")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
|
||||
;
|
||||
|
||||
editContext->Class<OcclusionCullingPlaneComponentConfig>(
|
||||
"OcclusionCullingPlaneComponentConfig", "")
|
||||
->ClassElement(AZ::Edit::ClassElements::Group, "Settings")
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
->DataElement(AZ::Edit::UIHandlers::CheckBox, &OcclusionCullingPlaneComponentConfig::m_showVisualization, "Show Visualization", "Show the occlusion culling plane visualization")
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly)
|
||||
->DataElement(AZ::Edit::UIHandlers::CheckBox, &OcclusionCullingPlaneComponentConfig::m_transparentVisualization, "Transparent Visualization", "Sets the occlusion culling plane visualization as transparent")
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, Edit::PropertyRefreshLevels::ValuesOnly)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto behaviorContext = azrtti_cast<BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->ConstantProperty("EditorOcclusionCullingPlaneComponentTypeId", BehaviorConstant(Uuid(EditorOcclusionCullingPlaneComponentTypeId)))
|
||||
->Attribute(AZ::Script::Attributes::Module, "render")
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation);
|
||||
}
|
||||
}
|
||||
|
||||
EditorOcclusionCullingPlaneComponent::EditorOcclusionCullingPlaneComponent()
|
||||
{
|
||||
}
|
||||
|
||||
EditorOcclusionCullingPlaneComponent::EditorOcclusionCullingPlaneComponent(const OcclusionCullingPlaneComponentConfig& config)
|
||||
: BaseClass(config)
|
||||
{
|
||||
}
|
||||
|
||||
void EditorOcclusionCullingPlaneComponent::Activate()
|
||||
{
|
||||
BaseClass::Activate();
|
||||
AzFramework::EntityDebugDisplayEventBus::Handler::BusConnect(GetEntityId());
|
||||
}
|
||||
|
||||
void EditorOcclusionCullingPlaneComponent::Deactivate()
|
||||
{
|
||||
AzFramework::EntityDebugDisplayEventBus::Handler::BusDisconnect();
|
||||
BaseClass::Deactivate();
|
||||
}
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
|
||||
#include <AzToolsFramework/API/ComponentEntitySelectionBus.h>
|
||||
#include <OcclusionCullingPlane/OcclusionCullingPlaneComponent.h>
|
||||
#include <OcclusionCullingPlane/OcclusionCullingPlaneComponentConstants.h>
|
||||
#include <Atom/Feature/Utils/EditorRenderComponentAdapter.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
class EditorOcclusionCullingPlaneComponent final
|
||||
: public EditorRenderComponentAdapter<OcclusionCullingPlaneComponentController, OcclusionCullingPlaneComponent, OcclusionCullingPlaneComponentConfig>
|
||||
, private AzFramework::EntityDebugDisplayEventBus::Handler
|
||||
{
|
||||
public:
|
||||
using BaseClass = EditorRenderComponentAdapter<OcclusionCullingPlaneComponentController, OcclusionCullingPlaneComponent, OcclusionCullingPlaneComponentConfig>;
|
||||
AZ_EDITOR_COMPONENT(AZ::Render::EditorOcclusionCullingPlaneComponent, EditorOcclusionCullingPlaneComponentTypeId, BaseClass);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
EditorOcclusionCullingPlaneComponent();
|
||||
EditorOcclusionCullingPlaneComponent(const OcclusionCullingPlaneComponentConfig& config);
|
||||
|
||||
// AZ::Component overrides
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
};
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <OcclusionCullingPlane/OcclusionCullingPlaneComponent.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
OcclusionCullingPlaneComponent::OcclusionCullingPlaneComponent(const OcclusionCullingPlaneComponentConfig& config)
|
||||
: BaseClass(config)
|
||||
{
|
||||
}
|
||||
|
||||
void OcclusionCullingPlaneComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
BaseClass::Reflect(context);
|
||||
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<OcclusionCullingPlaneComponent, BaseClass>()
|
||||
->Version(0)
|
||||
;
|
||||
}
|
||||
|
||||
if (auto behaviorContext = azrtti_cast<BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->ConstantProperty("OcclusionCullingPlaneComponentTypeId", BehaviorConstant(Uuid(OcclusionCullingPlaneComponentTypeId)))
|
||||
->Attribute(AZ::Script::Attributes::Module, "render")
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common);
|
||||
}
|
||||
}
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <OcclusionCullingPlane/OcclusionCullingPlaneComponentController.h>
|
||||
#include <OcclusionCullingPlane/OcclusionCullingPlaneComponentConstants.h>
|
||||
#include <AzFramework/Components/ComponentAdapter.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
class OcclusionCullingPlaneComponent final
|
||||
: public AzFramework::Components::ComponentAdapter<OcclusionCullingPlaneComponentController, OcclusionCullingPlaneComponentConfig>
|
||||
{
|
||||
public:
|
||||
using BaseClass = AzFramework::Components::ComponentAdapter<OcclusionCullingPlaneComponentController, OcclusionCullingPlaneComponentConfig>;
|
||||
AZ_COMPONENT(AZ::Render::OcclusionCullingPlaneComponent, OcclusionCullingPlaneComponentTypeId, BaseClass);
|
||||
|
||||
OcclusionCullingPlaneComponent() = default;
|
||||
OcclusionCullingPlaneComponent(const OcclusionCullingPlaneComponentConfig& config);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
};
|
||||
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
static constexpr const char* const OcclusionCullingPlaneComponentTypeId = "{F7537387-15A8-48F0-A1F3-D19C5886B886}";
|
||||
static constexpr const char* const EditorOcclusionCullingPlaneComponentTypeId = "{BE7CC17B-32EB-49B0-BAD9-D26E3A059012}";
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <OcclusionCullingPlane/OcclusionCullingPlaneComponentController.h>
|
||||
#include <OcclusionCullingPlane/OcclusionCullingPlaneComponentConstants.h>
|
||||
|
||||
#include <Atom/RPI.Public/Model/Model.h>
|
||||
#include <Atom/RPI.Public/Image/StreamingImage.h>
|
||||
#include <Atom/RPI.Public/Scene.h>
|
||||
|
||||
#include <AzCore/Asset/AssetManager.h>
|
||||
#include <AzCore/Asset/AssetManagerBus.h>
|
||||
#include <AzCore/Debug/EventTrace.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
#include <AzFramework/Entity/EntityContextBus.h>
|
||||
#include <AzFramework/Entity/EntityContext.h>
|
||||
#include <AzFramework/Scene/Scene.h>
|
||||
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
void OcclusionCullingPlaneComponentConfig::Reflect(ReflectContext* context)
|
||||
{
|
||||
if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<OcclusionCullingPlaneComponentConfig>()
|
||||
->Version(0)
|
||||
->Field("ShowVisualization", &OcclusionCullingPlaneComponentConfig::m_showVisualization)
|
||||
->Field("TransparentVisualization", &OcclusionCullingPlaneComponentConfig::m_transparentVisualization)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
void OcclusionCullingPlaneComponentController::Reflect(ReflectContext* context)
|
||||
{
|
||||
OcclusionCullingPlaneComponentConfig::Reflect(context);
|
||||
|
||||
if (auto* serializeContext = azrtti_cast<SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<OcclusionCullingPlaneComponentController>()
|
||||
->Version(0)
|
||||
->Field("Configuration", &OcclusionCullingPlaneComponentController::m_configuration);
|
||||
}
|
||||
}
|
||||
|
||||
void OcclusionCullingPlaneComponentController::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
|
||||
{
|
||||
dependent.push_back(AZ_CRC("TransformService", 0x8ee22c50));
|
||||
}
|
||||
|
||||
void OcclusionCullingPlaneComponentController::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC("OcclusionCullingPlaneService", 0x9123f33d));
|
||||
}
|
||||
|
||||
void OcclusionCullingPlaneComponentController::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
{
|
||||
incompatible.push_back(AZ_CRC("OcclusionCullingPlaneService", 0x9123f33d));
|
||||
}
|
||||
|
||||
void OcclusionCullingPlaneComponentController::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
|
||||
{
|
||||
required.push_back(AZ_CRC("TransformService"));
|
||||
}
|
||||
|
||||
OcclusionCullingPlaneComponentController::OcclusionCullingPlaneComponentController(const OcclusionCullingPlaneComponentConfig& config)
|
||||
: m_configuration(config)
|
||||
{
|
||||
}
|
||||
|
||||
void OcclusionCullingPlaneComponentController::Activate(AZ::EntityId entityId)
|
||||
{
|
||||
m_entityId = entityId;
|
||||
|
||||
TransformNotificationBus::Handler::BusConnect(m_entityId);
|
||||
|
||||
m_featureProcessor = RPI::Scene::GetFeatureProcessorForEntity<OcclusionCullingPlaneFeatureProcessorInterface>(entityId);
|
||||
AZ_Assert(m_featureProcessor, "OcclusionCullingPlaneComponentController was unable to find a OcclusionCullingPlaneFeatureProcessor on the EntityContext provided.");
|
||||
|
||||
m_transformInterface = TransformBus::FindFirstHandler(entityId);
|
||||
AZ_Assert(m_transformInterface, "Unable to attach to a TransformBus handler");
|
||||
if (!m_transformInterface)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// add this occlusion plane to the feature processor
|
||||
const AZ::Transform& transform = m_transformInterface->GetWorldTM();
|
||||
m_handle = m_featureProcessor->AddOcclusionCullingPlane(transform);
|
||||
|
||||
// set visualization
|
||||
m_featureProcessor->ShowVisualization(m_handle, m_configuration.m_showVisualization);
|
||||
m_featureProcessor->SetTransparentVisualization(m_handle, m_configuration.m_transparentVisualization);
|
||||
}
|
||||
|
||||
void OcclusionCullingPlaneComponentController::Deactivate()
|
||||
{
|
||||
if (m_featureProcessor)
|
||||
{
|
||||
m_featureProcessor->RemoveOcclusionCullingPlane(m_handle);
|
||||
}
|
||||
|
||||
Data::AssetBus::MultiHandler::BusDisconnect();
|
||||
TransformNotificationBus::Handler::BusDisconnect();
|
||||
|
||||
m_transformInterface = nullptr;
|
||||
m_featureProcessor = nullptr;
|
||||
}
|
||||
|
||||
void OcclusionCullingPlaneComponentController::SetConfiguration(const OcclusionCullingPlaneComponentConfig& config)
|
||||
{
|
||||
m_configuration = config;
|
||||
}
|
||||
|
||||
const OcclusionCullingPlaneComponentConfig& OcclusionCullingPlaneComponentController::GetConfiguration() const
|
||||
{
|
||||
return m_configuration;
|
||||
}
|
||||
|
||||
void OcclusionCullingPlaneComponentController::OnTransformChanged([[maybe_unused]] const AZ::Transform& local, const AZ::Transform& world)
|
||||
{
|
||||
if (!m_featureProcessor)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_featureProcessor->SetTransform(m_handle, world);
|
||||
}
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
* its licensors.
|
||||
*
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||
* distribution (the "License"). All use of this software is governed by the License,
|
||||
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <Atom/Feature/OcclusionCullingPlane/OcclusionCullingPlaneFeatureProcessorInterface.h>
|
||||
#include <Atom/RPI.Public/Model/Model.h>
|
||||
#include <LmbrCentral/Shape/BoxShapeComponentBus.h>
|
||||
#include <OcclusionCullingPlane/OcclusionCullingPlaneComponentConstants.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
class OcclusionCullingPlaneComponentConfig final
|
||||
: public AZ::ComponentConfig
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(AZ::Render::OcclusionCullingPlaneComponentConfig, "{D0E107CA-5AFB-4675-BC97-94BCA5F248DB}", ComponentConfig);
|
||||
AZ_CLASS_ALLOCATOR(OcclusionCullingPlaneComponentConfig, SystemAllocator, 0);
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
bool m_showVisualization = true;
|
||||
bool m_transparentVisualization = false;
|
||||
|
||||
OcclusionCullingPlaneComponentConfig() = default;
|
||||
};
|
||||
|
||||
class OcclusionCullingPlaneComponentController final
|
||||
: public Data::AssetBus::MultiHandler
|
||||
, private TransformNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
friend class EditorOcclusionCullingPlaneComponent;
|
||||
|
||||
AZ_CLASS_ALLOCATOR(OcclusionCullingPlaneComponentController, AZ::SystemAllocator, 0);
|
||||
AZ_RTTI(AZ::Render::OcclusionCullingPlaneComponentController, "{8EDA3C7D-5171-4843-9969-4D84DB13F221}");
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
|
||||
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
|
||||
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
|
||||
|
||||
OcclusionCullingPlaneComponentController() = default;
|
||||
OcclusionCullingPlaneComponentController(const OcclusionCullingPlaneComponentConfig& config);
|
||||
|
||||
void Activate(AZ::EntityId entityId);
|
||||
void Deactivate();
|
||||
void SetConfiguration(const OcclusionCullingPlaneComponentConfig& config);
|
||||
const OcclusionCullingPlaneComponentConfig& GetConfiguration() const;
|
||||
|
||||
private:
|
||||
|
||||
AZ_DISABLE_COPY(OcclusionCullingPlaneComponentController);
|
||||
|
||||
// TransformNotificationBus overrides
|
||||
void OnTransformChanged(const AZ::Transform& local, const AZ::Transform& world) override;
|
||||
|
||||
// handle for this occlusion plane in the feature processor
|
||||
OcclusionCullingPlaneHandle m_handle;
|
||||
|
||||
OcclusionCullingPlaneFeatureProcessorInterface* m_featureProcessor = nullptr;
|
||||
TransformInterface* m_transformInterface = nullptr;
|
||||
AZ::EntityId m_entityId;
|
||||
OcclusionCullingPlaneComponentConfig m_configuration;
|
||||
};
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
+2
@@ -56,6 +56,8 @@ set(FILES
|
||||
Source/Mesh/EditorMeshSystemComponent.h
|
||||
Source/Mesh/MeshThumbnail.h
|
||||
Source/Mesh/MeshThumbnail.cpp
|
||||
Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.h
|
||||
Source/OcclusionCullingPlane/EditorOcclusionCullingPlaneComponent.cpp
|
||||
Source/PostProcess/EditorPostFxLayerComponent.cpp
|
||||
Source/PostProcess/EditorPostFxLayerComponent.h
|
||||
Source/PostProcess/Bloom/EditorBloomComponent.cpp
|
||||
|
||||
+4
@@ -72,6 +72,10 @@ set(FILES
|
||||
Source/Mesh/MeshComponent.cpp
|
||||
Source/Mesh/MeshComponentController.h
|
||||
Source/Mesh/MeshComponentController.cpp
|
||||
Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.h
|
||||
Source/OcclusionCullingPlane/OcclusionCullingPlaneComponent.cpp
|
||||
Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.h
|
||||
Source/OcclusionCullingPlane/OcclusionCullingPlaneComponentController.cpp
|
||||
Source/PostProcess/PostFxLayerComponent.cpp
|
||||
Source/PostProcess/PostFxLayerComponent.h
|
||||
Source/PostProcess/PostFxLayerComponentConfig.cpp
|
||||
|
||||
Reference in New Issue
Block a user