Fix scene selection outline mask path

This commit is contained in:
2026-04-09 05:16:04 +08:00
parent c48311eaaf
commit 2084412010
29 changed files with 1250 additions and 56 deletions

View File

@@ -1,5 +1,7 @@
#include "Passes/SceneViewportSelectionOutlinePass.h" #include "Passes/SceneViewportSelectionOutlinePass.h"
#include "Viewport/ViewportHostRenderTargets.h"
namespace XCEngine { namespace XCEngine {
namespace Editor { namespace Editor {
@@ -9,11 +11,11 @@ class SceneViewportSelectionOutlinePass final : public Rendering::RenderPass {
public: public:
SceneViewportSelectionOutlinePass( SceneViewportSelectionOutlinePass(
SceneViewportSelectionOutlinePassRenderer& renderer, SceneViewportSelectionOutlinePassRenderer& renderer,
RHI::RHIResourceView* objectIdTextureView, ViewportRenderTargets* targets,
std::vector<uint64_t> selectedObjectIds, std::vector<uint64_t> selectedObjectIds,
const SceneViewportSelectionOutlineStyle& style) const SceneViewportSelectionOutlineStyle& style)
: m_renderer(renderer) : m_renderer(renderer)
, m_objectIdTextureView(objectIdTextureView) , m_targets(targets)
, m_selectedObjectIds(std::move(selectedObjectIds)) , m_selectedObjectIds(std::move(selectedObjectIds))
, m_style(style) { , m_style(style) {
} }
@@ -26,14 +28,15 @@ public:
return m_renderer.Render( return m_renderer.Render(
context.renderContext, context.renderContext,
context.surface, context.surface,
m_objectIdTextureView, context.sceneData,
*m_targets,
m_selectedObjectIds, m_selectedObjectIds,
m_style); m_style);
} }
private: private:
SceneViewportSelectionOutlinePassRenderer& m_renderer; SceneViewportSelectionOutlinePassRenderer& m_renderer;
RHI::RHIResourceView* m_objectIdTextureView = nullptr; ViewportRenderTargets* m_targets = nullptr;
std::vector<uint64_t> m_selectedObjectIds = {}; std::vector<uint64_t> m_selectedObjectIds = {};
SceneViewportSelectionOutlineStyle m_style = {}; SceneViewportSelectionOutlineStyle m_style = {};
}; };
@@ -41,35 +44,51 @@ private:
} // namespace } // namespace
SceneViewportSelectionOutlinePassRenderer::SceneViewportSelectionOutlinePassRenderer() SceneViewportSelectionOutlinePassRenderer::SceneViewportSelectionOutlinePassRenderer()
: m_outlinePass() { : m_selectionMaskPass()
, m_outlinePass() {
} }
void SceneViewportSelectionOutlinePassRenderer::Shutdown() { void SceneViewportSelectionOutlinePassRenderer::Shutdown() {
m_selectionMaskPass.Shutdown();
m_outlinePass.Shutdown(); m_outlinePass.Shutdown();
} }
bool SceneViewportSelectionOutlinePassRenderer::Render( bool SceneViewportSelectionOutlinePassRenderer::Render(
const Rendering::RenderContext& renderContext, const Rendering::RenderContext& renderContext,
const Rendering::RenderSurface& surface, const Rendering::RenderSurface& surface,
RHI::RHIResourceView* objectIdTextureView, const Rendering::RenderSceneData& sceneData,
ViewportRenderTargets& targets,
const std::vector<uint64_t>& selectedObjectIds, const std::vector<uint64_t>& selectedObjectIds,
const SceneViewportSelectionOutlineStyle& style) { const SceneViewportSelectionOutlineStyle& style) {
Rendering::RenderSurface selectionMaskSurface = BuildViewportSelectionMaskSurface(targets);
selectionMaskSurface.SetRenderArea(surface.GetRenderArea());
if (!m_selectionMaskPass.Render(
renderContext,
selectionMaskSurface,
sceneData,
selectedObjectIds)) {
return false;
}
targets.selectionMaskState = RHI::ResourceStates::PixelShaderResource;
return m_outlinePass.Render( return m_outlinePass.Render(
renderContext, renderContext,
surface, surface,
objectIdTextureView, targets.selectionMaskShaderView,
selectedObjectIds, targets.depthShaderView,
ToBuiltinSceneViewportSelectionOutlineStyle(style)); ToBuiltinSceneViewportSelectionOutlineStyle(style));
} }
std::unique_ptr<Rendering::RenderPass> CreateSceneViewportSelectionOutlinePass( std::unique_ptr<Rendering::RenderPass> CreateSceneViewportSelectionOutlinePass(
SceneViewportSelectionOutlinePassRenderer& renderer, SceneViewportSelectionOutlinePassRenderer& renderer,
RHI::RHIResourceView* objectIdTextureView, ViewportRenderTargets* targets,
const std::vector<uint64_t>& selectedObjectIds, const std::vector<uint64_t>& selectedObjectIds,
const SceneViewportSelectionOutlineStyle& style) { const SceneViewportSelectionOutlineStyle& style) {
return std::make_unique<SceneViewportSelectionOutlinePass>( return std::make_unique<SceneViewportSelectionOutlinePass>(
renderer, renderer,
objectIdTextureView, targets,
selectedObjectIds, selectedObjectIds,
style); style);
} }

View File

