diff --git a/tsd/src/tsd/algorithms/cpu/outline.cpp b/tsd/src/tsd/algorithms/cpu/outline.cpp index 756d488d6..77ed82833 100644 --- a/tsd/src/tsd/algorithms/cpu/outline.cpp +++ b/tsd/src/tsd/algorithms/cpu/outline.cpp @@ -14,17 +14,27 @@ namespace tsd::algorithms::cpu { namespace { -inline uint32_t shadePixel(uint32_t c_in) +inline uint32_t shadePixel(uint32_t c_in, uint32_t outlineColor) { auto c_in_f = helium::cvt_color_to_float4(c_in); - auto c_h = tsd::math::float4(1.f, 0.5f, 0.f, 1.f); + auto c_h = helium::cvt_color_to_float4(outlineColor); auto c_out = tsd::math::lerp(c_in_f, c_h, 0.8f); return helium::cvt_color_to_uint32(c_out); } +inline bool isValidPrimitive(uint32_t objectId, uint32_t primitiveId) +{ + return objectId != ~0u && primitiveId != ~0u; +} + +inline uint64_t primitiveKey(uint32_t objectId, uint32_t primitiveId) +{ + return (uint64_t(objectId) << 32) | primitiveId; +} + } // namespace -void outline(const uint32_t *objectId, +void outlineObject(const uint32_t *objectId, uint32_t *color, uint32_t outlineId, uint32_t width, @@ -46,7 +56,57 @@ void outline(const uint32_t *objectId, } if (cnt > 1 && cnt < 8) - color[i] = shadePixel(color[i]); + color[i] = shadePixel( + color[i], helium::cvt_color_to_uint32({1.f, 0.5f, 0.f, 1.f})); + }); +} + +void outlinePrimitives(const uint32_t *objectId, + const uint32_t *primitiveId, + uint32_t *color, + uint32_t outlineColor, + uint32_t thickness, + uint32_t width, + uint32_t height) +{ + const uint32_t radius = std::max(1u, thickness); + detail::parallel_for(0u, width * height, [=](uint32_t i) { + uint32_t y = i / width; + uint32_t x = i % width; + + bool sawValid = false; + bool sawInvalid = false; + bool sawDifferentValid = false; + uint64_t firstValidKey = 0; + + for (unsigned fy = std::max(0u, y - radius); + fy <= std::min(height - 1, y + radius) && !sawDifferentValid; + fy++) { + for (unsigned fx = std::max(0u, x - radius); + fx <= std::min(width - 1, x + radius); + fx++) { + const size_t fi = fx + size_t(width) * fy; + const uint32_t neighborObjectId = objectId[fi]; + const uint32_t neighborPrimitiveId = primitiveId[fi]; + + if (!isValidPrimitive(neighborObjectId, neighborPrimitiveId)) { + sawInvalid = true; + continue; + } + + const uint64_t key = primitiveKey(neighborObjectId, neighborPrimitiveId); + if (!sawValid) { + sawValid = true; + firstValidKey = key; + } else if (key != firstValidKey) { + sawDifferentValid = true; + break; + } + } + } + + if (sawValid && (sawInvalid || sawDifferentValid)) + color[i] = shadePixel(color[i], outlineColor); }); } diff --git a/tsd/src/tsd/algorithms/cpu/outline.hpp b/tsd/src/tsd/algorithms/cpu/outline.hpp index 8c24c35d5..cbfccf136 100644 --- a/tsd/src/tsd/algorithms/cpu/outline.hpp +++ b/tsd/src/tsd/algorithms/cpu/outline.hpp @@ -9,10 +9,21 @@ namespace tsd::algorithms::cpu { // Highlight edges of a selected object by blending an orange tint // on pixels where the 3x3 neighborhood straddles the object boundary. -void outline(const uint32_t *objectId, +void outlineObject(const uint32_t *objectId, uint32_t *color, uint32_t outlineId, uint32_t width, uint32_t height); +// Highlight edges between neighboring primitive identities, where identity is +// defined by the pair (objectId, primitiveId). Background transitions are +// treated as silhouettes. +void outlinePrimitives(const uint32_t *objectId, + const uint32_t *primitiveId, + uint32_t *color, + uint32_t outlineColor, + uint32_t thickness, + uint32_t width, + uint32_t height); + } // namespace tsd::algorithms::cpu diff --git a/tsd/src/tsd/algorithms/cuda/outline.cu b/tsd/src/tsd/algorithms/cuda/outline.cu index 1de4ccee4..e285b9c41 100644 --- a/tsd/src/tsd/algorithms/cuda/outline.cu +++ b/tsd/src/tsd/algorithms/cuda/outline.cu @@ -17,17 +17,30 @@ namespace tsd::algorithms::cuda { namespace { -__forceinline__ __device__ uint32_t shadePixel(uint32_t c_in) +__forceinline__ __device__ uint32_t shadePixel( + uint32_t c_in, uint32_t outlineColor) { auto c_in_f = helium::cvt_color_to_float4(c_in); - auto c_h = tsd::math::float4(1.f, 0.5f, 0.f, 1.f); + auto c_h = helium::cvt_color_to_float4(outlineColor); auto c_out = tsd::math::lerp(c_in_f, c_h, 0.8f); return helium::cvt_color_to_uint32(c_out); } +__forceinline__ __device__ bool isValidPrimitive( + uint32_t objectId, uint32_t primitiveId) +{ + return objectId != ~0u && primitiveId != ~0u; +} + +__forceinline__ __device__ uint64_t primitiveKey( + uint32_t objectId, uint32_t primitiveId) +{ + return (uint64_t(objectId) << 32) | primitiveId; +} + } // namespace -void outline(cudaStream_t stream, +void outlineObject(cudaStream_t stream, const uint32_t *objectId, uint32_t *color, uint32_t outlineId, @@ -42,8 +55,11 @@ void outline(cudaStream_t stream, uint32_t x = i % width; int cnt = 0; - for (unsigned fy = max(0u, y - 1); fy <= min(height - 1, y + 1); fy++) { - for (unsigned fx = max(0u, x - 1); fx <= min(width - 1, x + 1); + for (unsigned fy = std::max(0u, y - 1); + fy <= std::min(height - 1, y + 1); + fy++) { + for (unsigned fx = std::max(0u, x - 1); + fx <= std::min(width - 1, x + 1); fx++) { size_t fi = fx + size_t(width) * fy; if (objectId[fi] == outlineId) @@ -52,17 +68,90 @@ void outline(cudaStream_t stream, } if (cnt > 1 && cnt < 8) - color[i] = shadePixel(color[i]); + color[i] = shadePixel( + color[i], helium::cvt_color_to_uint32({1.f, 0.5f, 0.f, 1.f})); }); } -void outline(const uint32_t *objectId, +void outlineObject(const uint32_t *objectId, uint32_t *color, uint32_t outlineId, uint32_t width, uint32_t height) { - outline(cudaStream_t{0}, objectId, color, outlineId, width, height); + outlineObject(cudaStream_t{0}, objectId, color, outlineId, width, height); +} + +void outlinePrimitives(cudaStream_t stream, + const uint32_t *objectId, + const uint32_t *primitiveId, + uint32_t *color, + uint32_t outlineColor, + uint32_t thickness, + uint32_t width, + uint32_t height) +{ + const uint32_t radius = std::max(1u, thickness); + thrust::for_each(thrust::cuda::par.on(stream), + thrust::make_counting_iterator(0u), + thrust::make_counting_iterator(width * height), + [=] __device__(uint32_t i) { + uint32_t y = i / width; + uint32_t x = i % width; + + bool sawValid = false; + bool sawInvalid = false; + bool sawDifferentValid = false; + uint64_t firstValidKey = 0; + + for (unsigned fy = std::max(0u, y - radius); + fy <= std::min(height - 1, y + radius) && !sawDifferentValid; + fy++) { + for (unsigned fx = std::max(0u, x - radius); + fx <= std::min(width - 1, x + radius); + fx++) { + const size_t fi = fx + size_t(width) * fy; + const uint32_t neighborObjectId = objectId[fi]; + const uint32_t neighborPrimitiveId = primitiveId[fi]; + + if (!isValidPrimitive(neighborObjectId, neighborPrimitiveId)) { + sawInvalid = true; + continue; + } + + const uint64_t key = + primitiveKey(neighborObjectId, neighborPrimitiveId); + if (!sawValid) { + sawValid = true; + firstValidKey = key; + } else if (key != firstValidKey) { + sawDifferentValid = true; + break; + } + } + } + + if (sawValid && (sawInvalid || sawDifferentValid)) + color[i] = shadePixel(color[i], outlineColor); + }); +} + +void outlinePrimitives(const uint32_t *objectId, + const uint32_t *primitiveId, + uint32_t *color, + uint32_t outlineColor, + uint32_t thickness, + uint32_t width, + uint32_t height) +{ + outlinePrimitives(cudaStream_t{0}, + objectId, + primitiveId, + color, + outlineColor, + thickness, + width, + height); } } // namespace tsd::algorithms::cuda diff --git a/tsd/src/tsd/algorithms/cuda/outline.hpp b/tsd/src/tsd/algorithms/cuda/outline.hpp index 048b508ad..245b41d20 100644 --- a/tsd/src/tsd/algorithms/cuda/outline.hpp +++ b/tsd/src/tsd/algorithms/cuda/outline.hpp @@ -8,17 +8,34 @@ namespace tsd::algorithms::cuda { -void outline(cudaStream_t stream, +void outlineObject(cudaStream_t stream, const uint32_t *objectId, uint32_t *color, uint32_t outlineId, uint32_t width, uint32_t height); -void outline(const uint32_t *objectId, +void outlineObject(const uint32_t *objectId, uint32_t *color, uint32_t outlineId, uint32_t width, uint32_t height); +void outlinePrimitives(cudaStream_t stream, + const uint32_t *objectId, + const uint32_t *primitiveId, + uint32_t *color, + uint32_t outlineColor, + uint32_t thickness, + uint32_t width, + uint32_t height); + +void outlinePrimitives(const uint32_t *objectId, + const uint32_t *primitiveId, + uint32_t *color, + uint32_t outlineColor, + uint32_t thickness, + uint32_t width, + uint32_t height); + } // namespace tsd::algorithms::cuda diff --git a/tsd/src/tsd/rendering/CMakeLists.txt b/tsd/src/tsd/rendering/CMakeLists.txt index 3a70d6308..7d6168202 100644 --- a/tsd/src/tsd/rendering/CMakeLists.txt +++ b/tsd/src/tsd/rendering/CMakeLists.txt @@ -19,6 +19,7 @@ PRIVATE pipeline/passes/CopyToSDLTexturePass.cpp pipeline/passes/MultiDeviceSceneRenderPass.cpp pipeline/passes/OutlineRenderPass.cpp + pipeline/passes/PrimitiveOutlineRenderPass.cpp pipeline/passes/OutputTransformPass.cpp pipeline/passes/PickPass.cpp pipeline/passes/ImagePass.cpp @@ -47,6 +48,7 @@ if (TSD_USE_CUDA) pipeline/passes/ClearBuffersPass.cpp pipeline/passes/ImagePass.cpp pipeline/passes/OutlineRenderPass.cpp + pipeline/passes/PrimitiveOutlineRenderPass.cpp pipeline/passes/OutputTransformPass.cpp pipeline/passes/ToneMapPass.cpp pipeline/passes/VisualizeAOVPass.cpp diff --git a/tsd/src/tsd/rendering/pipeline/ImagePipeline.h b/tsd/src/tsd/rendering/pipeline/ImagePipeline.h index dc994c05f..d53952dd5 100644 --- a/tsd/src/tsd/rendering/pipeline/ImagePipeline.h +++ b/tsd/src/tsd/rendering/pipeline/ImagePipeline.h @@ -13,6 +13,7 @@ #include "passes/CopyToColorBufferPass.hpp" #include "passes/MultiDeviceSceneRenderPass.h" #include "passes/OutlineRenderPass.h" +#include "passes/PrimitiveOutlineRenderPass.h" #include "passes/PickPass.h" #include "passes/SaveToFilePass.h" #include "passes/VisualizeAOVPass.h" diff --git a/tsd/src/tsd/rendering/pipeline/passes/OutlineRenderPass.cpp b/tsd/src/tsd/rendering/pipeline/passes/OutlineRenderPass.cpp index c13fb6dc1..98e7382a6 100644 --- a/tsd/src/tsd/rendering/pipeline/passes/OutlineRenderPass.cpp +++ b/tsd/src/tsd/rendering/pipeline/passes/OutlineRenderPass.cpp @@ -30,12 +30,12 @@ void OutlineRenderPass::render(ImageBuffers &b, int stageId) #ifdef TSD_ALGORITHMS_HAS_CUDA if (b.stream) { - tsd::algorithms::cuda::outline( + tsd::algorithms::cuda::outlineObject( b.stream, b.objectId, b.color, m_outlineId, size.x, size.y); return; } #endif - tsd::algorithms::cpu::outline( + tsd::algorithms::cpu::outlineObject( b.objectId, b.color, m_outlineId, size.x, size.y); } diff --git a/tsd/src/tsd/rendering/pipeline/passes/PrimitiveOutlineRenderPass.cpp b/tsd/src/tsd/rendering/pipeline/passes/PrimitiveOutlineRenderPass.cpp new file mode 100644 index 000000000..340f01be7 --- /dev/null +++ b/tsd/src/tsd/rendering/pipeline/passes/PrimitiveOutlineRenderPass.cpp @@ -0,0 +1,60 @@ +// Copyright 2026 NVIDIA Corporation +// SPDX-License-Identifier: Apache-2.0 + +#include "PrimitiveOutlineRenderPass.h" +// tsd_algorithms +#include "tsd/algorithms/cpu/outline.hpp" +#ifdef TSD_ALGORITHMS_HAS_CUDA +#include "tsd/algorithms/cuda/outline.hpp" +#endif +// helium +#include + +namespace tsd::rendering { + +PrimitiveOutlineRenderPass::PrimitiveOutlineRenderPass() = default; + +PrimitiveOutlineRenderPass::~PrimitiveOutlineRenderPass() = default; + +void PrimitiveOutlineRenderPass::setOutlineColor( + const tsd::math::float4 &color) +{ + m_outlineColor = color; +} + +void PrimitiveOutlineRenderPass::setThickness(uint32_t thickness) +{ + m_thickness = thickness; +} + +void PrimitiveOutlineRenderPass::render(ImageBuffers &b, int stageId) +{ + if (!b.objectId || !b.primitiveId || stageId == 0) + return; + + const auto size = getDimensions(); + const auto outlineColor = helium::cvt_color_to_uint32(m_outlineColor); + +#ifdef TSD_ALGORITHMS_HAS_CUDA + if (b.stream) { + tsd::algorithms::cuda::outlinePrimitives(b.stream, + b.objectId, + b.primitiveId, + b.color, + outlineColor, + m_thickness, + size.x, + size.y); + return; + } +#endif + tsd::algorithms::cpu::outlinePrimitives(b.objectId, + b.primitiveId, + b.color, + outlineColor, + m_thickness, + size.x, + size.y); +} + +} // namespace tsd::rendering diff --git a/tsd/src/tsd/rendering/pipeline/passes/PrimitiveOutlineRenderPass.h b/tsd/src/tsd/rendering/pipeline/passes/PrimitiveOutlineRenderPass.h new file mode 100644 index 000000000..b8391a5ca --- /dev/null +++ b/tsd/src/tsd/rendering/pipeline/passes/PrimitiveOutlineRenderPass.h @@ -0,0 +1,28 @@ +// Copyright 2026 NVIDIA Corporation +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "ImagePass.h" +// tsd_core +#include "tsd/core/TSDMath.hpp" + +namespace tsd::rendering { + +struct PrimitiveOutlineRenderPass : public ImagePass +{ + PrimitiveOutlineRenderPass(); + ~PrimitiveOutlineRenderPass() override; + const char *name() const override { return "Primitive Outline"; } + + void setOutlineColor(const tsd::math::float4 &color); + void setThickness(uint32_t thickness); + + private: + void render(ImageBuffers &b, int stageId) override; + + tsd::math::float4 m_outlineColor{0.8f, 0.8f, 0.8f, 1.f}; + uint32_t m_thickness{1}; +}; + +} // namespace tsd::rendering diff --git a/tsd/src/tsd/ui/imgui/windows/Viewport.cpp b/tsd/src/tsd/ui/imgui/windows/Viewport.cpp index b856afe45..ec5a4828a 100644 --- a/tsd/src/tsd/ui/imgui/windows/Viewport.cpp +++ b/tsd/src/tsd/ui/imgui/windows/Viewport.cpp @@ -26,6 +26,29 @@ namespace tsd::ui::imgui { +namespace { + +bool deviceSupportsExtension(anari::Device d, const char *extension) +{ + if (!d || !extension) + return false; + + auto list = (const char *const *)anariGetObjectInfo( + d, ANARI_DEVICE, "default", "extension", ANARI_STRING_LIST); + + if (!list) + return false; + + for (const char *const *i = list; *i != nullptr; ++i) { + if (std::string(*i) == extension) + return true; + } + + return false; +} + +} // namespace + Viewport::Viewport( Application *app, tsd::rendering::Manipulator *m, const char *name) : BaseViewport(app, name) @@ -77,9 +100,13 @@ void Viewport::buildUI() ImGui::EndDisabled(); if (m_anariPass && !didPick) { + const bool doPrimitiveOutline = m_outlinePrimitives + && m_deviceSupportsPrimitiveId + && m_visualizeAOV == tsd::rendering::AOVType::NONE; bool needIDs = appContext()->getFirstSelected().valid() || m_visualizeAOV == tsd::rendering::AOVType::EDGES - || m_visualizeAOV == tsd::rendering::AOVType::OBJECT_ID; + || m_visualizeAOV == tsd::rendering::AOVType::OBJECT_ID + || doPrimitiveOutline; m_anariPass->setEnableIDs(needIDs); } @@ -117,6 +144,13 @@ void Viewport::setLibrary(const std::string &libName) if (d) { m_device = d; + m_deviceSupportsPrimitiveId = deviceSupportsExtension( + d, "ANARI_KHR_FRAME_CHANNEL_PRIMITIVE_ID"); + + if (!m_deviceSupportsPrimitiveId + && m_visualizeAOV == tsd::rendering::AOVType::PRIMITIVE_ID) { + m_visualizeAOV = tsd::rendering::AOVType::NONE; + } tsd::core::logStatus("[viewport] setting up renderer objects..."); @@ -231,7 +265,7 @@ void Viewport::saveSettings(tsd::core::DataNode &root) root["showOverlay"] = m_showOverlay; root["showOnlySelected"] = m_showOnlySelected; root["highlightSelection"] = m_highlightSelection; - root["showOnlySelected"] = m_showOnlySelected; + root["outlinePrimitives"] = m_outlinePrimitives; root["visualizeAOV"] = static_cast(m_visualizeAOV); root["depthVisualMinimum"] = m_depthVisualMinimum; root["depthVisualMaximum"] = m_depthVisualMaximum; @@ -260,7 +294,7 @@ void Viewport::loadSettings(tsd::core::DataNode &root) root["showOverlay"].getValue(ANARI_BOOL, &m_showOverlay); root["showOnlySelected"].getValue(ANARI_BOOL, &m_showOnlySelected); root["highlightSelection"].getValue(ANARI_BOOL, &m_highlightSelection); - root["showOnlySelected"].getValue(ANARI_BOOL, &m_showOnlySelected); + root["outlinePrimitives"].getValue(ANARI_BOOL, &m_outlinePrimitives); int aovType = static_cast(m_visualizeAOV); root["visualizeAOV"].getValue(ANARI_INT32, &aovType); m_visualizeAOV = static_cast(aovType); @@ -410,6 +444,9 @@ void Viewport::imagePipeline_populate(tsd::rendering::ImagePipeline &p) m_visualizeAOVPass->setEnabled(false); m_visualizeAOVPass->setEdgeInvert(m_edgeInvert); + m_primitiveOutlinePass = + p.emplace_back(); + m_outlinePass = p.emplace_back(); m_outputPass = p.emplace_back( @@ -491,6 +528,7 @@ void Viewport::teardownDevice() m_autoExposurePass = nullptr; m_toneMapPass = nullptr; m_outputTransformPass = nullptr; + m_primitiveOutlinePass = nullptr; m_outlinePass = nullptr; m_outputPass = nullptr; m_saveToFilePass = nullptr; @@ -509,6 +547,7 @@ void Viewport::teardownDevice() m_prevRenderer = {}; m_device = nullptr; + m_deviceSupportsPrimitiveId = false; } void Viewport::pick(tsd::math::int2 l, bool selectObject) @@ -612,17 +651,25 @@ void Viewport::syncImagePassState() m_visualizeAOV == tsd::rendering::AOVType::ALBEDO); m_anariPass->setEnableNormals( m_visualizeAOV == tsd::rendering::AOVType::NORMAL); - m_anariPass->setEnablePrimitiveId( - m_visualizeAOV == tsd::rendering::AOVType::PRIMITIVE_ID); + const bool doPrimitiveOutline = m_outlinePrimitives + && m_deviceSupportsPrimitiveId + && m_visualizeAOV == tsd::rendering::AOVType::NONE; + m_anariPass->setEnablePrimitiveId(m_deviceSupportsPrimitiveId + && (m_visualizeAOV == tsd::rendering::AOVType::PRIMITIVE_ID + || doPrimitiveOutline)); m_anariPass->setEnableInstanceId( m_visualizeAOV == tsd::rendering::AOVType::INSTANCE_ID); const auto selectedNode = appContext()->getFirstSelected(); const bool needIDs = selectedNode.valid() || m_visualizeAOV == tsd::rendering::AOVType::EDGES - || m_visualizeAOV == tsd::rendering::AOVType::OBJECT_ID; + || m_visualizeAOV == tsd::rendering::AOVType::OBJECT_ID + || doPrimitiveOutline; m_anariPass->setEnableIDs(needIDs); + if (m_primitiveOutlinePass) + m_primitiveOutlinePass->setEnabled(doPrimitiveOutline); + updateDisplayPassState(); } @@ -749,12 +796,24 @@ void Viewport::ui_menubar_Viewport() "object ID", "primitive ID", "instance ID"}; - if (int aov = int(m_visualizeAOV); - ImGui::Combo("AOV", &aov, aovItems, IM_ARRAYSIZE(aovItems))) { - if (aov != int(m_visualizeAOV)) { - m_visualizeAOV = static_cast(aov); - syncImagePassState(); + const int primitiveIdAOV = int(tsd::rendering::AOVType::PRIMITIVE_ID); + if (ImGui::BeginCombo("AOV", aovItems[int(m_visualizeAOV)])) { + for (int i = 0; i < IM_ARRAYSIZE(aovItems); ++i) { + const bool isSelected = i == int(m_visualizeAOV); + const bool supported = + i != primitiveIdAOV || m_deviceSupportsPrimitiveId; + if (!supported) + ImGui::BeginDisabled(); + if (ImGui::Selectable(aovItems[i], isSelected) && supported) { + m_visualizeAOV = static_cast(i); + syncImagePassState(); + } + if (isSelected) + ImGui::SetItemDefaultFocus(); + if (!supported) + ImGui::EndDisabled(); } + ImGui::EndCombo(); } ImGui::BeginDisabled(m_visualizeAOV != tsd::rendering::AOVType::DEPTH); @@ -860,6 +919,11 @@ void Viewport::ui_menubar_Viewport() ImGui::Checkbox("Highlight Selected", &m_highlightSelection); ImGui::EndDisabled(); + ImGui::BeginDisabled(!m_deviceSupportsPrimitiveId); + if (ImGui::Checkbox("Outline Primitives", &m_outlinePrimitives)) + syncImagePassState(); + ImGui::EndDisabled(); + if (ImGui::Checkbox("Only Show Selected", &m_showOnlySelected)) setSelectionVisibilityFilterEnabled(m_showOnlySelected); diff --git a/tsd/src/tsd/ui/imgui/windows/Viewport.h b/tsd/src/tsd/ui/imgui/windows/Viewport.h index 69a8bd632..49e35c56d 100644 --- a/tsd/src/tsd/ui/imgui/windows/Viewport.h +++ b/tsd/src/tsd/ui/imgui/windows/Viewport.h @@ -14,6 +14,7 @@ #include "tsd/rendering/pipeline/passes/AutoExposurePass.h" #include "tsd/rendering/pipeline/passes/CopyToSDLTexturePass.h" #include "tsd/rendering/pipeline/passes/OutlineRenderPass.h" +#include "tsd/rendering/pipeline/passes/PrimitiveOutlineRenderPass.h" #include "tsd/rendering/pipeline/passes/OutputTransformPass.h" #include "tsd/rendering/pipeline/passes/PickPass.h" #include "tsd/rendering/pipeline/passes/SaveToFilePass.h" @@ -92,8 +93,10 @@ struct Viewport : public BaseViewport bool m_showOverlay{true}; bool m_highlightSelection{true}; + bool m_outlinePrimitives{false}; bool m_showOnlySelected{false}; std::optional m_frameProgress{0.f}; + bool m_deviceSupportsPrimitiveId{false}; tsd::rendering::AOVType m_visualizeAOV{tsd::rendering::AOVType::NONE}; float m_depthVisualMinimum{0.f}; @@ -129,6 +132,7 @@ struct Viewport : public BaseViewport tsd::rendering::AutoExposurePass *m_autoExposurePass{nullptr}; tsd::rendering::ToneMapPass *m_toneMapPass{nullptr}; tsd::rendering::OutputTransformPass *m_outputTransformPass{nullptr}; + tsd::rendering::PrimitiveOutlineRenderPass *m_primitiveOutlinePass{nullptr}; tsd::rendering::OutlineRenderPass *m_outlinePass{nullptr}; tsd::rendering::CopyToSDLTexturePass *m_outputPass{nullptr}; tsd::rendering::SaveToFilePass *m_saveToFilePass{nullptr}; diff --git a/tsd/tests/test_Algorithms.cpp b/tsd/tests/test_Algorithms.cpp index bdf2d8ba2..8cd993b11 100644 --- a/tsd/tests/test_Algorithms.cpp +++ b/tsd/tests/test_Algorithms.cpp @@ -377,7 +377,7 @@ SCENARIO("Host compositing and outlining update only the intended pixels", WHEN("Outline shading is applied") { - cpu::outline(objectId.data(), color.data(), 5u, 4u, 4u); + cpu::outlineObject(objectId.data(), color.data(), 5u, 4u, 4u); THEN("Boundary-adjacent pixels are shaded while distant pixels are not") {