engine: sync editor rendering and ui changes

This commit is contained in:
2026-04-08 16:09:15 +08:00
parent 31756847ab
commit 162f1cc12e
153 changed files with 4454 additions and 2990 deletions

View File

@@ -1,6 +1,7 @@
#pragma once
#include "Math.h"
#include "Vector3.h"
namespace XCEngine {
namespace Math {

View File

@@ -22,7 +22,7 @@ enum class KeyCode : Core::uint8 {
Up = 126, Down = 125, Left = 123, Right = 124,
Home = 115, End = 119, PageUp = 116, PageDown = 121,
Delete = 51, Backspace = 51,
Delete = 117, Backspace = 51,
Zero = 39, One = 30, Two = 31, Three = 32,
Four = 33, Five = 34, Six = 35, Seven = 37,

View File

@@ -26,6 +26,7 @@ public:
bool IsValid() const override;
void SetViewType(ResourceViewType type) { m_viewType = type; }
void SetFormat(Format format) { m_format = format; }
ResourceViewType GetViewType() const override { return m_viewType; }
ResourceViewDimension GetDimension() const override { return m_dimension; }
Format GetFormat() const override { return m_format; }

View File

@@ -19,10 +19,14 @@ struct OpenGLDepthStencilState {
uint8_t stencilReadMask = 0xFF;
uint8_t stencilWriteMask = 0xFF;
int stencilRef = 0;
ComparisonFunc stencilFunc = ComparisonFunc::Always;
StencilOp stencilFailOp = StencilOp::Keep;
StencilOp stencilDepthFailOp = StencilOp::Keep;
StencilOp stencilDepthPassOp = StencilOp::Keep;
ComparisonFunc frontStencilFunc = ComparisonFunc::Always;
StencilOp frontStencilFailOp = StencilOp::Keep;
StencilOp frontStencilDepthFailOp = StencilOp::Keep;
StencilOp frontStencilDepthPassOp = StencilOp::Keep;
ComparisonFunc backStencilFunc = ComparisonFunc::Always;
StencilOp backStencilFailOp = StencilOp::Keep;
StencilOp backStencilDepthFailOp = StencilOp::Keep;
StencilOp backStencilDepthPassOp = StencilOp::Keep;
};
struct OpenGLBlendState {

View File

@@ -87,6 +87,7 @@ private:
bool m_renderPassActive = false;
bool m_hasViewport = false;
bool m_hasScissor = false;
uint32_t m_stencilRef = 0;
VkViewport m_viewport = {};
VkRect2D m_scissor = {};
std::vector<VkFramebuffer> m_transientFramebuffers;

View File

@@ -18,6 +18,9 @@ enum class BuiltinMaterialPass : Core::uint32 {
DepthOnly,
ShadowCaster,
ObjectId,
Skybox,
PostProcess,
FinalColor,
Forward = ForwardLit
};
@@ -36,7 +39,12 @@ enum class BuiltinPassResourceSemantic : Core::uint8 {
Material,
Lighting,
ShadowReceiver,
Environment,
PassConstants,
BaseColorTexture,
SourceColorTexture,
SkyboxPanoramicTexture,
SkyboxTexture,
ShadowMapTexture,
LinearClampSampler,
ShadowMapSampler
@@ -60,7 +68,12 @@ struct BuiltinPassResourceBindingPlan {
PassResourceBindingLocation material = {};
PassResourceBindingLocation lighting = {};
PassResourceBindingLocation shadowReceiver = {};
PassResourceBindingLocation environment = {};
PassResourceBindingLocation passConstants = {};
PassResourceBindingLocation baseColorTexture = {};
PassResourceBindingLocation sourceColorTexture = {};
PassResourceBindingLocation skyboxPanoramicTexture = {};
PassResourceBindingLocation skyboxTexture = {};
PassResourceBindingLocation linearClampSampler = {};
PassResourceBindingLocation shadowMapTexture = {};
PassResourceBindingLocation shadowMapSampler = {};
@@ -85,8 +98,13 @@ struct BuiltinPassSetLayoutMetadata {
bool usesMaterial = false;
bool usesLighting = false;
bool usesShadowReceiver = false;
bool usesEnvironment = false;
bool usesPassConstants = false;
bool usesTexture = false;
bool usesBaseColorTexture = false;
bool usesSourceColorTexture = false;
bool usesSkyboxPanoramicTexture = false;
bool usesSkyboxTexture = false;
bool usesShadowMapTexture = false;
bool usesSampler = false;
bool usesLinearClampSampler = false;

View File

@@ -1,5 +1,8 @@
#pragma once
#include <cstring>
#include <XCEngine/RHI/RHICommandList.h>
#include <XCEngine/RHI/RHITypes.h>
#include <XCEngine/Resources/Material/Material.h>
#include <XCEngine/Resources/Shader/Shader.h>
@@ -97,6 +100,28 @@ inline RHI::BlendOp ToRHIBlendOp(Resources::MaterialBlendOp op) {
}
}
inline RHI::StencilOp ToRHIStencilOp(Resources::MaterialStencilOp op) {
switch (op) {
case Resources::MaterialStencilOp::Zero:
return RHI::StencilOp::Zero;
case Resources::MaterialStencilOp::Replace:
return RHI::StencilOp::Replace;
case Resources::MaterialStencilOp::IncrSat:
return RHI::StencilOp::IncrSat;
case Resources::MaterialStencilOp::DecrSat:
return RHI::StencilOp::DecrSat;
case Resources::MaterialStencilOp::Invert:
return RHI::StencilOp::Invert;
case Resources::MaterialStencilOp::IncrWrap:
return RHI::StencilOp::Incr;
case Resources::MaterialStencilOp::DecrWrap:
return RHI::StencilOp::Decr;
case Resources::MaterialStencilOp::Keep:
default:
return RHI::StencilOp::Keep;
}
}
inline Resources::MaterialRenderState ResolveEffectiveRenderState(
const Resources::ShaderPass* shaderPass,
const Resources::Material* material) {
@@ -122,6 +147,8 @@ inline RHI::RasterizerDesc BuildRasterizerState(const Resources::MaterialRenderS
desc.frontFace = static_cast<uint32_t>(RHI::FrontFace::CounterClockwise);
desc.depthClipEnable = true;
desc.cullMode = static_cast<uint32_t>(ToRHICullMode(renderState.cullMode));
desc.depthBias = renderState.depthBiasUnits;
desc.slopeScaledDepthBias = renderState.depthBiasFactor;
return desc;
}
@@ -145,7 +172,17 @@ inline RHI::DepthStencilStateDesc BuildDepthStencilState(const Resources::Materi
desc.depthTestEnable = renderState.depthTestEnable;
desc.depthWriteEnable = renderState.depthWriteEnable;
desc.depthFunc = static_cast<uint32_t>(ToRHIComparisonFunc(renderState.depthFunc));
desc.stencilEnable = false;
desc.stencilEnable = renderState.stencil.enabled;
desc.stencilReadMask = renderState.stencil.readMask;
desc.stencilWriteMask = renderState.stencil.writeMask;
desc.front.failOp = static_cast<uint32_t>(ToRHIStencilOp(renderState.stencil.front.failOp));
desc.front.passOp = static_cast<uint32_t>(ToRHIStencilOp(renderState.stencil.front.passOp));
desc.front.depthFailOp = static_cast<uint32_t>(ToRHIStencilOp(renderState.stencil.front.depthFailOp));
desc.front.func = static_cast<uint32_t>(ToRHIComparisonFunc(renderState.stencil.front.func));
desc.back.failOp = static_cast<uint32_t>(ToRHIStencilOp(renderState.stencil.back.failOp));
desc.back.passOp = static_cast<uint32_t>(ToRHIStencilOp(renderState.stencil.back.passOp));
desc.back.depthFailOp = static_cast<uint32_t>(ToRHIStencilOp(renderState.stencil.back.depthFailOp));
desc.back.func = static_cast<uint32_t>(ToRHIComparisonFunc(renderState.stencil.back.func));
return desc;
}
@@ -167,12 +204,32 @@ inline void ApplyResolvedRenderState(
ApplyRenderState(ResolveEffectiveRenderState(shaderPass, material), pipelineDesc);
}
inline void ApplyDynamicRenderState(
const Resources::MaterialRenderState& renderState,
RHI::RHICommandList& commandList) {
if (renderState.stencil.enabled) {
commandList.SetStencilRef(renderState.stencil.reference);
}
}
inline Resources::MaterialRenderState BuildStaticPipelineRenderStateKey(
const Resources::MaterialRenderState& renderState) {
Resources::MaterialRenderState keyState = renderState;
keyState.stencil.reference = 0;
return keyState;
}
struct MaterialRenderStateHash {
size_t operator()(const Resources::MaterialRenderState& state) const noexcept {
size_t hash = 2166136261u;
auto combine = [&hash](size_t value) {
hash ^= value + 0x9e3779b9u + (hash << 6) + (hash >> 2);
};
auto combineFloat = [&combine](float value) {
Core::uint32 bits = 0;
std::memcpy(&bits, &value, sizeof(bits));
combine(static_cast<size_t>(bits));
};
combine(static_cast<size_t>(state.blendEnable));
combine(static_cast<size_t>(state.srcBlend));
@@ -186,6 +243,20 @@ struct MaterialRenderStateHash {
combine(static_cast<size_t>(state.depthWriteEnable));
combine(static_cast<size_t>(state.depthFunc));
combine(static_cast<size_t>(state.cullMode));
combineFloat(state.depthBiasFactor);
combine(static_cast<size_t>(state.depthBiasUnits));
combine(static_cast<size_t>(state.stencil.enabled));
combine(static_cast<size_t>(state.stencil.readMask));
combine(static_cast<size_t>(state.stencil.writeMask));
combine(static_cast<size_t>(state.stencil.reference));
combine(static_cast<size_t>(state.stencil.front.failOp));
combine(static_cast<size_t>(state.stencil.front.passOp));
combine(static_cast<size_t>(state.stencil.front.depthFailOp));
combine(static_cast<size_t>(state.stencil.front.func));
combine(static_cast<size_t>(state.stencil.back.failOp));
combine(static_cast<size_t>(state.stencil.back.passOp));
combine(static_cast<size_t>(state.stencil.back.depthFailOp));
combine(static_cast<size_t>(state.stencil.back.func));
return hash;
}
};

View File

@@ -102,16 +102,6 @@ public:
bool HasRenderStateOverride() const { return m_hasRenderStateOverride; }
void SetRenderStateOverrideEnabled(bool enabled);
// Legacy-only compatibility hint for older material assets that still
// select a builtin pass by serialized name.
void SetLegacyShaderPassHint(const Containers::String& shaderPass);
const Containers::String& GetLegacyShaderPassHint() const { return m_legacyShaderPassHint; }
bool HasLegacyShaderPassHint() const { return !m_legacyShaderPassHint.Empty(); }
void ClearLegacyShaderPassHint();
void SetShaderPass(const Containers::String& shaderPass);
const Containers::String& GetShaderPass() const { return GetLegacyShaderPassHint(); }
void SetTag(const Containers::String& name, const Containers::String& value);
Containers::String GetTag(const Containers::String& name) const;
bool HasTag(const Containers::String& name) const;
@@ -186,7 +176,6 @@ private:
Core::int32 m_renderQueue = static_cast<Core::int32>(MaterialRenderQueue::Geometry);
MaterialRenderState m_renderState;
bool m_hasRenderStateOverride = false;
Containers::String m_legacyShaderPassHint;
Containers::Array<MaterialTagEntry> m_tags;
ShaderKeywordSet m_keywordSet;
Containers::HashMap<Containers::String, MaterialProperty> m_properties;

View File

@@ -50,6 +50,57 @@ enum class MaterialBlendFactor : Core::uint8 {
InvSrc1Alpha = 16
};
enum class MaterialStencilOp : Core::uint8 {
Keep = 0,
Zero = 1,
Replace = 2,
IncrSat = 3,
DecrSat = 4,
Invert = 5,
IncrWrap = 6,
DecrWrap = 7
};
struct MaterialStencilFaceState {
MaterialStencilOp failOp = MaterialStencilOp::Keep;
MaterialStencilOp passOp = MaterialStencilOp::Keep;
MaterialStencilOp depthFailOp = MaterialStencilOp::Keep;
MaterialComparisonFunc func = MaterialComparisonFunc::Always;
bool operator==(const MaterialStencilFaceState& other) const {
return failOp == other.failOp &&
passOp == other.passOp &&
depthFailOp == other.depthFailOp &&
func == other.func;
}
bool operator!=(const MaterialStencilFaceState& other) const {
return !(*this == other);
}
};
struct MaterialStencilState {
bool enabled = false;
Core::uint8 readMask = 0xFF;
Core::uint8 writeMask = 0xFF;
Core::uint8 reference = 0;
MaterialStencilFaceState front = {};
MaterialStencilFaceState back = {};
bool operator==(const MaterialStencilState& other) const {
return enabled == other.enabled &&
readMask == other.readMask &&
writeMask == other.writeMask &&
reference == other.reference &&
front == other.front &&
back == other.back;
}
bool operator!=(const MaterialStencilState& other) const {
return !(*this == other);
}
};
struct MaterialRenderState {
bool blendEnable = false;
MaterialBlendFactor srcBlend = MaterialBlendFactor::One;
@@ -65,6 +116,9 @@ struct MaterialRenderState {
MaterialComparisonFunc depthFunc = MaterialComparisonFunc::Less;
MaterialCullMode cullMode = MaterialCullMode::None;
float depthBiasFactor = 0.0f;
Core::int32 depthBiasUnits = 0;
MaterialStencilState stencil = {};
bool operator==(const MaterialRenderState& other) const {
return blendEnable == other.blendEnable &&
@@ -78,7 +132,10 @@ struct MaterialRenderState {
depthTestEnable == other.depthTestEnable &&
depthWriteEnable == other.depthWriteEnable &&
depthFunc == other.depthFunc &&
cullMode == other.cullMode;
cullMode == other.cullMode &&
depthBiasFactor == other.depthBiasFactor &&
depthBiasUnits == other.depthBiasUnits &&
stencil == other.stencil;
}
bool operator!=(const MaterialRenderState& other) const {

View File

@@ -7,6 +7,7 @@
#include "Mutex.h"
#include "SpinLock.h"
#include <algorithm>
#include <memory>
#include <vector>
#include <queue>
#include <thread>
@@ -102,7 +103,9 @@ void TaskSystem::ParallelFor(int32_t start, int32_t end, Func&& func) {
}
for (auto& task : tasks) {
Submit(std::make_unique<LambdaTask<std::function<void()>>>(std::move(task), TaskPriority::High));
Submit(std::make_unique<::XCEngine::Threading::LambdaTask<std::function<void()>>>(
std::move(task),
TaskPriority::High));
}
}

View File

@@ -29,12 +29,21 @@ struct UIColor {
enum class UIDrawCommandType : std::uint8_t {
FilledRect = 0,
RectOutline,
FilledRectLinearGradient,
Line,
FilledCircle,
CircleOutline,
Text,
Image,
PushClipRect,
PopClipRect
};
enum class UILinearGradientDirection : std::uint8_t {
Horizontal = 0,
Vertical
};
struct UIDrawCommand {
UIDrawCommandType type = UIDrawCommandType::FilledRect;
UIRect rect = {};
@@ -42,10 +51,13 @@ struct UIDrawCommand {
UIPoint uvMin = {};
UIPoint uvMax = UIPoint(1.0f, 1.0f);
UIColor color = {};
UIColor secondaryColor = {};
float thickness = 1.0f;
float rounding = 0.0f;
float radius = 0.0f;
float fontSize = 0.0f;
bool intersectWithCurrentClip = true;
UILinearGradientDirection gradientDirection = UILinearGradientDirection::Horizontal;
UITextureHandle texture = {};
std::string text;
};
@@ -107,6 +119,62 @@ public:
return AddCommand(std::move(command));
}
UIDrawCommand& AddFilledRectLinearGradient(
const UIRect& rect,
const UIColor& startColor,
const UIColor& endColor,
UILinearGradientDirection direction = UILinearGradientDirection::Horizontal,
float rounding = 0.0f) {
UIDrawCommand command = {};
command.type = UIDrawCommandType::FilledRectLinearGradient;
command.rect = rect;
command.color = startColor;
command.secondaryColor = endColor;
command.rounding = rounding;
command.gradientDirection = direction;
return AddCommand(std::move(command));
}
UIDrawCommand& AddLine(
const UIPoint& start,
const UIPoint& end,
const UIColor& color,
float thickness = 1.0f) {
UIDrawCommand command = {};
command.type = UIDrawCommandType::Line;
command.position = start;
command.uvMin = end;
command.color = color;
command.thickness = thickness;
return AddCommand(std::move(command));
}
UIDrawCommand& AddFilledCircle(
const UIPoint& center,
float radius,
const UIColor& color) {
UIDrawCommand command = {};
command.type = UIDrawCommandType::FilledCircle;
command.position = center;
command.radius = radius;
command.color = color;
return AddCommand(std::move(command));
}
UIDrawCommand& AddCircleOutline(
const UIPoint& center,
float radius,
const UIColor& color,
float thickness = 1.0f) {
UIDrawCommand command = {};
command.type = UIDrawCommandType::CircleOutline;
command.position = center;
command.radius = radius;
command.color = color;
command.thickness = thickness;
return AddCommand(std::move(command));
}
UIDrawCommand& AddText(
const UIPoint& position,
std::string text,

View File

@@ -0,0 +1,32 @@
#pragma once
namespace XCEngine {
namespace UI {
namespace Widgets {
struct UIScrollWheelResult {
bool changed = false;
float overflow = 0.0f;
float offsetBefore = 0.0f;
float offsetAfter = 0.0f;
};
float ComputeUIScrollOverflow(float contentExtent, float viewportExtent);
float ClampUIScrollOffset(float offset, float contentExtent, float viewportExtent);
UIScrollWheelResult ApplyUIScrollWheel(
float offset,
float wheelDelta,
float contentExtent,
float viewportExtent,
float wheelStep = 48.0f,
float epsilon = 0.01f);
float EnsureUIScrollOffsetVisible(
float offset,
float itemStart,
float itemExtent,
float contentExtent,
float viewportExtent);
} // namespace Widgets
} // namespace UI
} // namespace XCEngine