diff --git a/Hydrogent/CMakeLists.txt b/Hydrogent/CMakeLists.txt index 88614290..cb79007a 100644 --- a/Hydrogent/CMakeLists.txt +++ b/Hydrogent/CMakeLists.txt @@ -18,6 +18,7 @@ set(SOURCE src/Tasks/HnTask.cpp src/Tasks/HnRenderTask.cpp src/Tasks/HnPostProcessTask.cpp + src/Tasks/HnTaskController.cpp ) set(INCLUDE @@ -37,6 +38,7 @@ set(INCLUDE include/Tasks/HnTask.hpp include/Tasks/HnRenderTask.hpp include/Tasks/HnPostProcessTask.hpp + include/Tasks/HnTaskController.hpp ) set(INTERFACE @@ -116,6 +118,7 @@ INTERFACE usd_hio usd_sdr usd_ndr + usd_trace ) target_include_directories(Diligent-Hydrogent diff --git a/Hydrogent/include/HnRendererImpl.hpp b/Hydrogent/include/HnRendererImpl.hpp index 8890ee8d..8e910942 100644 --- a/Hydrogent/include/HnRendererImpl.hpp +++ b/Hydrogent/include/HnRendererImpl.hpp @@ -39,6 +39,7 @@ #include "ObjectBase.hpp" #include "GPUCompletionAwaitQueue.hpp" #include "USD_Renderer.hpp" +#include "Tasks/HnTaskController.hpp" #include "pxr/usd/usd/stage.h" #include "pxr/imaging/hd/tokens.h" @@ -58,6 +59,7 @@ namespace USD class HnRenderDelegate; class HnMesh; class HnMaterial; +class HnTaskController; class HnRendererImpl final : public ObjectBase { @@ -112,6 +114,7 @@ class HnRendererImpl final : public ObjectBase USD_Renderer::WireframePsoCacheAccessor m_WireframePSOCache; std::unique_ptr m_RenderDelegate; + std::unique_ptr m_TaskController; pxr::UsdStageRefPtr m_Stage; pxr::HdEngine m_Engine; diff --git a/Hydrogent/include/Tasks/HnPostProcessTask.hpp b/Hydrogent/include/Tasks/HnPostProcessTask.hpp index 80845cfb..95b47432 100644 --- a/Hydrogent/include/Tasks/HnPostProcessTask.hpp +++ b/Hydrogent/include/Tasks/HnPostProcessTask.hpp @@ -34,15 +34,27 @@ namespace Diligent namespace USD { +struct HnPostProcessTaskParams +{ + constexpr bool operator==(const HnPostProcessTaskParams& rhs) const + { + return true; + } + constexpr bool operator!=(const HnPostProcessTaskParams& rhs) const + { + return !(*this == rhs); + } +}; + /// Post processing task implementation in Hydrogent. class HnPostProcessTask final : public HnTask { public: using TaskSharedPtr = std::shared_ptr; - static TaskSharedPtr Create(const pxr::SdfPath& id); + static TaskSharedPtr Create(pxr::HdSceneDelegate& ParamsDelegate, const pxr::SdfPath& id); - HnPostProcessTask(const pxr::SdfPath& Id); + HnPostProcessTask(pxr::HdSceneDelegate* ParamsDelegate, const pxr::SdfPath& Id); ~HnPostProcessTask(); virtual void Sync(pxr::HdSceneDelegate* Delegate, diff --git a/Hydrogent/include/Tasks/HnRenderTask.hpp b/Hydrogent/include/Tasks/HnRenderTask.hpp index 20117e77..decebf13 100644 --- a/Hydrogent/include/Tasks/HnRenderTask.hpp +++ b/Hydrogent/include/Tasks/HnRenderTask.hpp @@ -34,15 +34,27 @@ namespace Diligent namespace USD { +struct HnRenderTaskParams +{ + constexpr bool operator==(const HnRenderTaskParams& rhs) const + { + return true; + } + constexpr bool operator!=(const HnRenderTaskParams& rhs) const + { + return !(*this == rhs); + } +}; + /// Render task implementation in Hydrogent. class HnRenderTask final : public HnTask { public: using TaskSharedPtr = std::shared_ptr; - static TaskSharedPtr Create(const pxr::SdfPath& id); + static TaskSharedPtr Create(pxr::HdSceneDelegate& ParamsDelegate, const pxr::SdfPath& Id); - HnRenderTask(const pxr::SdfPath& Id); + HnRenderTask(pxr::HdSceneDelegate* ParamsDelegate, const pxr::SdfPath& Id); ~HnRenderTask(); virtual void Sync(pxr::HdSceneDelegate* Delegate, diff --git a/Hydrogent/include/Tasks/HnTaskController.hpp b/Hydrogent/include/Tasks/HnTaskController.hpp new file mode 100644 index 00000000..346e3010 --- /dev/null +++ b/Hydrogent/include/Tasks/HnTaskController.hpp @@ -0,0 +1,96 @@ +/* + * Copyright 2023 Diligent Graphics LLC + * + * 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. + * + * 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 for any 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 software (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. + */ + +#pragma once + +#include +#include + +#include "Tasks/HnTask.hpp" + +namespace Diligent +{ + +namespace USD +{ + +struct HnRenderTaskParams; +struct HnPostProcessTaskParams; + +/// Task controller implementation in Hydrogent. +class HnTaskController +{ +public: + HnTaskController(pxr::HdRenderIndex& RenderIndex, + const pxr::SdfPath& ControllerId); + + ~HnTaskController(); + + const pxr::HdRenderIndex& GetRenderIndex() const { return m_RenderIndex; } + pxr::HdRenderIndex& GetRenderIndex() { return m_RenderIndex; } + + const pxr::SdfPath& GetControllerId() const { return m_ControllerId; } + + const pxr::HdTaskSharedPtrVector GetRenderingTasks() const; + + /// Sets new collection for the render tasks. + void SetCollection(const pxr::HdRprimCollection& Collection); + + /// Sets new params for the render tasks. + void SetRenderParams(const HnRenderTaskParams& Params); + + /// Sets new params for the post-process task. + void SetPostProcessParams(const HnPostProcessTaskParams& Params); + + /// Sets new render tags for the render tasks. + void SetRenderTags(const pxr::TfTokenVector& RenderTags); + +private: + pxr::SdfPath GetRenderTaskId(const pxr::TfToken& MaterialTag) const; + pxr::SdfPath CreateRenderTask(const pxr::TfToken& MaterialTag); + pxr::SdfPath CreatePostProcessTask(); + +private: + pxr::HdRenderIndex& m_RenderIndex; + const pxr::SdfPath m_ControllerId; + + // Custom delegate to pass parameters to the render tasks. + class TaskParamsDelegate; + std::unique_ptr m_ParamsDelegate; + + pxr::SdfPathVector m_RenderTaskIds; + + enum TASK_ID + { + TASK_ID_POST_PROCESS, + TASK_ID_COUNT + }; + std::array m_TaskIds; +}; + +} // namespace USD + +} // namespace Diligent diff --git a/Hydrogent/src/HnRendererImpl.cpp b/Hydrogent/src/HnRendererImpl.cpp index 061b1470..a7e3b839 100644 --- a/Hydrogent/src/HnRendererImpl.cpp +++ b/Hydrogent/src/HnRendererImpl.cpp @@ -29,6 +29,7 @@ #include "HnMesh.hpp" #include "HnMaterial.hpp" #include "HnTokens.hpp" +#include "Tasks/HnTaskController.hpp" #include "EngineMemory.h" #include "USD_Renderer.hpp" @@ -148,11 +149,16 @@ void HnRendererImpl::LoadUSDStage(pxr::UsdStageRefPtr& Stage) m_Stage = Stage; m_RenderDelegate = HnRenderDelegate::Create({m_Device, m_Context, m_CameraAttribsCB, m_LightAttribsCB, m_USDRenderer}); + m_RenderIndex = pxr::HdRenderIndex::New(m_RenderDelegate.get(), pxr::HdDriverVector{}); - m_RenderIndex = pxr::HdRenderIndex::New(m_RenderDelegate.get(), pxr::HdDriverVector{}); - m_ImagingDelegate = new pxr::UsdImagingDelegate(m_RenderIndex, pxr::SdfPath::AbsoluteRootPath()); + const pxr::SdfPath SceneDelegateId = pxr::SdfPath::AbsoluteRootPath(); + + m_ImagingDelegate = new pxr::UsdImagingDelegate(m_RenderIndex, SceneDelegateId); m_ImagingDelegate->Populate(m_Stage->GetPseudoRoot()); + const pxr::SdfPath TaskControllerId = SceneDelegateId.AppendChild(pxr::TfToken{"_HnTaskController_"}); + m_TaskController = std::make_unique(*m_RenderIndex, TaskControllerId); + m_RenderTags = {pxr::HdRenderTagTokens->geometry}; auto Collection = pxr::HdRprimCollection{pxr::HdTokens->geometry, pxr::HdReprSelector(pxr::HdReprTokens->hull)}; @@ -204,8 +210,8 @@ void HnRendererImpl::Update() if (m_ImagingDelegate) { m_ImagingDelegate->ApplyPendingUpdates(); - pxr::HdTaskSharedPtrVector tasks = { - std::make_shared(m_GeometryPass, m_RenderTags)}; + pxr::HdTaskSharedPtrVector tasks = m_TaskController->GetRenderingTasks(); + tasks.push_back(std::make_shared(m_GeometryPass, m_RenderTags)); m_Engine.Execute(&m_ImagingDelegate->GetRenderIndex(), &tasks); } } diff --git a/Hydrogent/src/Tasks/HnPostProcessTask.cpp b/Hydrogent/src/Tasks/HnPostProcessTask.cpp index 52b2adc6..233d5f06 100644 --- a/Hydrogent/src/Tasks/HnPostProcessTask.cpp +++ b/Hydrogent/src/Tasks/HnPostProcessTask.cpp @@ -32,12 +32,12 @@ namespace Diligent namespace USD { -HnPostProcessTask::TaskSharedPtr HnPostProcessTask::Create(const pxr::SdfPath& Id) +HnPostProcessTask::TaskSharedPtr HnPostProcessTask::Create(pxr::HdSceneDelegate& ParamsDelegate, const pxr::SdfPath& Id) { - return TaskSharedPtr(new HnPostProcessTask{Id}); + return TaskSharedPtr(new HnPostProcessTask{&ParamsDelegate, Id}); } -HnPostProcessTask::HnPostProcessTask(const pxr::SdfPath& Id) : +HnPostProcessTask::HnPostProcessTask(pxr::HdSceneDelegate* ParamsDelegate, const pxr::SdfPath& Id) : HnTask{Id} { } diff --git a/Hydrogent/src/Tasks/HnRenderTask.cpp b/Hydrogent/src/Tasks/HnRenderTask.cpp index 435dee9b..65454211 100644 --- a/Hydrogent/src/Tasks/HnRenderTask.cpp +++ b/Hydrogent/src/Tasks/HnRenderTask.cpp @@ -32,12 +32,12 @@ namespace Diligent namespace USD { -HnRenderTask::TaskSharedPtr HnRenderTask::Create(const pxr::SdfPath& Id) +HnRenderTask::TaskSharedPtr HnRenderTask::Create(pxr::HdSceneDelegate& ParamsDelegate, const pxr::SdfPath& Id) { - return TaskSharedPtr(new HnRenderTask{Id}); + return TaskSharedPtr(new HnRenderTask{&ParamsDelegate, Id}); } -HnRenderTask::HnRenderTask(const pxr::SdfPath& Id) : +HnRenderTask::HnRenderTask(pxr::HdSceneDelegate* ParamsDelegate, const pxr::SdfPath& Id) : HnTask{Id} { } diff --git a/Hydrogent/src/Tasks/HnTaskController.cpp b/Hydrogent/src/Tasks/HnTaskController.cpp new file mode 100644 index 00000000..4b8f1d4b --- /dev/null +++ b/Hydrogent/src/Tasks/HnTaskController.cpp @@ -0,0 +1,321 @@ +/* + * Copyright 2023 Diligent Graphics LLC + * + * 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. + * + * 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 for any 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 software (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. + */ + +#include "Tasks/HnTaskController.hpp" + +#include + +#include "Tasks/HnRenderTask.hpp" +#include "Tasks/HnPostProcessTask.hpp" +#include "HnTokens.hpp" +#include "HashUtils.hpp" + +namespace Diligent +{ + +namespace USD +{ + +namespace +{ + +// clang-format off +TF_DEFINE_PRIVATE_TOKENS( + HnTaskControllerTokens, + + (postProcessTask) + + (renderBufferDescriptor) + (renderTags) +); +// clang-format on + +} // namespace + +class HnTaskController::TaskParamsDelegate final : public pxr::HdSceneDelegate +{ +public: + TaskParamsDelegate(pxr::HdRenderIndex& Index, + const pxr::SdfPath& Id) : + pxr::HdSceneDelegate{&Index, Id} + {} + + ~TaskParamsDelegate() override final + { + } + + template + void SetParameter(const pxr::SdfPath& Id, const TfToken& ValueKey, const T& Value) + { + m_ParamsCache[{Id, ValueKey}] = Value; + } + + template + T GetParameter(const pxr::SdfPath& Id, const TfToken& ValueKey) const + { + auto it = m_ParamsCache.find({Id, ValueKey}); + if (it == m_ParamsCache.end()) + { + UNEXPECTED("Parameter ", ValueKey, " is not set for ", Id); + return {}; + } + + VERIFY(it->second.IsHolding(), "Unexpected parameter type"); + return it->second.Get(); + } + + bool HasParameter(const pxr::SdfPath& Id, const TfToken& ValueKey) const + { + return m_ParamsCache.find({Id, ValueKey}) != m_ParamsCache.end(); + } + + pxr::VtValue Get(const pxr::SdfPath& Id, const TfToken& ValueKey) override final + { + auto it = m_ParamsCache.find({Id, ValueKey}); + return it != m_ParamsCache.end() ? it->second : pxr::VtValue{}; + } + + pxr::GfMatrix4d GetTransform(const pxr::SdfPath& Id) override final + { + auto it = m_ParamsCache.find({Id, pxr::HdTokens->transform}); + return it != m_ParamsCache.end() ? it->second.Get() : pxr::GfMatrix4d{1.0}; + } + + pxr::VtValue GetLightParamValue(const pxr::SdfPath& Id, + const pxr::TfToken& ParamName) override + { + return Get(Id, ParamName); + } + + bool IsEnabled(const TfToken& Option) const override final + { + return HdSceneDelegate::IsEnabled(Option); + } + + pxr::HdRenderBufferDescriptor GetRenderBufferDescriptor(const pxr::SdfPath& Id) override final + { + return GetParameter(Id, HnTaskControllerTokens->renderBufferDescriptor); + } + + pxr::TfTokenVector GetTaskRenderTags(const pxr::SdfPath& TaskId) override final + { + if (HasParameter(TaskId, HnTaskControllerTokens->renderTags)) + { + return GetParameter(TaskId, HnTaskControllerTokens->renderTags); + } + return pxr::TfTokenVector{}; + } + +private: + struct ParamKey + { + const pxr::SdfPath Path; + const pxr::TfToken ValueKey; + const size_t Hash; + + ParamKey(const pxr::SdfPath& _Path, const pxr::TfToken& _ValueKey) : + Path{_Path}, + ValueKey{_ValueKey}, + Hash{ComputeHash(pxr::SdfPath::Hash{}(_Path), pxr::TfToken::HashFunctor{}(_ValueKey))} + {} + + bool operator==(const ParamKey& rhs) const + { + return Hash && rhs.Hash && Path == rhs.Path && ValueKey == rhs.ValueKey; + } + + struct Hasher + { + size_t operator()(const ParamKey& Key) const + { + return Key.Hash; + } + }; + }; + + std::unordered_map m_ParamsCache; +}; + + +HnTaskController::HnTaskController(pxr::HdRenderIndex& RenderIndex, + const pxr::SdfPath& ControllerId) : + m_RenderIndex{RenderIndex}, + m_ControllerId{ControllerId}, + m_ParamsDelegate{std::make_unique(RenderIndex, ControllerId)} +{ + for (const auto& MaterialTag : {HnMaterialTagTokens->defaultTag, + HnMaterialTagTokens->masked, + HnMaterialTagTokens->additive, + HnMaterialTagTokens->translucent}) + { + m_RenderTaskIds.push_back(CreateRenderTask(MaterialTag)); + } + + m_TaskIds[TASK_ID_POST_PROCESS] = CreatePostProcessTask(); +} + +HnTaskController::~HnTaskController() +{ + // Remove all tasks from the render index + for (const auto& Id : m_RenderTaskIds) + { + m_RenderIndex.RemoveTask(Id); + } + + for (const auto& Id : m_TaskIds) + { + if (!Id.IsEmpty()) + { + m_RenderIndex.RemoveTask(Id); + } + } +} + +pxr::SdfPath HnTaskController::GetRenderTaskId(const pxr::TfToken& MaterialTag) const +{ + std::string Id = std::string{"renderTask_"} + MaterialTag.GetString(); + std::replace(Id.begin(), Id.end(), ':', '_'); + return GetControllerId().AppendChild(TfToken{Id}); +} + +pxr::SdfPath HnTaskController::CreateRenderTask(const pxr::TfToken& MaterialTag) +{ + const pxr::SdfPath RenderTaskId = GetRenderTaskId(MaterialTag); + // Note that we pass the delegate to the scene index. This delegate will be passed + // to the task's Sync() method. + m_RenderIndex.InsertTask(m_ParamsDelegate.get(), RenderTaskId); + + HnRenderTaskParams TaskParams; + + pxr::HdRprimCollection Collection{ + pxr::HdTokens->geometry, + pxr::HdReprSelector{pxr::HdReprTokens->smoothHull}, + false, // forcedRepr + MaterialTag, + }; + Collection.SetRootPath(pxr::SdfPath::AbsoluteRootPath()); + + pxr::TfTokenVector RenderTags = {pxr::HdRenderTagTokens->geometry}; + + m_ParamsDelegate->SetParameter(RenderTaskId, pxr::HdTokens->params, TaskParams); + m_ParamsDelegate->SetParameter(RenderTaskId, pxr::HdTokens->collection, Collection); + m_ParamsDelegate->SetParameter(RenderTaskId, pxr::HdTokens->renderTags, RenderTags); + + return RenderTaskId; +} + +pxr::SdfPath HnTaskController::CreatePostProcessTask() +{ + const pxr::SdfPath PostProcessTaskId = GetControllerId().AppendChild(HnTaskControllerTokens->postProcessTask); + m_RenderIndex.InsertTask(m_ParamsDelegate.get(), PostProcessTaskId); + + HnPostProcessTaskParams TaskParams; + m_ParamsDelegate->SetParameter(PostProcessTaskId, pxr::HdTokens->params, TaskParams); + + return PostProcessTaskId; +} + +const pxr::HdTaskSharedPtrVector HnTaskController::GetRenderingTasks() const +{ + pxr::HdTaskSharedPtrVector Tasks; + + for (const pxr::SdfPath& Id : m_RenderTaskIds) + { + Tasks.push_back(m_RenderIndex.GetTask(Id)); + } + + for (auto idx : {TASK_ID_POST_PROCESS}) + { + if (!m_TaskIds[idx].IsEmpty()) + { + Tasks.push_back(m_RenderIndex.GetTask(m_TaskIds[idx])); + } + } + + return Tasks; +} + +void HnTaskController::SetCollection(const pxr::HdRprimCollection& Collection) +{ + pxr::HdRprimCollection NewCollection = Collection; + for (const pxr::SdfPath& TaskId : m_RenderTaskIds) + { + pxr::HdRprimCollection OldCollection = m_ParamsDelegate->GetParameter(TaskId, pxr::HdTokens->collection); + + const pxr::TfToken& OldMaterialTag = OldCollection.GetMaterialTag(); + NewCollection.SetMaterialTag(OldMaterialTag); + + if (OldCollection == NewCollection) + continue; + + m_ParamsDelegate->SetParameter(TaskId, pxr::HdTokens->collection, NewCollection); + m_RenderIndex.GetChangeTracker().MarkTaskDirty(TaskId, pxr::HdChangeTracker::DirtyCollection); + } +} + +void HnTaskController::SetRenderParams(const HnRenderTaskParams& Params) +{ + for (const pxr::SdfPath& TaskId : m_RenderTaskIds) + { + HnRenderTaskParams OldParams = m_ParamsDelegate->GetParameter(TaskId, pxr::HdTokens->params); + if (OldParams == Params) + continue; + + m_ParamsDelegate->SetParameter(TaskId, pxr::HdTokens->params, Params); + m_RenderIndex.GetChangeTracker().MarkTaskDirty(TaskId, pxr::HdChangeTracker::DirtyParams); + } +} + +void HnTaskController::SetPostProcessParams(const HnPostProcessTaskParams& Params) +{ + const auto& TaskId = m_TaskIds[TASK_ID_POST_PROCESS]; + if (TaskId.IsEmpty()) + return; + + HnPostProcessTaskParams OldParams = m_ParamsDelegate->GetParameter(TaskId, pxr::HdTokens->params); + if (OldParams == Params) + return; + + m_ParamsDelegate->SetParameter(TaskId, pxr::HdTokens->params, Params); + m_RenderIndex.GetChangeTracker().MarkTaskDirty(TaskId, pxr::HdChangeTracker::DirtyParams); +} + +void HnTaskController::SetRenderTags(const pxr::TfTokenVector& RenderTags) +{ + for (const pxr::SdfPath& TaskId : m_RenderTaskIds) + { + pxr::TfTokenVector OldRenderTags = m_ParamsDelegate->GetParameter(TaskId, pxr::HdTokens->renderTags); + if (OldRenderTags == RenderTags) + continue; + + m_ParamsDelegate->SetParameter(TaskId, pxr::HdTokens->renderTags, RenderTags); + m_RenderIndex.GetChangeTracker().MarkTaskDirty(TaskId, pxr::HdChangeTracker::DirtyRenderTags); + } +} + +} // namespace USD + +} // namespace Diligent