@@ -2,7 +2,8 @@
#include "Viewport/SceneViewportPassSpecs.h" #include "Viewport/SceneViewportPassSpecs.h"
#include <XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass.h> #include <XCEngine/Rendering/Passes/BuiltinSelectionMaskPass.h>
#include <XCEngine/Rendering/Passes/BuiltinSelectionOutlinePass.h>
#include <XCEngine/Rendering/RenderContext.h> #include <XCEngine/Rendering/RenderContext.h>
#include <XCEngine/Rendering/RenderPass.h> #include <XCEngine/Rendering/RenderPass.h>
#include <XCEngine/Rendering/RenderSurface.h> #include <XCEngine/Rendering/RenderSurface.h>
@@ -14,6 +15,8 @@
namespace XCEngine { namespace XCEngine {
namespace Editor { namespace Editor {
struct ViewportRenderTargets;
class SceneViewportSelectionOutlinePassRenderer { class SceneViewportSelectionOutlinePassRenderer {
public: public:
SceneViewportSelectionOutlinePassRenderer(); SceneViewportSelectionOutlinePassRenderer();
@@ -24,17 +27,19 @@ public:
bool Render( bool Render(
const Rendering::RenderContext& renderContext, const Rendering::RenderContext& renderContext,
const Rendering::RenderSurface& surface, const Rendering::RenderSurface& surface,
RHI::RHIResourceView* objectIdTextureView, const Rendering::RenderSceneData& sceneData,
ViewportRenderTargets& targets,
const std::vector<uint64_t>& selectedObjectIds, const std::vector<uint64_t>& selectedObjectIds,
const SceneViewportSelectionOutlineStyle& style); const SceneViewportSelectionOutlineStyle& style);
private: private:
Rendering::Passes::BuiltinObjectIdOutlinePass m_outlinePass; Rendering::Passes::BuiltinSelectionMaskPass m_selectionMaskPass;
Rendering::Passes::BuiltinSelectionOutlinePass m_outlinePass;
}; };
std::unique_ptr<Rendering::RenderPass> CreateSceneViewportSelectionOutlinePass( std::unique_ptr<Rendering::RenderPass> CreateSceneViewportSelectionOutlinePass(
SceneViewportSelectionOutlinePassRenderer& renderer, SceneViewportSelectionOutlinePassRenderer& renderer,
RHI::RHIResourceView* objectIdTextureView, ViewportRenderTargets* targets,
const std::vector<uint64_t>& selectedObjectIds, const std::vector<uint64_t>& selectedObjectIds,
const SceneViewportSelectionOutlineStyle& style); const SceneViewportSelectionOutlineStyle& style);

View File

@@ -3,7 +3,7 @@
#include <XCEngine/Core/Math/Color.h> #include <XCEngine/Core/Math/Color.h>
#include <XCEngine/Core/Math/Vector3.h> #include <XCEngine/Core/Math/Vector3.h>
#include <XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h> #include <XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h>
#include <XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass.h> #include <XCEngine/Rendering/Passes/BuiltinSelectionOutlinePass.h>
namespace XCEngine { namespace XCEngine {
namespace Editor { namespace Editor {
@@ -41,9 +41,9 @@ struct SceneViewportSelectionOutlineStyle {
bool debugSelectionMask = false; bool debugSelectionMask = false;
}; };
inline Rendering::Passes::ObjectIdOutlineStyle ToBuiltinSceneViewportSelectionOutlineStyle( inline Rendering::Passes::SelectionOutlineStyle ToBuiltinSceneViewportSelectionOutlineStyle(
const SceneViewportSelectionOutlineStyle& style) { const SceneViewportSelectionOutlineStyle& style) {
Rendering::Passes::ObjectIdOutlineStyle builtinStyle = {}; Rendering::Passes::SelectionOutlineStyle builtinStyle = {};
builtinStyle.outlineColor = style.outlineColor; builtinStyle.outlineColor = style.outlineColor;
builtinStyle.outlineWidthPixels = style.outlineWidthPixels; builtinStyle.outlineWidthPixels = style.outlineWidthPixels;
builtinStyle.debugSelectionMask = style.debugSelectionMask; builtinStyle.debugSelectionMask = style.debugSelectionMask;

View File

@@ -10,7 +10,7 @@ void SceneViewportRenderPassBundle::Shutdown() {
} }
SceneViewportRenderPlanBuildResult SceneViewportRenderPassBundle::BuildRenderPlan( SceneViewportRenderPlanBuildResult SceneViewportRenderPassBundle::BuildRenderPlan(
const ViewportRenderTargets& targets, ViewportRenderTargets& targets,
const SceneViewportOverlayData& overlay, const SceneViewportOverlayData& overlay,
const std::vector<uint64_t>& selectedObjectIds, const std::vector<uint64_t>& selectedObjectIds,
const SceneViewportOverlayFrameData& editorOverlayFrameData, const SceneViewportOverlayFrameData& editorOverlayFrameData,
@@ -24,12 +24,12 @@ SceneViewportRenderPlanBuildResult SceneViewportRenderPassBundle::BuildRenderPla
return CreateSceneViewportGridPass(m_gridRenderer, data); return CreateSceneViewportGridPass(m_gridRenderer, data);
}, },
[this]( [this](
RHI::RHIResourceView* objectIdTextureView, ViewportRenderTargets* outlineTargets,
const std::vector<uint64_t>& selectionIds, const std::vector<uint64_t>& selectionIds,
const SceneViewportSelectionOutlineStyle& style) { const SceneViewportSelectionOutlineStyle& style) {
return CreateSceneViewportSelectionOutlinePass( return CreateSceneViewportSelectionOutlinePass(
m_selectionOutlineRenderer, m_selectionOutlineRenderer,
objectIdTextureView, outlineTargets,
selectionIds, selectionIds,
style); style);
}, },

View File

@@ -15,7 +15,7 @@ public:
void Shutdown(); void Shutdown();
SceneViewportRenderPlanBuildResult BuildRenderPlan( SceneViewportRenderPlanBuildResult BuildRenderPlan(
const ViewportRenderTargets& targets, ViewportRenderTargets& targets,
const SceneViewportOverlayData& overlay, const SceneViewportOverlayData& overlay,
const std::vector<uint64_t>& selectedObjectIds, const std::vector<uint64_t>& selectedObjectIds,
const SceneViewportOverlayFrameData& editorOverlayFrameData, const SceneViewportOverlayFrameData& editorOverlayFrameData,

View File

@@ -37,7 +37,7 @@ using SceneViewportOverlayPassFactory =
using SceneViewportGridPassFactory = using SceneViewportGridPassFactory =
std::function<std::unique_ptr<Rendering::RenderPass>(const SceneViewportGridPassData&)>; std::function<std::unique_ptr<Rendering::RenderPass>(const SceneViewportGridPassData&)>;
using SceneViewportSelectionOutlinePassFactory = std::function<std::unique_ptr<Rendering::RenderPass>( using SceneViewportSelectionOutlinePassFactory = std::function<std::unique_ptr<Rendering::RenderPass>(
RHI::RHIResourceView*, ViewportRenderTargets*,
const std::vector<uint64_t>&, const std::vector<uint64_t>&,
const SceneViewportSelectionOutlineStyle&)>; const SceneViewportSelectionOutlineStyle&)>;
@@ -47,7 +47,7 @@ struct SceneViewportRenderPlanBuildResult {
}; };
inline SceneViewportRenderPlanBuildResult BuildSceneViewportRenderPlan( inline SceneViewportRenderPlanBuildResult BuildSceneViewportRenderPlan(
const ViewportRenderTargets& targets, ViewportRenderTargets& targets,
const SceneViewportOverlayData& overlay, const SceneViewportOverlayData& overlay,
const std::vector<uint64_t>& selectedObjectIds, const std::vector<uint64_t>& selectedObjectIds,
const SceneViewportOverlayFrameData& editorOverlayFrameData, const SceneViewportOverlayFrameData& editorOverlayFrameData,
@@ -69,18 +69,21 @@ inline SceneViewportRenderPlanBuildResult BuildSceneViewportRenderPlan(
} }
if (!selectedObjectIds.empty()) { if (!selectedObjectIds.empty()) {
if (targets.objectIdShaderView != nullptr && if (targets.selectionMaskView != nullptr &&
targets.selectionMaskShaderView != nullptr &&
targets.depthView != nullptr &&
targets.depthShaderView != nullptr &&
selectionOutlinePassFactory != nullptr) { selectionOutlinePassFactory != nullptr) {
std::unique_ptr<Rendering::RenderPass> selectionOutlinePass = std::unique_ptr<Rendering::RenderPass> selectionOutlinePass =
selectionOutlinePassFactory( selectionOutlinePassFactory(
targets.objectIdShaderView, &targets,
selectedObjectIds, selectedObjectIds,
BuildSceneViewportSelectionOutlineStyle(debugSelectionMask)); BuildSceneViewportSelectionOutlineStyle(debugSelectionMask));
if (selectionOutlinePass != nullptr) { if (selectionOutlinePass != nullptr) {
result.plan.postScenePasses.AddPass(std::move(selectionOutlinePass)); result.plan.postScenePasses.AddPass(std::move(selectionOutlinePass));
} }
} else if (!debugSelectionMask) { } else if (!debugSelectionMask) {
result.warningStatusText = "Scene object id shader view is unavailable"; result.warningStatusText = "Scene selection outline resources are unavailable";
} }
} }

View File

@@ -152,7 +152,7 @@ inline void ApplySceneViewportRenderRequestSetup(
request.postScenePasses = postPasses; request.postScenePasses = postPasses;
} }
if (targets.objectIdView == nullptr) { if (targets.objectIdView == nullptr || targets.objectIdDepthView == nullptr) {
return; return;
} }
@@ -165,6 +165,7 @@ inline void MarkSceneViewportRenderSuccess(
const Rendering::CameraRenderRequest& request) { const Rendering::CameraRenderRequest& request) {
targets.colorState = RHI::ResourceStates::PixelShaderResource; targets.colorState = RHI::ResourceStates::PixelShaderResource;
targets.objectIdState = RHI::ResourceStates::PixelShaderResource; targets.objectIdState = RHI::ResourceStates::PixelShaderResource;
targets.selectionMaskState = RHI::ResourceStates::PixelShaderResource;
targets.hasValidObjectIdFrame = request.objectId.IsRequested(); targets.hasValidObjectIdFrame = request.objectId.IsRequested();
} }

View File

@@ -18,14 +18,21 @@ struct ViewportRenderTargets {
RHI::RHIResourceView* colorView = nullptr; RHI::RHIResourceView* colorView = nullptr;
RHI::RHITexture* depthTexture = nullptr; RHI::RHITexture* depthTexture = nullptr;
RHI::RHIResourceView* depthView = nullptr; RHI::RHIResourceView* depthView = nullptr;
RHI::RHIResourceView* depthShaderView = nullptr;
RHI::RHITexture* objectIdTexture = nullptr; RHI::RHITexture* objectIdTexture = nullptr;
RHI::RHITexture* objectIdDepthTexture = nullptr;
RHI::RHIResourceView* objectIdDepthView = nullptr;
RHI::RHIResourceView* objectIdView = nullptr; RHI::RHIResourceView* objectIdView = nullptr;
RHI::RHIResourceView* objectIdShaderView = nullptr; RHI::RHIResourceView* objectIdShaderView = nullptr;
RHI::RHITexture* selectionMaskTexture = nullptr;
RHI::RHIResourceView* selectionMaskView = nullptr;
RHI::RHIResourceView* selectionMaskShaderView = nullptr;
D3D12_CPU_DESCRIPTOR_HANDLE imguiCpuHandle = {}; D3D12_CPU_DESCRIPTOR_HANDLE imguiCpuHandle = {};
D3D12_GPU_DESCRIPTOR_HANDLE imguiGpuHandle = {}; D3D12_GPU_DESCRIPTOR_HANDLE imguiGpuHandle = {};
::XCEngine::UI::UITextureHandle textureHandle = {}; ::XCEngine::UI::UITextureHandle textureHandle = {};
RHI::ResourceStates colorState = RHI::ResourceStates::Common; RHI::ResourceStates colorState = RHI::ResourceStates::Common;
RHI::ResourceStates objectIdState = RHI::ResourceStates::Common; RHI::ResourceStates objectIdState = RHI::ResourceStates::Common;
RHI::ResourceStates selectionMaskState = RHI::ResourceStates::Common;
bool hasValidObjectIdFrame = false; bool hasValidObjectIdFrame = false;
}; };
@@ -44,9 +51,15 @@ inline ViewportHostResourceReuseQuery BuildViewportRenderTargetsReuseQuery(
query.resources.hasColorView = targets.colorView != nullptr; query.resources.hasColorView = targets.colorView != nullptr;
query.resources.hasDepthTexture = targets.depthTexture != nullptr; query.resources.hasDepthTexture = targets.depthTexture != nullptr;
query.resources.hasDepthView = targets.depthView != nullptr; query.resources.hasDepthView = targets.depthView != nullptr;
query.resources.hasDepthShaderView = targets.depthShaderView != nullptr;
query.resources.hasObjectIdTexture = targets.objectIdTexture != nullptr; query.resources.hasObjectIdTexture = targets.objectIdTexture != nullptr;
query.resources.hasObjectIdDepthTexture = targets.objectIdDepthTexture != nullptr;
query.resources.hasObjectIdDepthView = targets.objectIdDepthView != nullptr;
query.resources.hasObjectIdView = targets.objectIdView != nullptr; query.resources.hasObjectIdView = targets.objectIdView != nullptr;
query.resources.hasObjectIdShaderView = targets.objectIdShaderView != nullptr; query.resources.hasObjectIdShaderView = targets.objectIdShaderView != nullptr;
query.resources.hasSelectionMaskTexture = targets.selectionMaskTexture != nullptr;
query.resources.hasSelectionMaskView = targets.selectionMaskView != nullptr;
query.resources.hasSelectionMaskShaderView = targets.selectionMaskShaderView != nullptr;
query.resources.hasTextureDescriptor = targets.textureHandle.IsValid(); query.resources.hasTextureDescriptor = targets.textureHandle.IsValid();
return query; return query;
} }
@@ -65,10 +78,19 @@ inline Rendering::RenderSurface BuildViewportObjectIdSurface(const ViewportRende
targets.width, targets.width,
targets.height, targets.height,
targets.objectIdView, targets.objectIdView,
targets.depthView, targets.objectIdDepthView,
targets.objectIdState); targets.objectIdState);
} }
inline Rendering::RenderSurface BuildViewportSelectionMaskSurface(const ViewportRenderTargets& targets) {
return BuildViewportRenderSurface(
targets.width,
targets.height,
targets.selectionMaskView,
targets.depthView,
targets.selectionMaskState);
}
namespace Detail { namespace Detail {
template <typename ResourceType> template <typename ResourceType>
@@ -111,7 +133,17 @@ inline bool CreateViewportDepthResources(
const RHI::ResourceViewDesc depthViewDesc = const RHI::ResourceViewDesc depthViewDesc =
BuildViewportTextureViewDesc(RHI::Format::D24_UNorm_S8_UInt); BuildViewportTextureViewDesc(RHI::Format::D24_UNorm_S8_UInt);
targets.depthView = device->CreateDepthStencilView(targets.depthTexture, depthViewDesc); targets.depthView = device->CreateDepthStencilView(targets.depthTexture, depthViewDesc);
return targets.depthView != nullptr; if (targets.depthView == nullptr) {
return false;
}
RHI::ResourceViewDesc depthShaderViewDesc = {};
depthShaderViewDesc.dimension = RHI::ResourceViewDimension::Texture2D;
depthShaderViewDesc.mipLevel = 0;
targets.depthShaderView = device->CreateShaderResourceView(
targets.depthTexture,
depthShaderViewDesc);
return targets.depthShaderView != nullptr;
} }
inline bool CreateViewportObjectIdResources( inline bool CreateViewportObjectIdResources(
@@ -136,7 +168,48 @@ inline bool CreateViewportObjectIdResources(
targets.objectIdShaderView = device->CreateShaderResourceView( targets.objectIdShaderView = device->CreateShaderResourceView(
targets.objectIdTexture, targets.objectIdTexture,
objectIdViewDesc); objectIdViewDesc);
return targets.objectIdShaderView != nullptr; if (targets.objectIdShaderView == nullptr) {
return false;
}
const RHI::TextureDesc objectIdDepthDesc =
BuildViewportTextureDesc(targets.width, targets.height, RHI::Format::D24_UNorm_S8_UInt);
targets.objectIdDepthTexture = device->CreateTexture(objectIdDepthDesc);
if (targets.objectIdDepthTexture == nullptr) {
return false;
}
const RHI::ResourceViewDesc objectIdDepthViewDesc =
BuildViewportTextureViewDesc(RHI::Format::D24_UNorm_S8_UInt);
targets.objectIdDepthView = device->CreateDepthStencilView(
targets.objectIdDepthTexture,
objectIdDepthViewDesc);
return targets.objectIdDepthView != nullptr;
}
inline bool CreateViewportSelectionMaskResources(
RHI::RHIDevice* device,
ViewportRenderTargets& targets) {
const RHI::TextureDesc selectionMaskDesc =
BuildViewportTextureDesc(targets.width, targets.height, RHI::Format::R8G8B8A8_UNorm);
targets.selectionMaskTexture = device->CreateTexture(selectionMaskDesc);
if (targets.selectionMaskTexture == nullptr) {
return false;
}
const RHI::ResourceViewDesc selectionMaskViewDesc =
BuildViewportTextureViewDesc(RHI::Format::R8G8B8A8_UNorm);
targets.selectionMaskView = device->CreateRenderTargetView(
targets.selectionMaskTexture,
selectionMaskViewDesc);
if (targets.selectionMaskView == nullptr) {
return false;
}
targets.selectionMaskShaderView = device->CreateShaderResourceView(
targets.selectionMaskTexture,
selectionMaskViewDesc);
return targets.selectionMaskShaderView != nullptr;
} }
inline bool CreateViewportTextureDescriptor( inline bool CreateViewportTextureDescriptor(
@@ -170,7 +243,13 @@ inline void DestroyViewportRenderTargets(
Detail::ShutdownAndDelete(targets.objectIdView); Detail::ShutdownAndDelete(targets.objectIdView);
Detail::ShutdownAndDelete(targets.objectIdShaderView); Detail::ShutdownAndDelete(targets.objectIdShaderView);
Detail::ShutdownAndDelete(targets.objectIdDepthView);
Detail::ShutdownAndDelete(targets.objectIdDepthTexture);
Detail::ShutdownAndDelete(targets.objectIdTexture); Detail::ShutdownAndDelete(targets.objectIdTexture);
Detail::ShutdownAndDelete(targets.selectionMaskView);
Detail::ShutdownAndDelete(targets.selectionMaskShaderView);
Detail::ShutdownAndDelete(targets.selectionMaskTexture);
Detail::ShutdownAndDelete(targets.depthShaderView);
Detail::ShutdownAndDelete(targets.depthView); Detail::ShutdownAndDelete(targets.depthView);
Detail::ShutdownAndDelete(targets.depthTexture); Detail::ShutdownAndDelete(targets.depthTexture);
Detail::ShutdownAndDelete(targets.colorView); Detail::ShutdownAndDelete(targets.colorView);
@@ -183,6 +262,7 @@ inline void DestroyViewportRenderTargets(
targets.textureHandle = {}; targets.textureHandle = {};
targets.colorState = RHI::ResourceStates::Common; targets.colorState = RHI::ResourceStates::Common;
targets.objectIdState = RHI::ResourceStates::Common; targets.objectIdState = RHI::ResourceStates::Common;
targets.selectionMaskState = RHI::ResourceStates::Common;
targets.hasValidObjectIdFrame = false; targets.hasValidObjectIdFrame = false;
} }
@@ -204,7 +284,8 @@ inline bool CreateViewportRenderTargets(
if (!Detail::CreateViewportColorResources(device, targets) || if (!Detail::CreateViewportColorResources(device, targets) ||
!Detail::CreateViewportDepthResources(device, targets) || !Detail::CreateViewportDepthResources(device, targets) ||
(ViewportRequiresObjectIdResources(kind) && (ViewportRequiresObjectIdResources(kind) &&
!Detail::CreateViewportObjectIdResources(device, targets)) || (!Detail::CreateViewportObjectIdResources(device, targets) ||
!Detail::CreateViewportSelectionMaskResources(device, targets))) ||
!Detail::CreateViewportTextureDescriptor(backend, device, targets)) { !Detail::CreateViewportTextureDescriptor(backend, device, targets)) {
DestroyViewportRenderTargets(backend, targets); DestroyViewportRenderTargets(backend, targets);
return false; return false;
@@ -212,6 +293,7 @@ inline bool CreateViewportRenderTargets(
targets.colorState = RHI::ResourceStates::Common; targets.colorState = RHI::ResourceStates::Common;
targets.objectIdState = RHI::ResourceStates::Common; targets.objectIdState = RHI::ResourceStates::Common;
targets.selectionMaskState = RHI::ResourceStates::Common;
targets.hasValidObjectIdFrame = false; targets.hasValidObjectIdFrame = false;
return true; return true;
} }

View File

@@ -19,9 +19,15 @@ struct ViewportHostResourcePresence {
bool hasColorView = false; bool hasColorView = false;
bool hasDepthTexture = false; bool hasDepthTexture = false;
bool hasDepthView = false; bool hasDepthView = false;
bool hasDepthShaderView = false;
bool hasObjectIdTexture = false; bool hasObjectIdTexture = false;
bool hasObjectIdDepthTexture = false;
bool hasObjectIdDepthView = false;
bool hasObjectIdView = false; bool hasObjectIdView = false;
bool hasObjectIdShaderView = false; bool hasObjectIdShaderView = false;
bool hasSelectionMaskTexture = false;
bool hasSelectionMaskView = false;
bool hasSelectionMaskShaderView = false;
bool hasTextureDescriptor = false; bool hasTextureDescriptor = false;
}; };
@@ -70,8 +76,14 @@ inline bool CanReuseViewportResources(const ViewportHostResourceReuseQuery& quer
} }
return query.resources.hasObjectIdTexture && return query.resources.hasObjectIdTexture &&
query.resources.hasObjectIdDepthTexture &&
query.resources.hasObjectIdDepthView &&
query.resources.hasObjectIdView && query.resources.hasObjectIdView &&
query.resources.hasObjectIdShaderView; query.resources.hasObjectIdShaderView &&
query.resources.hasDepthShaderView &&
query.resources.hasSelectionMaskTexture &&
query.resources.hasSelectionMaskView &&
query.resources.hasSelectionMaskShaderView;
} }
inline RHI::TextureDesc BuildViewportTextureDesc( inline RHI::TextureDesc BuildViewportTextureDesc(

View File

@@ -484,10 +484,12 @@ add_library(XCEngine STATIC
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinDepthOnlyPass.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinDepthOnlyPass.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinShadowCasterPass.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinShadowCasterPass.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinObjectIdPass.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinObjectIdPass.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinSelectionMaskPass.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinColorScalePostProcessPass.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinColorScalePostProcessPass.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinFinalColorPass.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinFinalColorPass.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinObjectIdOutlinePass.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinSelectionOutlinePass.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinVolumetricPass.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinVolumetricPass.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Execution/CameraRenderer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Execution/CameraRenderer.cpp
@@ -499,10 +501,12 @@ add_library(XCEngine STATIC
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinShadowCasterPass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinShadowCasterPass.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinObjectIdPass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinObjectIdPass.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinObjectIdPassResources.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinObjectIdPassResources.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinSelectionMaskPass.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinColorScalePostProcessPass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinColorScalePostProcessPass.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinFinalColorPass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinFinalColorPass.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinInfiniteGridPass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinInfiniteGridPass.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinObjectIdOutlinePass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinObjectIdOutlinePass.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinSelectionOutlinePass.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinVolumetricPass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinVolumetricPass.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderSurface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderSurface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Extraction/RenderSceneExtractor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Extraction/RenderSceneExtractor.cpp

View File

@@ -0,0 +1,78 @@
Shader "Builtin Selection Mask"
{
Properties
{
_BaseColor ("Base Color", Color) = (1,1,1,1) [Semantic(BaseColor)]
_Cutoff ("Alpha Cutoff", Range) = 0.5 [Semantic(AlphaCutoff)]
_MainTex ("Base Map", 2D) = "white" [Semantic(BaseColorTexture)]
}
HLSLINCLUDE
// XC_BUILTIN_SELECTION_MASK_D3D12_SHARED
cbuffer PerObjectConstants
{
float4x4 gProjectionMatrix;
float4x4 gViewMatrix;
float4x4 gModelMatrix;
};
cbuffer MaterialConstants
{
float4 gBaseColorFactor;
float4 gAlphaCutoffParams;
};
Texture2D BaseColorTexture;
SamplerState LinearClampSampler;
struct VSInput
{
float3 position : POSITION;
float3 normal : NORMAL;
float2 texcoord : TEXCOORD0;
};
struct PSInput
{
float4 position : SV_POSITION;
float2 texcoord : TEXCOORD0;
};
PSInput MainVS(VSInput input)
{
PSInput output;
const float4 positionWS = mul(gModelMatrix, float4(input.position, 1.0f));
const float4 positionVS = mul(gViewMatrix, positionWS);
output.position = mul(gProjectionMatrix, positionVS);
output.texcoord = input.texcoord;
return output;
}
float4 MainPS(PSInput input) : SV_TARGET
{
// XC_BUILTIN_SELECTION_MASK_D3D12_PS
#ifdef XC_ALPHA_TEST
const float4 baseColor =
BaseColorTexture.Sample(LinearClampSampler, input.texcoord) * gBaseColorFactor;
clip(baseColor.a - gAlphaCutoffParams.x);
#endif
return float4(1.0, 1.0, 1.0, 1.0);
}
ENDHLSL
SubShader
{
Pass
{
Name "SelectionMask"
Tags { "LightMode" = "SelectionMask" }
Cull Back
ZWrite Off
ZTest LEqual
HLSLPROGRAM
#pragma target 4.5
#pragma vertex MainVS
#pragma fragment MainPS
#pragma shader_feature_local _ XC_ALPHA_TEST
ENDHLSL
}
}
}

View File

@@ -0,0 +1,131 @@
Shader "Builtin Selection Outline"
{
HLSLINCLUDE
// XC_BUILTIN_SELECTION_OUTLINE_D3D12_SHARED
cbuffer OutlineConstants
{
float4 gViewportSizeAndTexelSize;
float4 gOutlineColor;
float4 gOutlineInfo;
float4 gDepthParams;
};
Texture2D gSelectionMaskTexture;
Texture2D gDepthTexture;
struct VSOutput
{
float4 position : SV_POSITION;
};
VSOutput MainVS(uint vertexId : SV_VertexID)
{
// XC_BUILTIN_SELECTION_OUTLINE_D3D12_VS
static const float2 positions[3] = {
float2(-1.0, -1.0),
float2(-1.0, 3.0),
float2( 3.0, -1.0)
};
VSOutput output;
output.position = float4(positions[vertexId], 0.0, 1.0);
return output;
}
int2 ClampPixelCoord(int2 pixelCoord)
{
const int2 maxCoord = int2(
max((int)gViewportSizeAndTexelSize.x - 1, 0),
max((int)gViewportSizeAndTexelSize.y - 1, 0));
return clamp(pixelCoord, int2(0, 0), maxCoord);
}
float LoadSelectionMask(int2 pixelCoord)
{
return gSelectionMaskTexture.Load(int3(ClampPixelCoord(pixelCoord), 0)).r;
}
float LoadDepth(int2 pixelCoord)
{
return gDepthTexture.Load(int3(ClampPixelCoord(pixelCoord), 0)).r;
}
bool IsSelectedPixel(float maskValue)
{
return maskValue > 0.5;
}
float4 MainPS(VSOutput input) : SV_TARGET
{
// XC_BUILTIN_SELECTION_OUTLINE_D3D12_PS
const int2 pixelCoord = int2(input.position.xy);
const bool debugSelectionMask = gOutlineInfo.x > 0.5;
const bool centerSelected = IsSelectedPixel(LoadSelectionMask(pixelCoord));
if (debugSelectionMask) {
return centerSelected ? float4(1.0, 1.0, 1.0, 1.0) : float4(0.0, 0.0, 0.0, 1.0);
}
if (centerSelected) {
discard;
}
const float centerDepth = LoadDepth(pixelCoord);
const int outlineWidth = max((int)gOutlineInfo.y, 1);
const float depthThreshold = max(gDepthParams.x, 1.0e-6f);
float outline = 0.0;
[loop]
for (int y = -2; y <= 2; ++y) {
[loop]
for (int x = -2; x <= 2; ++x) {
if (x == 0 && y == 0) {
continue;
}
const float distancePixels = length(float2((float)x, (float)y));
if (distancePixels > outlineWidth) {
continue;
}
const int2 sampleCoord = pixelCoord + int2(x, y);
if (!IsSelectedPixel(LoadSelectionMask(sampleCoord))) {
continue;
}
const float selectedDepth = LoadDepth(sampleCoord);
if (!(centerDepth > selectedDepth + depthThreshold)) {
continue;
}
const float weight = saturate(
1.0 - ((distancePixels - 1.0) / max((float)outlineWidth, 1.0)));
outline = max(outline, weight);
}
}
if (outline <= 0.001) {
discard;
}
return float4(gOutlineColor.rgb, gOutlineColor.a * outline);
}
ENDHLSL
SubShader
{
Pass
{
Name "SelectionOutline"
Tags { "LightMode" = "SelectionOutline" }
Cull Off
ZWrite Off
ZTest Always
Blend SrcAlpha OneMinusSrcAlpha, One OneMinusSrcAlpha
HLSLPROGRAM
#pragma target 4.5
#pragma vertex MainVS
#pragma fragment MainPS
ENDHLSL
}
}
}

View File

@@ -226,7 +226,8 @@ inline bool TryBuildBuiltinPassDefaultResourceBindings(
if (ShaderPassMatchesBuiltinPass(shaderPass, BuiltinMaterialPass::Unlit) || if (ShaderPassMatchesBuiltinPass(shaderPass, BuiltinMaterialPass::Unlit) ||
ShaderPassMatchesBuiltinPass(shaderPass, BuiltinMaterialPass::DepthOnly) || ShaderPassMatchesBuiltinPass(shaderPass, BuiltinMaterialPass::DepthOnly) ||
ShaderPassMatchesBuiltinPass(shaderPass, BuiltinMaterialPass::ShadowCaster)) { ShaderPassMatchesBuiltinPass(shaderPass, BuiltinMaterialPass::ShadowCaster) ||
ShaderPassMatchesBuiltinPass(shaderPass, BuiltinMaterialPass::SelectionMask)) {
AppendBuiltinPassResourceBinding( AppendBuiltinPassResourceBinding(
outBindings, outBindings,
"PerObjectConstants", "PerObjectConstants",
@@ -318,10 +319,17 @@ inline bool TryBuildBuiltinPassDefaultResourceBindings(
"PerObject"); "PerObject");
AppendBuiltinPassResourceBinding( AppendBuiltinPassResourceBinding(
outBindings, outBindings,
"MaterialConstants", "LightingConstants",
Resources::ShaderResourceType::ConstantBuffer, Resources::ShaderResourceType::ConstantBuffer,
1u, 1u,
0u, 0u,
"Lighting");
AppendBuiltinPassResourceBinding(
outBindings,
"MaterialConstants",
Resources::ShaderResourceType::ConstantBuffer,
2u,
0u,
"Material"); "Material");
return true; return true;
} }

View File

@@ -23,6 +23,8 @@ inline const char* GetBuiltinPassCanonicalName(BuiltinMaterialPass pass) {
return "shadowcaster"; return "shadowcaster";
case BuiltinMaterialPass::ObjectId: case BuiltinMaterialPass::ObjectId:
return "objectid"; return "objectid";
case BuiltinMaterialPass::SelectionMask:
return "selectionmask";
case BuiltinMaterialPass::Skybox: case BuiltinMaterialPass::Skybox:
return "skybox"; return "skybox";
case BuiltinMaterialPass::Volumetric: case BuiltinMaterialPass::Volumetric:

View File

@@ -18,6 +18,7 @@ enum class BuiltinMaterialPass : Core::uint32 {
DepthOnly, DepthOnly,
ShadowCaster, ShadowCaster,
ObjectId, ObjectId,
SelectionMask,
Skybox, Skybox,
Volumetric, Volumetric,
PostProcess, PostProcess,

View File

@@ -0,0 +1,40 @@
#pragma once
#include <XCEngine/Rendering/Passes/BuiltinDepthStylePassBase.h>
#include <cstdint>
#include <vector>
namespace XCEngine {
namespace Rendering {
struct RenderSceneData;
struct VisibleRenderItem;
namespace Passes {
class BuiltinSelectionMaskPass final : public BuiltinDepthStylePassBase {
public:
BuiltinSelectionMaskPass();
~BuiltinSelectionMaskPass() override = default;
static RHI::InputLayoutDesc BuildInputLayout();
const char* GetName() const override;
bool Render(
const RenderContext& context,
const RenderSurface& surface,
const RenderSceneData& sceneData,
const std::vector<uint64_t>& selectedObjectIds);
protected:
bool ShouldRenderVisibleItem(const VisibleRenderItem& visibleItem) const override;
private:
std::vector<uint64_t> m_selectedObjectIds = {};
};
} // namespace Passes
} // namespace Rendering
} // namespace XCEngine

View File

@@ -0,0 +1,79 @@
#pragma once
#include <XCEngine/Core/Asset/ResourceHandle.h>
#include <XCEngine/Core/Containers/String.h>
#include <XCEngine/Core/Math/Color.h>
#include <XCEngine/Core/Math/Vector4.h>
#include <XCEngine/Rendering/RenderContext.h>
#include <XCEngine/Rendering/RenderSurface.h>
#include <XCEngine/RHI/RHIDescriptorPool.h>
#include <XCEngine/RHI/RHIDescriptorSet.h>
#include <XCEngine/RHI/RHIEnums.h>
#include <XCEngine/RHI/RHIPipelineLayout.h>
#include <XCEngine/RHI/RHIPipelineState.h>
#include <XCEngine/RHI/RHIResourceView.h>
#include <XCEngine/Resources/Shader/Shader.h>
#include <optional>
namespace XCEngine {
namespace Rendering {
namespace Passes {
struct SelectionOutlineStyle {
Math::Color outlineColor = Math::Color(1.0f, 0.4f, 0.0f, 1.0f);
float outlineWidthPixels = 2.0f;
bool debugSelectionMask = false;
};
class BuiltinSelectionOutlinePass {
public:
explicit BuiltinSelectionOutlinePass(Containers::String shaderPath = Containers::String());
~BuiltinSelectionOutlinePass() = default;
BuiltinSelectionOutlinePass(const BuiltinSelectionOutlinePass&) = delete;
BuiltinSelectionOutlinePass& operator=(const BuiltinSelectionOutlinePass&) = delete;
BuiltinSelectionOutlinePass(BuiltinSelectionOutlinePass&&) = delete;
BuiltinSelectionOutlinePass& operator=(BuiltinSelectionOutlinePass&&) = delete;
void SetShaderPath(const Containers::String& shaderPath);
const Containers::String& GetShaderPath() const;
void Shutdown();
bool Render(
const RenderContext& renderContext,
const RenderSurface& surface,
RHI::RHIResourceView* selectionMaskTextureView,
RHI::RHIResourceView* depthTextureView,
const SelectionOutlineStyle& style = {});
private:
struct OutlineConstants {
Math::Vector4 viewportSizeAndTexelSize = Math::Vector4::Zero();
Math::Vector4 outlineColor = Math::Vector4::Zero();
Math::Vector4 outlineInfo = Math::Vector4::Zero();
Math::Vector4 depthParams = Math::Vector4::Zero();
};
bool EnsureInitialized(const RenderContext& renderContext);
bool CreateResources(const RenderContext& renderContext);
void DestroyResources();
bool HasCreatedResources() const;
void ResetState();
RHI::RHIDevice* m_device = nullptr;
RHI::RHIType m_backendType = RHI::RHIType::D3D12;
RHI::RHIPipelineLayout* m_pipelineLayout = nullptr;
RHI::RHIPipelineState* m_pipelineState = nullptr;
RHI::RHIDescriptorPool* m_constantPool = nullptr;
RHI::RHIDescriptorSet* m_constantSet = nullptr;
RHI::RHIDescriptorPool* m_texturePool = nullptr;
RHI::RHIDescriptorSet* m_textureSet = nullptr;
Containers::String m_shaderPath;
std::optional<Resources::ResourceHandle<Resources::Shader>> m_builtinSelectionOutlineShader;
};
} // namespace Passes
} // namespace Rendering
} // namespace XCEngine

View File

@@ -35,6 +35,8 @@ Containers::String GetBuiltinDepthOnlyShaderPath();
Containers::String GetBuiltinShadowCasterShaderPath(); Containers::String GetBuiltinShadowCasterShaderPath();
Containers::String GetBuiltinObjectIdShaderPath(); Containers::String GetBuiltinObjectIdShaderPath();
Containers::String GetBuiltinObjectIdOutlineShaderPath(); Containers::String GetBuiltinObjectIdOutlineShaderPath();
Containers::String GetBuiltinSelectionMaskShaderPath();
Containers::String GetBuiltinSelectionOutlineShaderPath();
Containers::String GetBuiltinSkyboxShaderPath(); Containers::String GetBuiltinSkyboxShaderPath();
Containers::String GetBuiltinVolumetricShaderPath(); Containers::String GetBuiltinVolumetricShaderPath();
Containers::String GetBuiltinColorScalePostProcessShaderPath(); Containers::String GetBuiltinColorScalePostProcessShaderPath();

View File

@@ -360,7 +360,9 @@ BuiltinDepthStylePassBase::ResolvedShaderPass BuiltinDepthStylePassBase::Resolve
return resolved; return resolved;
} }
if (material != nullptr && IsTransparentRenderQueue(ResolveMaterialRenderQueue(material))) { if (material != nullptr &&
IsTransparentRenderQueue(ResolveMaterialRenderQueue(material)) &&
m_passType != BuiltinMaterialPass::SelectionMask) {
return {}; return {};
} }

View File

@@ -0,0 +1,65 @@
#include "Rendering/Passes/BuiltinSelectionMaskPass.h"
#include "Components/GameObject.h"
#include "Resources/BuiltinResources.h"
#include <XCEngine/Rendering/FrameData/RenderSceneData.h>
#include <XCEngine/Rendering/FrameData/VisibleRenderItem.h>
#include <algorithm>
namespace XCEngine {
namespace Rendering {
namespace Passes {
BuiltinSelectionMaskPass::BuiltinSelectionMaskPass()
: BuiltinDepthStylePassBase(
BuiltinMaterialPass::SelectionMask,
Resources::GetBuiltinSelectionMaskShaderPath()) {
}
RHI::InputLayoutDesc BuiltinSelectionMaskPass::BuildInputLayout() {
return BuildCommonInputLayout();
}
const char* BuiltinSelectionMaskPass::GetName() const {
return "BuiltinSelectionMaskPass";
}
bool BuiltinSelectionMaskPass::Render(
const RenderContext& context,
const RenderSurface& surface,
const RenderSceneData& sceneData,
const std::vector<uint64_t>& selectedObjectIds) {
m_selectedObjectIds = selectedObjectIds;
if (m_selectedObjectIds.empty()) {
return false;
}
RenderSceneData selectionMaskSceneData = sceneData;
selectionMaskSceneData.cameraData.clearFlags = RenderClearFlags::Color;
selectionMaskSceneData.cameraData.clearColor = Math::Color::Black();
const RenderPassContext passContext = {
context,
surface,
selectionMaskSceneData,
nullptr,
nullptr
};
return Execute(passContext);
}
bool BuiltinSelectionMaskPass::ShouldRenderVisibleItem(const VisibleRenderItem& visibleItem) const {
if (visibleItem.gameObject == nullptr) {
return false;
}
const uint64_t objectId = visibleItem.gameObject->GetID();
return std::find(m_selectedObjectIds.begin(), m_selectedObjectIds.end(), objectId) !=
m_selectedObjectIds.end();
}
} // namespace Passes
} // namespace Rendering
} // namespace XCEngine

View File

@@ -0,0 +1,429 @@
#include "Rendering/Passes/BuiltinSelectionOutlinePass.h"
#include "Core/Asset/ResourceManager.h"
#include "Debug/Logger.h"
#include "Rendering/Detail/ShaderVariantUtils.h"
#include "Rendering/Materials/RenderMaterialStateUtils.h"
#include "RHI/RHICommandList.h"
#include "RHI/RHIDevice.h"
#include <XCEngine/Resources/BuiltinResources.h>
#include <utility>
namespace XCEngine {
namespace Rendering {
namespace Passes {
namespace {
const Resources::ShaderPass* FindSelectionOutlineCompatiblePass(
const Resources::Shader& shader,
Resources::ShaderBackend backend) {
const Resources::ShaderPass* outlinePass = shader.FindPass("SelectionOutline");
if (outlinePass != nullptr &&
::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(shader, outlinePass->name, backend)) {
return outlinePass;
}
const Resources::ShaderPass* editorOutlinePass = shader.FindPass("EditorSelectionOutline");
if (editorOutlinePass != nullptr &&
::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(
shader,
editorOutlinePass->name,
backend)) {
return editorOutlinePass;
}
if (shader.GetPassCount() > 0 &&
::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(
shader,
shader.GetPasses()[0].name,
backend)) {
return &shader.GetPasses()[0];
}
return nullptr;
}
RHI::GraphicsPipelineDesc CreatePipelineDesc(
RHI::RHIType backendType,
RHI::RHIPipelineLayout* pipelineLayout,
const Resources::Shader& shader,
const Containers::String& passName) {
RHI::GraphicsPipelineDesc pipelineDesc = {};
pipelineDesc.pipelineLayout = pipelineLayout;
pipelineDesc.topologyType = static_cast<uint32_t>(RHI::PrimitiveTopologyType::Triangle);
pipelineDesc.renderTargetCount = 1;
pipelineDesc.renderTargetFormats[0] = static_cast<uint32_t>(RHI::Format::R8G8B8A8_UNorm);
pipelineDesc.depthStencilFormat = static_cast<uint32_t>(RHI::Format::Unknown);
pipelineDesc.sampleCount = 1;
const Resources::ShaderPass* shaderPass = shader.FindPass(passName);
if (shaderPass != nullptr && shaderPass->hasFixedFunctionState) {
::XCEngine::Rendering::ApplyRenderState(shaderPass->fixedFunctionState, pipelineDesc);
} else {
Resources::MaterialRenderState fallbackState = {};
fallbackState.cullMode = Resources::MaterialCullMode::None;
fallbackState.depthWriteEnable = false;
fallbackState.depthTestEnable = false;
fallbackState.depthFunc = Resources::MaterialComparisonFunc::Always;
fallbackState.blendEnable = true;
fallbackState.srcBlend = Resources::MaterialBlendFactor::SrcAlpha;
fallbackState.dstBlend = Resources::MaterialBlendFactor::InvSrcAlpha;
fallbackState.srcBlendAlpha = Resources::MaterialBlendFactor::One;
fallbackState.dstBlendAlpha = Resources::MaterialBlendFactor::InvSrcAlpha;
fallbackState.blendOp = Resources::MaterialBlendOp::Add;
fallbackState.blendOpAlpha = Resources::MaterialBlendOp::Add;
fallbackState.colorWriteMask = static_cast<uint8_t>(RHI::ColorWriteMask::All);
::XCEngine::Rendering::ApplyRenderState(fallbackState, pipelineDesc);
}
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(backendType);
if (const Resources::ShaderStageVariant* vertexVariant =
shader.FindVariant(passName, Resources::ShaderType::Vertex, backend)) {
if (shaderPass != nullptr) {
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
shader.GetPath(),
*shaderPass,
backend,
*vertexVariant,
pipelineDesc.vertexShader);
}
}
if (const Resources::ShaderStageVariant* fragmentVariant =
shader.FindVariant(passName, Resources::ShaderType::Fragment, backend)) {
if (shaderPass != nullptr) {
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
shader.GetPath(),
*shaderPass,
backend,
*fragmentVariant,
pipelineDesc.fragmentShader);
}
}
return pipelineDesc;
}
} // namespace
BuiltinSelectionOutlinePass::BuiltinSelectionOutlinePass(Containers::String shaderPath)
: m_shaderPath(shaderPath.Empty() ? Resources::GetBuiltinSelectionOutlineShaderPath() : std::move(shaderPath)) {
ResetState();
}
void BuiltinSelectionOutlinePass::SetShaderPath(const Containers::String& shaderPath) {
if (m_shaderPath == shaderPath) {
return;
}
DestroyResources();
m_shaderPath = shaderPath;
}
const Containers::String& BuiltinSelectionOutlinePass::GetShaderPath() const {
return m_shaderPath;
}
void BuiltinSelectionOutlinePass::Shutdown() {
DestroyResources();
}
bool BuiltinSelectionOutlinePass::Render(
const RenderContext& renderContext,
const RenderSurface& surface,
RHI::RHIResourceView* selectionMaskTextureView,
RHI::RHIResourceView* depthTextureView,
const SelectionOutlineStyle& style) {
if (!renderContext.IsValid() ||
renderContext.backendType != RHI::RHIType::D3D12 ||
selectionMaskTextureView == nullptr ||
depthTextureView == nullptr) {
return false;
}
if (!EnsureInitialized(renderContext)) {
return false;
}
const std::vector<RHI::RHIResourceView*>& colorAttachments = surface.GetColorAttachments();
if (colorAttachments.empty() || colorAttachments[0] == nullptr) {
return false;
}
OutlineConstants constants = {};
constants.viewportSizeAndTexelSize = Math::Vector4(
static_cast<float>(surface.GetWidth()),
static_cast<float>(surface.GetHeight()),
surface.GetWidth() > 0 ? 1.0f / static_cast<float>(surface.GetWidth()) : 0.0f,
surface.GetHeight() > 0 ? 1.0f / static_cast<float>(surface.GetHeight()) : 0.0f);
constants.outlineColor = Math::Vector4(
style.outlineColor.r,
style.outlineColor.g,
style.outlineColor.b,
style.outlineColor.a);
constants.outlineInfo = Math::Vector4(
style.debugSelectionMask ? 1.0f : 0.0f,
style.outlineWidthPixels,
0.0f,
0.0f);
constants.depthParams = Math::Vector4(1.0e-5f, 0.0f, 0.0f, 0.0f);
m_constantSet->WriteConstant(0, &constants, sizeof(constants));
m_textureSet->Update(0, selectionMaskTextureView);
m_textureSet->Update(1, depthTextureView);
RHI::RHICommandList* commandList = renderContext.commandList;
RHI::RHIResourceView* renderTarget = colorAttachments[0];
commandList->TransitionBarrier(
renderTarget,
surface.GetColorStateAfter(),
RHI::ResourceStates::RenderTarget);
commandList->TransitionBarrier(
depthTextureView,
RHI::ResourceStates::DepthWrite,
RHI::ResourceStates::PixelShaderResource);
commandList->SetRenderTargets(1, &renderTarget, nullptr);
const Math::RectInt renderArea = surface.GetRenderArea();
const RHI::Viewport viewport = {
static_cast<float>(renderArea.x),
static_cast<float>(renderArea.y),
static_cast<float>(renderArea.width),
static_cast<float>(renderArea.height),
0.0f,
1.0f
};
const RHI::Rect scissorRect = {
renderArea.x,
renderArea.y,
renderArea.x + renderArea.width,
renderArea.y + renderArea.height
};
commandList->SetViewport(viewport);
commandList->SetScissorRect(scissorRect);
commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList);
commandList->SetPipelineState(m_pipelineState);
RHI::RHIDescriptorSet* descriptorSets[] = { m_constantSet, m_textureSet };
commandList->SetGraphicsDescriptorSets(0, 2, descriptorSets, m_pipelineLayout);
commandList->Draw(3, 1, 0, 0);
commandList->TransitionBarrier(
renderTarget,
RHI::ResourceStates::RenderTarget,
surface.GetColorStateAfter());
commandList->TransitionBarrier(
depthTextureView,
RHI::ResourceStates::PixelShaderResource,
RHI::ResourceStates::DepthWrite);
return true;
}
bool BuiltinSelectionOutlinePass::EnsureInitialized(const RenderContext& renderContext) {
if (m_pipelineLayout != nullptr &&
m_pipelineState != nullptr &&
m_constantPool != nullptr &&
m_constantSet != nullptr &&
m_texturePool != nullptr &&
m_textureSet != nullptr &&
m_device == renderContext.device &&
m_backendType == renderContext.backendType) {
return true;
}
if (HasCreatedResources()) {
DestroyResources();
}
return CreateResources(renderContext);
}
bool BuiltinSelectionOutlinePass::CreateResources(const RenderContext& renderContext) {
if (!renderContext.IsValid() || renderContext.backendType != RHI::RHIType::D3D12) {
return false;
}
if (m_shaderPath.Empty()) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"BuiltinSelectionOutlinePass requires an injected shader path before resource creation");
return false;
}
Resources::ResourceHandle<Resources::Shader> shader = Resources::ResourceManager::Get().Load<Resources::Shader>(
m_shaderPath);
if (!shader.IsValid()) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"BuiltinSelectionOutlinePass failed to load configured selection-outline shader resource");
ResetState();
return false;
}
m_device = renderContext.device;
m_backendType = renderContext.backendType;
m_builtinSelectionOutlineShader.emplace(std::move(shader));
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(m_backendType);
const Resources::ShaderPass* outlinePass =
FindSelectionOutlineCompatiblePass(*m_builtinSelectionOutlineShader->Get(), backend);
if (outlinePass == nullptr) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"BuiltinSelectionOutlinePass could not resolve a valid SelectionOutline shader pass");
DestroyResources();
return false;
}
RHI::DescriptorSetLayoutBinding constantBinding = {};
constantBinding.binding = 0;
constantBinding.type = static_cast<uint32_t>(RHI::DescriptorType::CBV);
constantBinding.count = 1;
RHI::DescriptorSetLayoutBinding textureBindings[2] = {};
textureBindings[0].binding = 0;
textureBindings[0].type = static_cast<uint32_t>(RHI::DescriptorType::SRV);
textureBindings[0].count = 1;
textureBindings[1].binding = 1;
textureBindings[1].type = static_cast<uint32_t>(RHI::DescriptorType::SRV);
textureBindings[1].count = 1;
RHI::DescriptorSetLayoutDesc constantLayout = {};
constantLayout.bindings = &constantBinding;
constantLayout.bindingCount = 1;
RHI::DescriptorSetLayoutDesc textureLayout = {};
textureLayout.bindings = textureBindings;
textureLayout.bindingCount = 2;
RHI::DescriptorSetLayoutDesc setLayouts[2] = {};
setLayouts[0] = constantLayout;
setLayouts[1] = textureLayout;
RHI::RHIPipelineLayoutDesc pipelineLayoutDesc = {};
pipelineLayoutDesc.setLayouts = setLayouts;
pipelineLayoutDesc.setLayoutCount = 2;
m_pipelineLayout = m_device->CreatePipelineLayout(pipelineLayoutDesc);
if (m_pipelineLayout == nullptr) {
DestroyResources();
return false;
}
RHI::DescriptorPoolDesc constantPoolDesc = {};
constantPoolDesc.type = RHI::DescriptorHeapType::CBV_SRV_UAV;
constantPoolDesc.descriptorCount = 1;
constantPoolDesc.shaderVisible = false;
m_constantPool = m_device->CreateDescriptorPool(constantPoolDesc);
if (m_constantPool == nullptr) {
DestroyResources();
return false;
}
m_constantSet = m_constantPool->AllocateSet(constantLayout);
if (m_constantSet == nullptr) {
DestroyResources();
return false;
}
RHI::DescriptorPoolDesc texturePoolDesc = {};
texturePoolDesc.type = RHI::DescriptorHeapType::CBV_SRV_UAV;
texturePoolDesc.descriptorCount = 2;
texturePoolDesc.shaderVisible = true;
m_texturePool = m_device->CreateDescriptorPool(texturePoolDesc);
if (m_texturePool == nullptr) {
DestroyResources();
return false;
}
m_textureSet = m_texturePool->AllocateSet(textureLayout);
if (m_textureSet == nullptr) {
DestroyResources();
return false;
}
m_pipelineState = m_device->CreatePipelineState(
CreatePipelineDesc(
m_backendType,
m_pipelineLayout,
*m_builtinSelectionOutlineShader->Get(),
outlinePass->name));
if (m_pipelineState == nullptr || !m_pipelineState->IsValid()) {
DestroyResources();
return false;
}
return true;
}
bool BuiltinSelectionOutlinePass::HasCreatedResources() const {
return m_device != nullptr ||
m_pipelineLayout != nullptr ||
m_pipelineState != nullptr ||
m_constantPool != nullptr ||
m_constantSet != nullptr ||
m_texturePool != nullptr ||
m_textureSet != nullptr ||
m_builtinSelectionOutlineShader.has_value();
}
void BuiltinSelectionOutlinePass::DestroyResources() {
if (m_pipelineState != nullptr) {
m_pipelineState->Shutdown();
delete m_pipelineState;
m_pipelineState = nullptr;
}
if (m_textureSet != nullptr) {
m_textureSet->Shutdown();
delete m_textureSet;
m_textureSet = nullptr;
}
if (m_texturePool != nullptr) {
m_texturePool->Shutdown();
delete m_texturePool;
m_texturePool = nullptr;
}
if (m_constantSet != nullptr) {
m_constantSet->Shutdown();
delete m_constantSet;
m_constantSet = nullptr;
}
if (m_constantPool != nullptr) {
m_constantPool->Shutdown();
delete m_constantPool;
m_constantPool = nullptr;
}
if (m_pipelineLayout != nullptr) {
m_pipelineLayout->Shutdown();
delete m_pipelineLayout;
m_pipelineLayout = nullptr;
}
if (m_builtinSelectionOutlineShader.has_value()) {
m_builtinSelectionOutlineShader.reset();
}
ResetState();
}
void BuiltinSelectionOutlinePass::ResetState() {
m_device = nullptr;
m_backendType = RHI::RHIType::D3D12;
m_pipelineLayout = nullptr;
m_pipelineState = nullptr;
m_constantPool = nullptr;
m_constantSet = nullptr;
m_texturePool = nullptr;
m_textureSet = nullptr;
m_builtinSelectionOutlineShader.reset();
}
} // namespace Passes
} // namespace Rendering
} // namespace XCEngine

View File

@@ -33,6 +33,8 @@ constexpr const char* kBuiltinDepthOnlyShaderPath = "builtin://shaders/depth-onl
constexpr const char* kBuiltinShadowCasterShaderPath = "builtin://shaders/shadow-caster"; constexpr const char* kBuiltinShadowCasterShaderPath = "builtin://shaders/shadow-caster";
constexpr const char* kBuiltinObjectIdShaderPath = "builtin://shaders/object-id"; constexpr const char* kBuiltinObjectIdShaderPath = "builtin://shaders/object-id";
constexpr const char* kBuiltinObjectIdOutlineShaderPath = "builtin://shaders/object-id-outline"; constexpr const char* kBuiltinObjectIdOutlineShaderPath = "builtin://shaders/object-id-outline";
constexpr const char* kBuiltinSelectionMaskShaderPath = "builtin://shaders/selection-mask";
constexpr const char* kBuiltinSelectionOutlineShaderPath = "builtin://shaders/selection-outline";
constexpr const char* kBuiltinSkyboxShaderPath = "builtin://shaders/skybox"; constexpr const char* kBuiltinSkyboxShaderPath = "builtin://shaders/skybox";
constexpr const char* kBuiltinVolumetricShaderPath = "builtin://shaders/volumetric"; constexpr const char* kBuiltinVolumetricShaderPath = "builtin://shaders/volumetric";
constexpr const char* kBuiltinColorScalePostProcessShaderPath = constexpr const char* kBuiltinColorScalePostProcessShaderPath =
@@ -60,6 +62,10 @@ constexpr const char* kBuiltinObjectIdShaderAssetRelativePath =
"engine/assets/builtin/shaders/object-id.shader"; "engine/assets/builtin/shaders/object-id.shader";
constexpr const char* kBuiltinObjectIdOutlineShaderAssetRelativePath = constexpr const char* kBuiltinObjectIdOutlineShaderAssetRelativePath =
"engine/assets/builtin/shaders/object-id-outline.shader"; "engine/assets/builtin/shaders/object-id-outline.shader";
constexpr const char* kBuiltinSelectionMaskShaderAssetRelativePath =
"engine/assets/builtin/shaders/selection-mask.shader";
constexpr const char* kBuiltinSelectionOutlineShaderAssetRelativePath =
"engine/assets/builtin/shaders/selection-outline.shader";
constexpr const char* kBuiltinSkyboxShaderAssetRelativePath = constexpr const char* kBuiltinSkyboxShaderAssetRelativePath =
"engine/assets/builtin/shaders/skybox.shader"; "engine/assets/builtin/shaders/skybox.shader";
constexpr const char* kBuiltinVolumetricShaderAssetRelativePath = constexpr const char* kBuiltinVolumetricShaderAssetRelativePath =
@@ -149,6 +155,12 @@ const char* GetBuiltinShaderAssetRelativePath(const Containers::String& builtinS
if (builtinShaderPath == Containers::String(kBuiltinObjectIdOutlineShaderPath)) { if (builtinShaderPath == Containers::String(kBuiltinObjectIdOutlineShaderPath)) {
return kBuiltinObjectIdOutlineShaderAssetRelativePath; return kBuiltinObjectIdOutlineShaderAssetRelativePath;
} }
if (builtinShaderPath == Containers::String(kBuiltinSelectionMaskShaderPath)) {
return kBuiltinSelectionMaskShaderAssetRelativePath;
}
if (builtinShaderPath == Containers::String(kBuiltinSelectionOutlineShaderPath)) {
return kBuiltinSelectionOutlineShaderAssetRelativePath;
}
if (builtinShaderPath == Containers::String(kBuiltinSkyboxShaderPath)) { if (builtinShaderPath == Containers::String(kBuiltinSkyboxShaderPath)) {
return kBuiltinSkyboxShaderAssetRelativePath; return kBuiltinSkyboxShaderAssetRelativePath;
} }
@@ -710,6 +722,14 @@ Shader* BuildBuiltinObjectIdOutlineShader(const Containers::String& path) {
return TryLoadBuiltinShaderFromAsset(path); return TryLoadBuiltinShaderFromAsset(path);
} }
Shader* BuildBuiltinSelectionMaskShader(const Containers::String& path) {
return TryLoadBuiltinShaderFromAsset(path);
}
Shader* BuildBuiltinSelectionOutlineShader(const Containers::String& path) {
return TryLoadBuiltinShaderFromAsset(path);
}
Shader* BuildBuiltinSkyboxShader(const Containers::String& path) { Shader* BuildBuiltinSkyboxShader(const Containers::String& path) {
return TryLoadBuiltinShaderFromAsset(path); return TryLoadBuiltinShaderFromAsset(path);
} }
@@ -735,9 +755,6 @@ Material* BuildDefaultPrimitiveMaterial(const Containers::String& path) {
params.memorySize = 0; params.memorySize = 0;
material->Initialize(params); material->Initialize(params);
MaterialRenderState renderState = {};
renderState.cullMode = MaterialCullMode::Back;
material->SetRenderState(renderState);
material->SetRenderQueue(MaterialRenderQueue::Geometry); material->SetRenderQueue(MaterialRenderQueue::Geometry);
material->SetShader(ResourceManager::Get().Load<Shader>(GetBuiltinForwardLitShaderPath())); material->SetShader(ResourceManager::Get().Load<Shader>(GetBuiltinForwardLitShaderPath()));
material->SetTexture( material->SetTexture(
@@ -823,6 +840,14 @@ bool TryGetBuiltinShaderPathByShaderName(
outPath = GetBuiltinObjectIdOutlineShaderPath(); outPath = GetBuiltinObjectIdOutlineShaderPath();
return true; return true;
} }
if (shaderName == "Builtin Selection Mask") {
outPath = GetBuiltinSelectionMaskShaderPath();
return true;
}
if (shaderName == "Builtin Selection Outline") {
outPath = GetBuiltinSelectionOutlineShaderPath();
return true;
}
if (shaderName == "Builtin Skybox") { if (shaderName == "Builtin Skybox") {
outPath = GetBuiltinSkyboxShaderPath(); outPath = GetBuiltinSkyboxShaderPath();
return true; return true;
@@ -902,6 +927,14 @@ Containers::String GetBuiltinObjectIdOutlineShaderPath() {
return Containers::String(kBuiltinObjectIdOutlineShaderPath); return Containers::String(kBuiltinObjectIdOutlineShaderPath);
} }
Containers::String GetBuiltinSelectionMaskShaderPath() {
return Containers::String(kBuiltinSelectionMaskShaderPath);
}
Containers::String GetBuiltinSelectionOutlineShaderPath() {
return Containers::String(kBuiltinSelectionOutlineShaderPath);
}
Containers::String GetBuiltinSkyboxShaderPath() { Containers::String GetBuiltinSkyboxShaderPath() {
return Containers::String(kBuiltinSkyboxShaderPath); return Containers::String(kBuiltinSkyboxShaderPath);
} }
@@ -1018,6 +1051,10 @@ LoadResult CreateBuiltinShaderResource(const Containers::String& path) {
shader = BuildBuiltinObjectIdShader(path); shader = BuildBuiltinObjectIdShader(path);
} else if (path == GetBuiltinObjectIdOutlineShaderPath()) { } else if (path == GetBuiltinObjectIdOutlineShaderPath()) {
shader = BuildBuiltinObjectIdOutlineShader(path); shader = BuildBuiltinObjectIdOutlineShader(path);
} else if (path == GetBuiltinSelectionMaskShaderPath()) {
shader = BuildBuiltinSelectionMaskShader(path);
} else if (path == GetBuiltinSelectionOutlineShaderPath()) {
shader = BuildBuiltinSelectionOutlineShader(path);
} else if (path == GetBuiltinSkyboxShaderPath()) { } else if (path == GetBuiltinSkyboxShaderPath()) {
shader = BuildBuiltinSkyboxShader(path); shader = BuildBuiltinSkyboxShader(path);
} else if (path == GetBuiltinVolumetricShaderPath()) { } else if (path == GetBuiltinVolumetricShaderPath()) {

View File

@@ -180,6 +180,19 @@ TEST(MaterialLoader, ResourceManagerRegistersMaterialAndShaderLoaders) {
manager.Shutdown(); manager.Shutdown();
} }
TEST(MaterialLoader, BuiltinDefaultPrimitiveMaterialLeavesRenderStateOverrideDisabled) {
ResourceManager& manager = ResourceManager::Get();
manager.Initialize();
const auto materialHandle = manager.Load<Material>(GetBuiltinDefaultPrimitiveMaterialPath());
ASSERT_TRUE(materialHandle.IsValid());
ASSERT_NE(materialHandle->GetShader(), nullptr);
EXPECT_EQ(materialHandle->GetShader()->GetPath(), GetBuiltinForwardLitShaderPath());
EXPECT_FALSE(materialHandle->HasRenderStateOverride());
manager.Shutdown();
}
TEST(MaterialLoader, LoadValidMaterialParsesRenderMetadata) { TEST(MaterialLoader, LoadValidMaterialParsesRenderMetadata) {
ResourceManager& manager = ResourceManager::Get(); ResourceManager& manager = ResourceManager::Get();
manager.Initialize(); manager.Initialize();

View File

@@ -69,6 +69,8 @@ TEST(ShaderLoader, CanLoad) {
EXPECT_TRUE(loader.CanLoad(GetBuiltinUnlitShaderPath())); EXPECT_TRUE(loader.CanLoad(GetBuiltinUnlitShaderPath()));
EXPECT_TRUE(loader.CanLoad(GetBuiltinObjectIdShaderPath())); EXPECT_TRUE(loader.CanLoad(GetBuiltinObjectIdShaderPath()));
EXPECT_TRUE(loader.CanLoad(GetBuiltinObjectIdOutlineShaderPath())); EXPECT_TRUE(loader.CanLoad(GetBuiltinObjectIdOutlineShaderPath()));
EXPECT_TRUE(loader.CanLoad(GetBuiltinSelectionMaskShaderPath()));
EXPECT_TRUE(loader.CanLoad(GetBuiltinSelectionOutlineShaderPath()));
EXPECT_FALSE(loader.CanLoad("test.vert")); EXPECT_FALSE(loader.CanLoad("test.vert"));
EXPECT_FALSE(loader.CanLoad("test.frag")); EXPECT_FALSE(loader.CanLoad("test.frag"));
EXPECT_FALSE(loader.CanLoad("test.glsl")); EXPECT_FALSE(loader.CanLoad("test.glsl"));
@@ -2516,6 +2518,27 @@ TEST(ShaderLoader, LoadBuiltinObjectIdShaderBuildsAuthoringVariants) {
delete shader; delete shader;
} }
TEST(ShaderLoader, LoadBuiltinSelectionMaskShaderBuildsAuthoringVariants) {
ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinSelectionMaskShaderPath());
ASSERT_TRUE(result);
ASSERT_NE(result.resource, nullptr);
Shader* shader = static_cast<Shader*>(result.resource);
ASSERT_NE(shader, nullptr);
ASSERT_TRUE(shader->IsValid());
const ShaderPass* alphaTestPass = shader->FindPass("SelectionMask");
ASSERT_NE(alphaTestPass, nullptr);
EXPECT_EQ(alphaTestPass->resources.Size(), 4u);
ASSERT_EQ(alphaTestPass->keywordDeclarations.Size(), 1u);
EXPECT_EQ(alphaTestPass->keywordDeclarations[0].type, ShaderKeywordDeclarationType::ShaderFeatureLocal);
ASSERT_EQ(alphaTestPass->keywordDeclarations[0].options.Size(), 2u);
EXPECT_EQ(alphaTestPass->keywordDeclarations[0].options[1], "XC_ALPHA_TEST");
delete shader;
}
TEST(ShaderLoader, LoadBuiltinDepthOnlyShaderBuildsAuthoringVariants) { TEST(ShaderLoader, LoadBuiltinDepthOnlyShaderBuildsAuthoringVariants) {
ShaderLoader loader; ShaderLoader loader;
LoadResult result = loader.Load(GetBuiltinDepthOnlyShaderPath()); LoadResult result = loader.Load(GetBuiltinDepthOnlyShaderPath());

View File

@@ -78,10 +78,16 @@ SceneViewportOverlayFrameData CreateOverlayFrameDataWithLine(const SceneViewport
} }
TEST(SceneViewportRenderPassBundleTest, BuildRenderPlanCreatesGridOutlineAndOverlayPasses) { TEST(SceneViewportRenderPassBundleTest, BuildRenderPlanCreatesGridOutlineAndOverlayPasses) {
DummyResourceView objectIdShaderView(ResourceViewType::ShaderResource); DummyResourceView depthView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt);
DummyResourceView depthShaderView(ResourceViewType::ShaderResource, Format::D24_UNorm_S8_UInt);
DummyResourceView selectionMaskView(ResourceViewType::RenderTarget);
DummyResourceView selectionMaskShaderView(ResourceViewType::ShaderResource);
ViewportRenderTargets targets = {}; ViewportRenderTargets targets = {};
targets.objectIdShaderView = &objectIdShaderView; targets.depthView = &depthView;
targets.depthShaderView = &depthShaderView;
targets.selectionMaskView = &selectionMaskView;
targets.selectionMaskShaderView = &selectionMaskShaderView;
const SceneViewportOverlayData overlay = CreateValidOverlay(); const SceneViewportOverlayData overlay = CreateValidOverlay();
const SceneViewportOverlayFrameData editorOverlayFrameData = const SceneViewportOverlayFrameData editorOverlayFrameData =
@@ -100,12 +106,13 @@ TEST(SceneViewportRenderPassBundleTest, BuildRenderPlanCreatesGridOutlineAndOver
EXPECT_EQ(result.warningStatusText, nullptr); EXPECT_EQ(result.warningStatusText, nullptr);
} }
TEST(SceneViewportRenderPassBundleTest, BuildRenderPlanReportsMissingObjectIdShaderView) { TEST(SceneViewportRenderPassBundleTest, BuildRenderPlanReportsMissingSelectionOutlineResources) {
const SceneViewportOverlayData overlay = CreateValidOverlay(); const SceneViewportOverlayData overlay = CreateValidOverlay();
ViewportRenderTargets targets = {};
SceneViewportRenderPassBundle bundle; SceneViewportRenderPassBundle bundle;
const auto result = bundle.BuildRenderPlan( const auto result = bundle.BuildRenderPlan(
{}, targets,
overlay, overlay,
{ 42u }, { 42u },
{}, {},
@@ -113,7 +120,7 @@ TEST(SceneViewportRenderPassBundleTest, BuildRenderPlanReportsMissingObjectIdSha
EXPECT_EQ(result.plan.postScenePasses.GetPassCount(), 1u); EXPECT_EQ(result.plan.postScenePasses.GetPassCount(), 1u);
EXPECT_EQ(result.plan.overlayPasses.GetPassCount(), 0u); EXPECT_EQ(result.plan.overlayPasses.GetPassCount(), 0u);
EXPECT_STREQ(result.warningStatusText, "Scene object id shader view is unavailable"); EXPECT_STREQ(result.warningStatusText, "Scene selection outline resources are unavailable");
} }
} // namespace } // namespace

View File

@@ -18,11 +18,15 @@ using XCEngine::Editor::GetSceneViewportInfiniteGridShaderPath;
using XCEngine::Editor::GetSceneViewportPointLightGizmoIconPath; using XCEngine::Editor::GetSceneViewportPointLightGizmoIconPath;
using XCEngine::Editor::GetSceneViewportSpotLightGizmoIconPath; using XCEngine::Editor::GetSceneViewportSpotLightGizmoIconPath;
using XCEngine::Resources::GetBuiltinObjectIdOutlineShaderPath; using XCEngine::Resources::GetBuiltinObjectIdOutlineShaderPath;
using XCEngine::Resources::GetBuiltinSelectionMaskShaderPath;
using XCEngine::Resources::GetBuiltinSelectionOutlineShaderPath;
using XCEngine::Resources::LoadResult; using XCEngine::Resources::LoadResult;
using XCEngine::Resources::ResourceHandle; using XCEngine::Resources::ResourceHandle;
using XCEngine::Resources::ResourceManager; using XCEngine::Resources::ResourceManager;
using XCEngine::Resources::Shader; using XCEngine::Resources::Shader;
using XCEngine::Resources::ShaderBackend; using XCEngine::Resources::ShaderBackend;
using XCEngine::Resources::ShaderKeywordDeclarationType;
using XCEngine::Resources::ShaderKeywordSet;
using XCEngine::Resources::ShaderLoader; using XCEngine::Resources::ShaderLoader;
using XCEngine::Resources::ShaderPass; using XCEngine::Resources::ShaderPass;
using XCEngine::Resources::ShaderStageVariant; using XCEngine::Resources::ShaderStageVariant;
@@ -110,4 +114,61 @@ TEST(SceneViewportShaderPathsTest, ResourceManagerLoadsSceneViewportOutlineShade
manager.Shutdown(); manager.Shutdown();
} }
TEST(SceneViewportShaderPathsTest, ResourceManagerLoadsSelectionOutlineShadersByResolvedPath) {
ResourceManager& manager = ResourceManager::Get();
manager.Shutdown();
const ResourceHandle<Shader> maskShaderHandle = manager.Load<Shader>(GetBuiltinSelectionMaskShaderPath());
ASSERT_TRUE(maskShaderHandle.IsValid());
const ShaderPass* maskPass = maskShaderHandle->FindPass("SelectionMask");
ASSERT_NE(maskPass, nullptr);
ASSERT_EQ(maskPass->variants.Size(), 4u);
ASSERT_EQ(maskPass->keywordDeclarations.Size(), 1u);
EXPECT_EQ(maskPass->keywordDeclarations[0].type, ShaderKeywordDeclarationType::ShaderFeatureLocal);
ASSERT_EQ(maskPass->keywordDeclarations[0].options.Size(), 2u);
EXPECT_EQ(maskPass->keywordDeclarations[0].options[0], "_");
EXPECT_EQ(maskPass->keywordDeclarations[0].options[1], "XC_ALPHA_TEST");
EXPECT_TRUE(maskShaderHandle->PassDeclaresKeyword("SelectionMask", "XC_ALPHA_TEST"));
const ShaderStageVariant* maskFragment = maskShaderHandle->FindVariant(
"SelectionMask",
ShaderType::Fragment,
ShaderBackend::D3D12);
ASSERT_NE(maskFragment, nullptr);
EXPECT_NE(
std::string(maskFragment->sourceCode.CStr()).find("XC_BUILTIN_SELECTION_MASK_D3D12_PS"),
std::string::npos);
ShaderKeywordSet alphaKeywords = {};
alphaKeywords.enabledKeywords.PushBack("XC_ALPHA_TEST");
const ShaderStageVariant* alphaMaskFragment = maskShaderHandle->FindVariant(
"SelectionMask",
ShaderType::Fragment,
ShaderBackend::D3D12,
alphaKeywords);
ASSERT_NE(alphaMaskFragment, nullptr);
EXPECT_NE(
std::string(alphaMaskFragment->sourceCode.CStr()).find("#define XC_ALPHA_TEST 1"),
std::string::npos);
const ResourceHandle<Shader> outlineShaderHandle = manager.Load<Shader>(GetBuiltinSelectionOutlineShaderPath());
ASSERT_TRUE(outlineShaderHandle.IsValid());
const ShaderPass* outlinePass = outlineShaderHandle->FindPass("SelectionOutline");
ASSERT_NE(outlinePass, nullptr);
ASSERT_EQ(outlinePass->variants.Size(), 2u);
const ShaderStageVariant* outlineFragment = outlineShaderHandle->FindVariant(
"SelectionOutline",
ShaderType::Fragment,
ShaderBackend::D3D12);
ASSERT_NE(outlineFragment, nullptr);
EXPECT_NE(
std::string(outlineFragment->sourceCode.CStr()).find("XC_BUILTIN_SELECTION_OUTLINE_D3D12_PS"),
std::string::npos);
manager.Shutdown();
}
} // namespace } // namespace

View File

@@ -73,9 +73,15 @@ TEST(ViewportHostSurfaceUtilsTest, ViewportReuseRequiresObjectIdOnlyForSceneView
EXPECT_TRUE(ViewportRequiresObjectIdResources(EditorViewportKind::Scene)); EXPECT_TRUE(ViewportRequiresObjectIdResources(EditorViewportKind::Scene));
EXPECT_FALSE(CanReuseViewportResources(sceneQuery)); EXPECT_FALSE(CanReuseViewportResources(sceneQuery));
sceneQuery.resources.hasDepthShaderView = true;
sceneQuery.resources.hasObjectIdTexture = true; sceneQuery.resources.hasObjectIdTexture = true;
sceneQuery.resources.hasObjectIdDepthTexture = true;
sceneQuery.resources.hasObjectIdDepthView = true;
sceneQuery.resources.hasObjectIdView = true; sceneQuery.resources.hasObjectIdView = true;
sceneQuery.resources.hasObjectIdShaderView = true; sceneQuery.resources.hasObjectIdShaderView = true;
sceneQuery.resources.hasSelectionMaskTexture = true;
sceneQuery.resources.hasSelectionMaskView = true;
sceneQuery.resources.hasSelectionMaskShaderView = true;
EXPECT_TRUE(CanReuseViewportResources(sceneQuery)); EXPECT_TRUE(CanReuseViewportResources(sceneQuery));
} }
@@ -90,9 +96,15 @@ TEST(ViewportHostSurfaceUtilsTest, ViewportReuseRejectsMismatchedOrMissingResour
query.resources.hasColorView = true; query.resources.hasColorView = true;
query.resources.hasDepthTexture = true; query.resources.hasDepthTexture = true;
query.resources.hasDepthView = true; query.resources.hasDepthView = true;
query.resources.hasDepthShaderView = true;
query.resources.hasObjectIdTexture = true; query.resources.hasObjectIdTexture = true;
query.resources.hasObjectIdDepthTexture = true;
query.resources.hasObjectIdDepthView = true;
query.resources.hasObjectIdView = true; query.resources.hasObjectIdView = true;
query.resources.hasObjectIdShaderView = true; query.resources.hasObjectIdShaderView = true;
query.resources.hasSelectionMaskTexture = true;
query.resources.hasSelectionMaskView = true;
query.resources.hasSelectionMaskShaderView = true;
query.resources.hasTextureDescriptor = true; query.resources.hasTextureDescriptor = true;
EXPECT_TRUE(CanReuseViewportResources(query)); EXPECT_TRUE(CanReuseViewportResources(query));

View File

@@ -190,6 +190,7 @@ TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportSelectionOutlineStyleApplies
TEST(ViewportRenderFlowUtilsTest, ApplySceneRenderRequestSetupAttachesOptionalPassesAndObjectIdSurface) { TEST(ViewportRenderFlowUtilsTest, ApplySceneRenderRequestSetupAttachesOptionalPassesAndObjectIdSurface) {
DummyResourceView depthView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt); DummyResourceView depthView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt);
DummyResourceView objectIdDepthView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt);
DummyResourceView objectIdView(ResourceViewType::RenderTarget); DummyResourceView objectIdView(ResourceViewType::RenderTarget);
DummyResourceView objectIdShaderView(ResourceViewType::ShaderResource); DummyResourceView objectIdShaderView(ResourceViewType::ShaderResource);
@@ -197,6 +198,7 @@ TEST(ViewportRenderFlowUtilsTest, ApplySceneRenderRequestSetupAttachesOptionalPa
targets.width = 800; targets.width = 800;
targets.height = 600; targets.height = 600;
targets.depthView = &depthView; targets.depthView = &depthView;
targets.objectIdDepthView = &objectIdDepthView;
targets.objectIdView = &objectIdView; targets.objectIdView = &objectIdView;
targets.objectIdShaderView = &objectIdShaderView; targets.objectIdShaderView = &objectIdShaderView;
targets.objectIdState = ResourceStates::Common; targets.objectIdState = ResourceStates::Common;
@@ -217,7 +219,7 @@ TEST(ViewportRenderFlowUtilsTest, ApplySceneRenderRequestSetupAttachesOptionalPa
EXPECT_TRUE(request.objectId.IsRequested()); EXPECT_TRUE(request.objectId.IsRequested());
ASSERT_EQ(request.objectId.surface.GetColorAttachments().size(), 1u); ASSERT_EQ(request.objectId.surface.GetColorAttachments().size(), 1u);
EXPECT_EQ(request.objectId.surface.GetColorAttachments()[0], &objectIdView); EXPECT_EQ(request.objectId.surface.GetColorAttachments()[0], &objectIdView);
EXPECT_EQ(request.objectId.surface.GetDepthAttachment(), &depthView); EXPECT_EQ(request.objectId.surface.GetDepthAttachment(), &objectIdDepthView);
const auto requestArea = request.surface.GetRenderArea(); const auto requestArea = request.surface.GetRenderArea();
const auto objectIdArea = request.objectId.surface.GetRenderArea(); const auto objectIdArea = request.objectId.surface.GetRenderArea();
@@ -247,9 +249,18 @@ TEST(ViewportRenderFlowUtilsTest, ApplySceneRenderRequestSetupSkipsUnavailableOp
} }
TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanCollectsPostSceneAndOverlayPasses) { TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanCollectsPostSceneAndOverlayPasses) {
DummyResourceView depthView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt);
DummyResourceView depthShaderView(ResourceViewType::ShaderResource, Format::D24_UNorm_S8_UInt);
DummyResourceView selectionMaskView(ResourceViewType::RenderTarget);
DummyResourceView selectionMaskShaderView(ResourceViewType::ShaderResource);
DummyResourceView objectIdShaderView(ResourceViewType::ShaderResource); DummyResourceView objectIdShaderView(ResourceViewType::ShaderResource);
ViewportRenderTargets targets = {}; ViewportRenderTargets targets = {};
targets.depthView = &depthView;
targets.depthShaderView = &depthShaderView;
targets.selectionMaskView = &selectionMaskView;
targets.selectionMaskShaderView = &selectionMaskShaderView;
targets.objectIdShaderView = &objectIdShaderView; targets.objectIdShaderView = &objectIdShaderView;
const SceneViewportOverlayData overlay = CreateValidOverlay(); const SceneViewportOverlayData overlay = CreateValidOverlay();
@@ -274,11 +285,16 @@ TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanCollectsPostSceneA
return std::make_unique<NoopRenderPass>(); return std::make_unique<NoopRenderPass>();
}, },
[&selectionOutlinePassFactoryCallCount]( [&selectionOutlinePassFactoryCallCount](
RHIResourceView* objectIdTextureView, ViewportRenderTargets* outlineTargets,
const std::vector<uint64_t>& selectedObjectIds, const std::vector<uint64_t>& selectedObjectIds,
const SceneViewportSelectionOutlineStyle& style) { const SceneViewportSelectionOutlineStyle& style) {
++selectionOutlinePassFactoryCallCount; ++selectionOutlinePassFactoryCallCount;
EXPECT_NE(objectIdTextureView, nullptr); EXPECT_NE(outlineTargets, nullptr);
if (outlineTargets == nullptr) {
return std::unique_ptr<NoopRenderPass>();
}
EXPECT_NE(outlineTargets->selectionMaskShaderView, nullptr);
EXPECT_NE(outlineTargets->depthShaderView, nullptr);
EXPECT_EQ(selectedObjectIds.size(), 2u); EXPECT_EQ(selectedObjectIds.size(), 2u);
EXPECT_FLOAT_EQ(style.outlineWidthPixels, 2.0f); EXPECT_FLOAT_EQ(style.outlineWidthPixels, 2.0f);
return std::make_unique<NoopRenderPass>(); return std::make_unique<NoopRenderPass>();
@@ -301,6 +317,7 @@ TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanCollectsPostSceneA
TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanSkipsRhiOverlayPassWhenFrameContainsOnlySceneIcons) { TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanSkipsRhiOverlayPassWhenFrameContainsOnlySceneIcons) {
const SceneViewportOverlayData overlay = CreateValidOverlay(); const SceneViewportOverlayData overlay = CreateValidOverlay();
ViewportRenderTargets targets = {};
SceneViewportOverlayFrameData editorOverlayFrameData = {}; SceneViewportOverlayFrameData editorOverlayFrameData = {};
editorOverlayFrameData.overlay = overlay; editorOverlayFrameData.overlay = overlay;
@@ -310,7 +327,7 @@ TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanSkipsRhiOverlayPas
size_t overlayFactoryCallCount = 0u; size_t overlayFactoryCallCount = 0u;
const auto result = BuildSceneViewportRenderPlan( const auto result = BuildSceneViewportRenderPlan(
{}, targets,
overlay, overlay,
{}, {},
editorOverlayFrameData, editorOverlayFrameData,
@@ -318,7 +335,7 @@ TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanSkipsRhiOverlayPas
return std::make_unique<NoopRenderPass>(); return std::make_unique<NoopRenderPass>();
}, },
[]( [](
RHIResourceView*, ViewportRenderTargets*,
const std::vector<uint64_t>&, const std::vector<uint64_t>&,
const SceneViewportSelectionOutlineStyle&) { const SceneViewportSelectionOutlineStyle&) {
return std::make_unique<NoopRenderPass>(); return std::make_unique<NoopRenderPass>();
@@ -337,8 +354,9 @@ TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanSkipsRhiOverlayPas
TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanWarnsWhenSelectionOutlineCannotAccessObjectIdTexture) { TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanWarnsWhenSelectionOutlineCannotAccessObjectIdTexture) {
const SceneViewportOverlayData overlay = CreateValidOverlay(); const SceneViewportOverlayData overlay = CreateValidOverlay();
ViewportRenderTargets targets = {};
const auto result = BuildSceneViewportRenderPlan( const auto result = BuildSceneViewportRenderPlan(
{}, targets,
overlay, overlay,
{ 42u }, { 42u },
{}, {},
@@ -346,7 +364,7 @@ TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanWarnsWhenSelection
return std::make_unique<NoopRenderPass>(); return std::make_unique<NoopRenderPass>();
}, },
[]( [](
RHIResourceView*, ViewportRenderTargets*,
const std::vector<uint64_t>&, const std::vector<uint64_t>&,
const SceneViewportSelectionOutlineStyle&) { const SceneViewportSelectionOutlineStyle&) {
return std::make_unique<NoopRenderPass>(); return std::make_unique<NoopRenderPass>();
@@ -357,11 +375,12 @@ TEST(ViewportRenderFlowUtilsTest, BuildSceneViewportRenderPlanWarnsWhenSelection
false); false);
EXPECT_EQ(result.plan.postScenePasses.GetPassCount(), 1u); EXPECT_EQ(result.plan.postScenePasses.GetPassCount(), 1u);
EXPECT_STREQ(result.warningStatusText, "Scene object id shader view is unavailable"); EXPECT_STREQ(result.warningStatusText, "Scene selection outline resources are unavailable");
} }
TEST(ViewportRenderFlowUtilsTest, ApplySceneViewportRenderPlanAttachesPlannedPassesAndClearState) { TEST(ViewportRenderFlowUtilsTest, ApplySceneViewportRenderPlanAttachesPlannedPassesAndClearState) {
DummyResourceView depthView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt); DummyResourceView depthView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt);
DummyResourceView objectIdDepthView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt);
DummyResourceView objectIdView(ResourceViewType::RenderTarget); DummyResourceView objectIdView(ResourceViewType::RenderTarget);
DummyResourceView objectIdShaderView(ResourceViewType::ShaderResource); DummyResourceView objectIdShaderView(ResourceViewType::ShaderResource);
@@ -369,6 +388,7 @@ TEST(ViewportRenderFlowUtilsTest, ApplySceneViewportRenderPlanAttachesPlannedPas
targets.width = 800; targets.width = 800;
targets.height = 600; targets.height = 600;
targets.depthView = &depthView; targets.depthView = &depthView;
targets.objectIdDepthView = &objectIdDepthView;
targets.objectIdView = &objectIdView; targets.objectIdView = &objectIdView;
targets.objectIdShaderView = &objectIdShaderView; targets.objectIdShaderView = &objectIdShaderView;
@@ -395,14 +415,19 @@ TEST(ViewportRenderFlowUtilsTest, ApplySceneViewportRenderPlanAttachesPlannedPas
TEST(ViewportRenderFlowUtilsTest, MarkSceneRenderSuccessMovesTargetsToShaderResourceState) { TEST(ViewportRenderFlowUtilsTest, MarkSceneRenderSuccessMovesTargetsToShaderResourceState) {
DummyResourceView depthView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt); DummyResourceView depthView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt);
DummyResourceView objectIdView(ResourceViewType::RenderTarget); DummyResourceView objectIdView(ResourceViewType::RenderTarget);
DummyResourceView objectIdDepthView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt);
DummyResourceView selectionMaskView(ResourceViewType::RenderTarget);
ViewportRenderTargets targets = {}; ViewportRenderTargets targets = {};
targets.width = 640; targets.width = 640;
targets.height = 360; targets.height = 360;
targets.depthView = &depthView; targets.depthView = &depthView;
targets.objectIdDepthView = &objectIdDepthView;
targets.objectIdView = &objectIdView; targets.objectIdView = &objectIdView;
targets.selectionMaskView = &selectionMaskView;
targets.colorState = ResourceStates::Common; targets.colorState = ResourceStates::Common;
targets.objectIdState = ResourceStates::Common; targets.objectIdState = ResourceStates::Common;
targets.selectionMaskState = ResourceStates::Common;
XCEngine::Rendering::CameraRenderRequest request = {}; XCEngine::Rendering::CameraRenderRequest request = {};
request.surface = RenderSurface(640, 360); request.surface = RenderSurface(640, 360);
@@ -411,16 +436,19 @@ TEST(ViewportRenderFlowUtilsTest, MarkSceneRenderSuccessMovesTargetsToShaderReso
MarkSceneViewportRenderSuccess(targets, request); MarkSceneViewportRenderSuccess(targets, request);
EXPECT_EQ(targets.colorState, ResourceStates::PixelShaderResource); EXPECT_EQ(targets.colorState, ResourceStates::PixelShaderResource);
EXPECT_EQ(targets.objectIdState, ResourceStates::PixelShaderResource); EXPECT_EQ(targets.objectIdState, ResourceStates::PixelShaderResource);
EXPECT_EQ(targets.selectionMaskState, ResourceStates::PixelShaderResource);
EXPECT_TRUE(targets.hasValidObjectIdFrame); EXPECT_TRUE(targets.hasValidObjectIdFrame);
ViewportRenderTargets noObjectIdTargets = {}; ViewportRenderTargets noObjectIdTargets = {};
noObjectIdTargets.colorState = ResourceStates::Common; noObjectIdTargets.colorState = ResourceStates::Common;
noObjectIdTargets.objectIdState = ResourceStates::Common; noObjectIdTargets.objectIdState = ResourceStates::Common;
noObjectIdTargets.selectionMaskState = ResourceStates::Common;
XCEngine::Rendering::CameraRenderRequest noObjectIdRequest = {}; XCEngine::Rendering::CameraRenderRequest noObjectIdRequest = {};
MarkSceneViewportRenderSuccess(noObjectIdTargets, noObjectIdRequest); MarkSceneViewportRenderSuccess(noObjectIdTargets, noObjectIdRequest);
EXPECT_EQ(noObjectIdTargets.colorState, ResourceStates::PixelShaderResource); EXPECT_EQ(noObjectIdTargets.colorState, ResourceStates::PixelShaderResource);
EXPECT_EQ(noObjectIdTargets.objectIdState, ResourceStates::PixelShaderResource); EXPECT_EQ(noObjectIdTargets.objectIdState, ResourceStates::PixelShaderResource);
EXPECT_EQ(noObjectIdTargets.selectionMaskState, ResourceStates::PixelShaderResource);
EXPECT_FALSE(noObjectIdTargets.hasValidObjectIdFrame); EXPECT_FALSE(noObjectIdTargets.hasValidObjectIdFrame);
} }

View File

@@ -8,6 +8,7 @@ namespace {
using XCEngine::Editor::BuildViewportColorSurface; using XCEngine::Editor::BuildViewportColorSurface;
using XCEngine::Editor::BuildViewportObjectIdSurface; using XCEngine::Editor::BuildViewportObjectIdSurface;
using XCEngine::Editor::BuildViewportSelectionMaskSurface;
using XCEngine::Editor::BuildViewportRenderTargetsReuseQuery; using XCEngine::Editor::BuildViewportRenderTargetsReuseQuery;
using XCEngine::Editor::DestroyViewportRenderTargets; using XCEngine::Editor::DestroyViewportRenderTargets;
using XCEngine::Editor::EditorViewportKind; using XCEngine::Editor::EditorViewportKind;
@@ -78,10 +79,16 @@ TEST(ViewportRenderTargetsTest, BuildReuseQueryReflectsCurrentResourcePresence)
targets.colorView = reinterpret_cast<RHIResourceView*>(static_cast<uintptr_t>(0x2)); targets.colorView = reinterpret_cast<RHIResourceView*>(static_cast<uintptr_t>(0x2));
targets.depthTexture = reinterpret_cast<RHITexture*>(static_cast<uintptr_t>(0x3)); targets.depthTexture = reinterpret_cast<RHITexture*>(static_cast<uintptr_t>(0x3));
targets.depthView = reinterpret_cast<RHIResourceView*>(static_cast<uintptr_t>(0x4)); targets.depthView = reinterpret_cast<RHIResourceView*>(static_cast<uintptr_t>(0x4));
targets.objectIdTexture = reinterpret_cast<RHITexture*>(static_cast<uintptr_t>(0x5)); targets.depthShaderView = reinterpret_cast<RHIResourceView*>(static_cast<uintptr_t>(0x5));
targets.objectIdView = reinterpret_cast<RHIResourceView*>(static_cast<uintptr_t>(0x6)); targets.objectIdTexture = reinterpret_cast<RHITexture*>(static_cast<uintptr_t>(0x6));
targets.objectIdShaderView = reinterpret_cast<RHIResourceView*>(static_cast<uintptr_t>(0x7)); targets.objectIdDepthTexture = reinterpret_cast<RHITexture*>(static_cast<uintptr_t>(0x7));
targets.textureHandle.nativeHandle = 0x8; targets.objectIdDepthView = reinterpret_cast<RHIResourceView*>(static_cast<uintptr_t>(0x8));
targets.objectIdView = reinterpret_cast<RHIResourceView*>(static_cast<uintptr_t>(0x9));
targets.objectIdShaderView = reinterpret_cast<RHIResourceView*>(static_cast<uintptr_t>(0xA));
targets.selectionMaskTexture = reinterpret_cast<RHITexture*>(static_cast<uintptr_t>(0xB));
targets.selectionMaskView = reinterpret_cast<RHIResourceView*>(static_cast<uintptr_t>(0xC));
targets.selectionMaskShaderView = reinterpret_cast<RHIResourceView*>(static_cast<uintptr_t>(0xD));
targets.textureHandle.nativeHandle = 0xE;
targets.textureHandle.width = 1280; targets.textureHandle.width = 1280;
targets.textureHandle.height = 720; targets.textureHandle.height = 720;
@@ -94,25 +101,36 @@ TEST(ViewportRenderTargetsTest, BuildReuseQueryReflectsCurrentResourcePresence)
EXPECT_TRUE(query.resources.hasColorView); EXPECT_TRUE(query.resources.hasColorView);
EXPECT_TRUE(query.resources.hasDepthTexture); EXPECT_TRUE(query.resources.hasDepthTexture);
EXPECT_TRUE(query.resources.hasDepthView); EXPECT_TRUE(query.resources.hasDepthView);
EXPECT_TRUE(query.resources.hasDepthShaderView);
EXPECT_TRUE(query.resources.hasObjectIdTexture); EXPECT_TRUE(query.resources.hasObjectIdTexture);
EXPECT_TRUE(query.resources.hasObjectIdDepthTexture);
EXPECT_TRUE(query.resources.hasObjectIdDepthView);
EXPECT_TRUE(query.resources.hasObjectIdView); EXPECT_TRUE(query.resources.hasObjectIdView);
EXPECT_TRUE(query.resources.hasObjectIdShaderView); EXPECT_TRUE(query.resources.hasObjectIdShaderView);
EXPECT_TRUE(query.resources.hasSelectionMaskTexture);
EXPECT_TRUE(query.resources.hasSelectionMaskView);
EXPECT_TRUE(query.resources.hasSelectionMaskShaderView);
EXPECT_TRUE(query.resources.hasTextureDescriptor); EXPECT_TRUE(query.resources.hasTextureDescriptor);
} }
TEST(ViewportRenderTargetsTest, BuildSurfaceUsesTargetAttachmentsAndStates) { TEST(ViewportRenderTargetsTest, BuildSurfaceUsesTargetAttachmentsAndStates) {
DummyResourceView colorView(ResourceViewType::RenderTarget); DummyResourceView colorView(ResourceViewType::RenderTarget);
DummyResourceView depthView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt); DummyResourceView depthView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt);
DummyResourceView objectIdDepthView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt);
DummyResourceView objectIdView(ResourceViewType::RenderTarget); DummyResourceView objectIdView(ResourceViewType::RenderTarget);
DummyResourceView selectionMaskView(ResourceViewType::RenderTarget);
ViewportRenderTargets targets = {}; ViewportRenderTargets targets = {};
targets.width = 800; targets.width = 800;
targets.height = 600; targets.height = 600;
targets.colorView = &colorView; targets.colorView = &colorView;
targets.depthView = &depthView; targets.depthView = &depthView;
targets.objectIdDepthView = &objectIdDepthView;
targets.objectIdView = &objectIdView; targets.objectIdView = &objectIdView;
targets.selectionMaskView = &selectionMaskView;
targets.colorState = ResourceStates::Common; targets.colorState = ResourceStates::Common;
targets.objectIdState = ResourceStates::PixelShaderResource; targets.objectIdState = ResourceStates::PixelShaderResource;
targets.selectionMaskState = ResourceStates::PixelShaderResource;
const auto colorSurface = BuildViewportColorSurface(targets); const auto colorSurface = BuildViewportColorSurface(targets);
ASSERT_EQ(colorSurface.GetColorAttachments().size(), 1u); ASSERT_EQ(colorSurface.GetColorAttachments().size(), 1u);
@@ -123,8 +141,14 @@ TEST(ViewportRenderTargetsTest, BuildSurfaceUsesTargetAttachmentsAndStates) {
const auto objectIdSurface = BuildViewportObjectIdSurface(targets); const auto objectIdSurface = BuildViewportObjectIdSurface(targets);
ASSERT_EQ(objectIdSurface.GetColorAttachments().size(), 1u); ASSERT_EQ(objectIdSurface.GetColorAttachments().size(), 1u);
EXPECT_EQ(objectIdSurface.GetColorAttachments()[0], &objectIdView); EXPECT_EQ(objectIdSurface.GetColorAttachments()[0], &objectIdView);
EXPECT_EQ(objectIdSurface.GetDepthAttachment(), &depthView); EXPECT_EQ(objectIdSurface.GetDepthAttachment(), &objectIdDepthView);
EXPECT_EQ(objectIdSurface.GetColorStateBefore(), ResourceStates::PixelShaderResource); EXPECT_EQ(objectIdSurface.GetColorStateBefore(), ResourceStates::PixelShaderResource);
const auto selectionMaskSurface = BuildViewportSelectionMaskSurface(targets);
ASSERT_EQ(selectionMaskSurface.GetColorAttachments().size(), 1u);
EXPECT_EQ(selectionMaskSurface.GetColorAttachments()[0], &selectionMaskView);
EXPECT_EQ(selectionMaskSurface.GetDepthAttachment(), &depthView);
EXPECT_EQ(selectionMaskSurface.GetColorStateBefore(), ResourceStates::PixelShaderResource);
} }
TEST(ViewportRenderTargetsTest, DestroyViewportRenderTargetsShutsDownAndClearsState) { TEST(ViewportRenderTargetsTest, DestroyViewportRenderTargetsShutsDownAndClearsState) {
@@ -132,9 +156,15 @@ TEST(ViewportRenderTargetsTest, DestroyViewportRenderTargetsShutsDownAndClearsSt
auto* colorView = new DummyResourceView(ResourceViewType::RenderTarget); auto* colorView = new DummyResourceView(ResourceViewType::RenderTarget);
auto* depthTexture = new DummyTexture(Format::D24_UNorm_S8_UInt); auto* depthTexture = new DummyTexture(Format::D24_UNorm_S8_UInt);
auto* depthView = new DummyResourceView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt); auto* depthView = new DummyResourceView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt);
auto* depthShaderView = new DummyResourceView(ResourceViewType::ShaderResource, Format::D24_UNorm_S8_UInt);
auto* objectIdTexture = new DummyTexture(); auto* objectIdTexture = new DummyTexture();
auto* objectIdDepthTexture = new DummyTexture(Format::D24_UNorm_S8_UInt);
auto* objectIdDepthView = new DummyResourceView(ResourceViewType::DepthStencil, Format::D24_UNorm_S8_UInt);
auto* objectIdView = new DummyResourceView(ResourceViewType::RenderTarget); auto* objectIdView = new DummyResourceView(ResourceViewType::RenderTarget);
auto* objectIdShaderView = new DummyResourceView(ResourceViewType::ShaderResource); auto* objectIdShaderView = new DummyResourceView(ResourceViewType::ShaderResource);
auto* selectionMaskTexture = new DummyTexture();
auto* selectionMaskView = new DummyResourceView(ResourceViewType::RenderTarget);
auto* selectionMaskShaderView = new DummyResourceView(ResourceViewType::ShaderResource);
ViewportRenderTargets targets = {}; ViewportRenderTargets targets = {};
targets.width = 640; targets.width = 640;
@@ -143,9 +173,15 @@ TEST(ViewportRenderTargetsTest, DestroyViewportRenderTargetsShutsDownAndClearsSt
targets.colorView = colorView; targets.colorView = colorView;
targets.depthTexture = depthTexture; targets.depthTexture = depthTexture;
targets.depthView = depthView; targets.depthView = depthView;
targets.depthShaderView = depthShaderView;
targets.objectIdTexture = objectIdTexture; targets.objectIdTexture = objectIdTexture;
targets.objectIdDepthTexture = objectIdDepthTexture;
targets.objectIdDepthView = objectIdDepthView;
targets.objectIdView = objectIdView; targets.objectIdView = objectIdView;
targets.objectIdShaderView = objectIdShaderView; targets.objectIdShaderView = objectIdShaderView;
targets.selectionMaskTexture = selectionMaskTexture;
targets.selectionMaskView = selectionMaskView;
targets.selectionMaskShaderView = selectionMaskShaderView;
targets.imguiCpuHandle.ptr = 123; targets.imguiCpuHandle.ptr = 123;
targets.imguiGpuHandle.ptr = 456; targets.imguiGpuHandle.ptr = 456;
targets.textureHandle.nativeHandle = 789; targets.textureHandle.nativeHandle = 789;
@@ -153,6 +189,7 @@ TEST(ViewportRenderTargetsTest, DestroyViewportRenderTargetsShutsDownAndClearsSt
targets.textureHandle.height = 360; targets.textureHandle.height = 360;
targets.colorState = ResourceStates::RenderTarget; targets.colorState = ResourceStates::RenderTarget;
targets.objectIdState = ResourceStates::PixelShaderResource; targets.objectIdState = ResourceStates::PixelShaderResource;
targets.selectionMaskState = ResourceStates::PixelShaderResource;
targets.hasValidObjectIdFrame = true; targets.hasValidObjectIdFrame = true;
DestroyViewportRenderTargets(nullptr, targets); DestroyViewportRenderTargets(nullptr, targets);
@@ -161,18 +198,30 @@ TEST(ViewportRenderTargetsTest, DestroyViewportRenderTargetsShutsDownAndClearsSt
EXPECT_TRUE(colorView->shutdownCalled); EXPECT_TRUE(colorView->shutdownCalled);
EXPECT_TRUE(depthTexture->shutdownCalled); EXPECT_TRUE(depthTexture->shutdownCalled);
EXPECT_TRUE(depthView->shutdownCalled); EXPECT_TRUE(depthView->shutdownCalled);
EXPECT_TRUE(depthShaderView->shutdownCalled);
EXPECT_TRUE(objectIdTexture->shutdownCalled); EXPECT_TRUE(objectIdTexture->shutdownCalled);
EXPECT_TRUE(objectIdDepthTexture->shutdownCalled);
EXPECT_TRUE(objectIdDepthView->shutdownCalled);
EXPECT_TRUE(objectIdView->shutdownCalled); EXPECT_TRUE(objectIdView->shutdownCalled);
EXPECT_TRUE(objectIdShaderView->shutdownCalled); EXPECT_TRUE(objectIdShaderView->shutdownCalled);
EXPECT_TRUE(selectionMaskTexture->shutdownCalled);
EXPECT_TRUE(selectionMaskView->shutdownCalled);
EXPECT_TRUE(selectionMaskShaderView->shutdownCalled);
EXPECT_EQ(targets.width, 0u); EXPECT_EQ(targets.width, 0u);
EXPECT_EQ(targets.height, 0u); EXPECT_EQ(targets.height, 0u);
EXPECT_EQ(targets.colorTexture, nullptr); EXPECT_EQ(targets.colorTexture, nullptr);
EXPECT_EQ(targets.colorView, nullptr); EXPECT_EQ(targets.colorView, nullptr);
EXPECT_EQ(targets.depthTexture, nullptr); EXPECT_EQ(targets.depthTexture, nullptr);
EXPECT_EQ(targets.depthView, nullptr); EXPECT_EQ(targets.depthView, nullptr);
EXPECT_EQ(targets.depthShaderView, nullptr);
EXPECT_EQ(targets.objectIdTexture, nullptr); EXPECT_EQ(targets.objectIdTexture, nullptr);
EXPECT_EQ(targets.objectIdDepthTexture, nullptr);
EXPECT_EQ(targets.objectIdDepthView, nullptr);
EXPECT_EQ(targets.objectIdView, nullptr); EXPECT_EQ(targets.objectIdView, nullptr);
EXPECT_EQ(targets.objectIdShaderView, nullptr); EXPECT_EQ(targets.objectIdShaderView, nullptr);
EXPECT_EQ(targets.selectionMaskTexture, nullptr);
EXPECT_EQ(targets.selectionMaskView, nullptr);
EXPECT_EQ(targets.selectionMaskShaderView, nullptr);
EXPECT_EQ(targets.imguiCpuHandle.ptr, 0u); EXPECT_EQ(targets.imguiCpuHandle.ptr, 0u);
EXPECT_EQ(targets.imguiGpuHandle.ptr, 0u); EXPECT_EQ(targets.imguiGpuHandle.ptr, 0u);
EXPECT_EQ(targets.textureHandle.nativeHandle, 0u); EXPECT_EQ(targets.textureHandle.nativeHandle, 0u);
@@ -180,6 +229,7 @@ TEST(ViewportRenderTargetsTest, DestroyViewportRenderTargetsShutsDownAndClearsSt
EXPECT_EQ(targets.textureHandle.height, 0u); EXPECT_EQ(targets.textureHandle.height, 0u);
EXPECT_EQ(targets.colorState, ResourceStates::Common); EXPECT_EQ(targets.colorState, ResourceStates::Common);
EXPECT_EQ(targets.objectIdState, ResourceStates::Common); EXPECT_EQ(targets.objectIdState, ResourceStates::Common);
EXPECT_EQ(targets.selectionMaskState, ResourceStates::Common);
EXPECT_FALSE(targets.hasValidObjectIdFrame); EXPECT_FALSE(targets.hasValidObjectIdFrame);
} }