Skip to content
Open

Ss RTX #3343

Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 182 additions & 0 deletions Content.Client/ADT/Mirror/MirrorOverlay.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
using System.Numerics;
using Content.Shared.ADT.Mirror;
using Content.Shared.Humanoid;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Shared.Enums;
using Robust.Shared.Graphics;
using Robust.Shared.Map;
using Robust.Shared.Physics;
using Robust.Shared.Prototypes;
using DrawDepth = Content.Shared.DrawDepth.DrawDepth;
using static Robust.Client.GameObjects.SpriteComponent;
using Content.Shared.Stealth.Components;

namespace Content.Client.ADT.Mirror;

public sealed partial class MirrorOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> StencilClearShader = "StencilClear";
private static readonly ProtoId<ShaderPrototype> StencilMaskShader = "StencilMask";
private static readonly ProtoId<ShaderPrototype> StencilEqualDrawShader = "StencilEqualDraw";

[Dependency] private IEntityManager _entityManager = default!;
[Dependency] private IPrototypeManager _prototypeManager = default!;
[Dependency] private IEyeManager _eyeMan = default!;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_eyeMan не используется

private SpriteSystem _sprite = default!;
private TransformSystem _transform = default!;
private ContainerSystem _container = default!;

public override OverlaySpace Space => OverlaySpace.WorldSpaceEntities;

public MirrorOverlay()
{
IoCManager.InjectDependencies(this);

ZIndex = (int)DrawDepth.BelowMobs;
}

protected override bool BeforeDraw(in OverlayDrawArgs args)
{
_sprite ??= _entityManager.System<SpriteSystem>();
_transform ??= _entityManager.System<TransformSystem>();
_container ??= _entityManager.System<ContainerSystem>();

return base.BeforeDraw(args);
}

protected override void Draw(in OverlayDrawArgs args)
{
var eye = args.Viewport.Eye;
if (eye == null)
return;

var mapId = args.MapId;
var worldAabb = args.WorldAABB;

var mirrors = _entityManager.AllEntityQueryEnumerator<MirrorComponent, SpriteComponent, TransformComponent>();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Этот цикл по сути ничего не делает, кроме лишней нагрузки. mirrorData нигде дальше не используется, кроме Count == 0. Позиции и повороты считаются, кладутся в список и выбрасываются, а цикл на строке 78 считает всё заново. На каждый кадр несколько лишних аллокаций.
Либо собирай список один раз и итерируйся по нему ниже, либо убери его совсем и делай ранний выход как-то по другому

var mirrorData = new List<(MirrorComponent Component, Vector2 Position, Angle Rotation)>();
while (mirrors.MoveNext(out var uid, out var component, out var sprite, out var transform))
{
if (transform.MapID == mapId)
{
var position = _sprite.GetSpriteWorldPosition((uid, sprite, transform));
var rotation = _transform.GetWorldRotation(transform) + sprite.Rotation;
mirrorData.Add((component, position, rotation));
}
}

if (mirrorData.Count == 0)
return;

var worldHandle = args.WorldHandle;

worldHandle.SetTransform(Matrix3x2.Identity);
worldHandle.UseShader(_prototypeManager.Index(StencilClearShader).Instance());
worldHandle.DrawRect(worldAabb, Color.White);

// Сама отрисовка начинается тут
// Каждое зеркало делает свою маску и рисует сущности, которые может
var mirrorEntities = _entityManager.AllEntityQueryEnumerator<MirrorComponent, SpriteComponent, TransformComponent>();
while (mirrorEntities.MoveNext(out var uid, out var mirror, out var sprite, out var transform))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Нет отсечения зеркал по видимости. Перебираются все зеркала на карте, включая те, что за экраном, и на каждое идёт два полноэкранных драв рект с шейдерами + полный проход рендер ентитис по всем отражаемым сущностям

{
if (transform.MapID != mapId)
continue;

worldHandle.UseShader(_prototypeManager.Index(StencilMaskShader).Instance());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_prototypeManager.Index на каждой итерации цикла Сделай три шейдер инстанса один раз в BeforeDraw и держи в полях(вроде должно работать)


_sprite.RenderSprite((uid, sprite), worldHandle, eye.Rotation, _transform.GetWorldRotation(transform),
_transform.GetWorldPosition(transform));

worldHandle.UseShader(_prototypeManager.Index(StencilEqualDrawShader).Instance());
RenderEntities(worldAabb, eye, worldHandle, mapId,
(mirror, _sprite.GetSpriteWorldPosition((uid, sprite, transform)),
_transform.GetWorldRotation(transform) + sprite.Rotation));

worldHandle.UseShader(_prototypeManager.Index(StencilClearShader).Instance());
worldHandle.SetTransform(Matrix3x2.Identity);
worldHandle.DrawRect(worldAabb, Color.White);
}

worldHandle.UseShader(null);
}

private void RenderEntities(Box2 worldAabb, IEye eye, DrawingHandleWorld worldHandle, MapId mapId,
(MirrorComponent Component, Vector2 Position, Angle Rotation) mirrorData)
{
var entities = _entityManager.AllEntityQueryEnumerator<MirrorReflectionComponent, SpriteComponent, TransformComponent>();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

перебираются все сущности с миррор рефлекшин для КАЖДОГО зеркала, которое ща есть в пвс у игрока.
Соответственно выходит O(зеркала*мобы).
Лучше сделать EntityLookupSystem.GetEntitiesInRange вокруг зеркала вместо полного энумератора

while (entities.MoveNext(out var uid, out var reflection, out var sprite, out var transform))
{
if (_entityManager.HasComponent<MirrorComponent>(uid) || transform.MapID != mapId)
continue;

if (!reflection.ReflectIfInvisible && _entityManager.HasComponent<StealthComponent>(uid))
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
continue;

var (mirror, mirrorPosition, mirrorRotation) = mirrorData;
var sourcePosition = _transform.GetWorldPosition(transform);
if (!worldAabb.Contains(sourcePosition) || _container.IsEntityInContainer(uid))
continue;

var normalAngle = mirrorRotation + Angle.FromDegrees(mirror.DirRotation);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Все эти данные т.е. позиция, градус и тд зависят от зеркала, которое обычно статичное и никак не изменяется, но при этом на каждой итерации все равно пересчитывается заново

var normal = normalAngle.ToVec().Normalized();
var entitySide = Vector2.Dot(sourcePosition - mirrorPosition, normal);
var viewerSide = Vector2.Dot(eye.Position.Position - mirrorPosition, normal);
if (entitySide * viewerSide <= 0f)
continue;

if (mirror.FadeFactor > 0 &&
Vector2.Distance(sourcePosition, mirrorPosition) >= mirror.GatherOffset + 1f / mirror.FadeFactor)
{
continue;
}

var color = sprite.Color;
var newColor = GetTransparentColor(uid, color, mirrorPosition, mirror.ToleratedDistance, mirror.FadeFactor);
_sprite.SetColor(uid, newColor);

var offsetSourcePosition = sourcePosition + normal * mirror.GatherOffset;
var reflectedPosition = offsetSourcePosition - 2f * Vector2.Dot(offsetSourcePosition - mirrorPosition, normal) * normal;
var reflectedFacing = normalAngle * 2f - _transform.GetWorldRotation(transform);

// Этот ебучий слой ломал вообще всё

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Комментарий - есть
Маты - есть
Просьба не убирать - есть
Объяснений почему этот фикс нужен - нет

// Не убирайте этот фикс
var hiddenStencilLayers = new List<(ISpriteLayer Layer, bool Visible)>();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new List<...> на каждую сущность на каждое зеркало каждый кадр, хотя слоя максимум два

if (_sprite.LayerMapTryGet((uid, sprite), HumanoidVisualLayers.StencilMask, out var stencilMaskLayer, false))
{
var stencilMask = sprite[stencilMaskLayer];
hiddenStencilLayers.Add((stencilMask, stencilMask.Visible));
stencilMask.Visible = false;

if (stencilMaskLayer > 0)
{
var stencilClear = sprite[stencilMaskLayer - 1];
hiddenStencilLayers.Add((stencilClear, stencilClear.Visible));
stencilClear.Visible = false;
}
}

_sprite.RenderSprite((uid, sprite), worldHandle, eye.Rotation, reflectedFacing,
reflectedPosition - normal * mirror.ReflectionOffset);

foreach (var (layer, visible) in hiddenStencilLayers)
layer.Visible = visible;

worldHandle.UseShader(_prototypeManager.Index(StencilEqualDrawShader).Instance());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Избыточная повторная индексация прототипа

_sprite.SetColor(uid, color);
}
}

private Color GetTransparentColor(EntityUid uid, Color originalColor, Vector2 mirrorPos, float toleratedDistance, float fadeFactorMod)
{
var dist = (_transform.GetWorldPosition(uid) - mirrorPos).Length();

var fadeFactor = MathF.Max(dist - toleratedDistance, 0f);
return originalColor.WithAlpha(Math.Clamp(originalColor.A - fadeFactor * fadeFactorMod, 0f, 0.9f));
}

protected override void DisposeBehavior()
{

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это не надо.

base.DisposeBehavior();
}
}
15 changes: 15 additions & 0 deletions Content.Client/ADT/Mirror/MirrorSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Robust.Client.Graphics;

namespace Content.Client.ADT.Mirror;

public sealed partial class MirrorSystem : EntitySystem
{
[Dependency] private IOverlayManager _overlay = default!;

public override void Initialize()
{
base.Initialize();

_overlay.AddOverlay(new MirrorOverlay());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Оверлей не снимается при шутдауне.

}
}
22 changes: 22 additions & 0 deletions Content.Shared/ADT/Mirror/MirrorComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Robust.Shared.GameStates;

namespace Content.Shared.ADT.Mirror;

[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Нетворкед + авто генерейт компонент стейт лишние.

public sealed partial class MirrorComponent : Component
{
[DataField, AutoNetworkedField]
public float DirRotation = 90f;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Перевести из float в Angle


[DataField, AutoNetworkedField]
public float GatherOffset = 1f;

[DataField, AutoNetworkedField]
public float ReflectionOffset = 0.2f;

[DataField, AutoNetworkedField]
public float FadeFactor = 1f;

[DataField, AutoNetworkedField]
public float ToleratedDistance = 1f;
}
10 changes: 10 additions & 0 deletions Content.Shared/ADT/Mirror/ReflectionComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Robust.Shared.GameStates;

namespace Content.Shared.ADT.Mirror;

[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class MirrorReflectionComponent : Component

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Файл называется Reflection, а компонент MirrorReflection. Приведи к единому.

{
[DataField, AutoNetworkedField]
public bool ReflectIfInvisible = false;
}
1 change: 1 addition & 0 deletions Resources/Prototypes/ADT/Body/Species/ipc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@
- type: AnimatedEmotes
- type: Posing
- type: SoftWalk
- type: MirrorReflection

- type: entity
parent: OrganBaseOrganic
Expand Down
3 changes: 2 additions & 1 deletion Resources/Prototypes/Entities/Mobs/base.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
- type: MobMover
- type: MovementSpeedModifier
- type: LagCompensation
- type: MirrorReflection # ADT-Tweak
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

- type: entity
save: false
Expand Down Expand Up @@ -299,4 +300,4 @@
# ADT-Tweak start
bloodRefreshAmount: 0.3
bleedReductionAmount: 0.3
# ADT-Tweak end
# ADT-Tweak end
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@
- !type:PlaySoundBehavior
sound:
collection: MetalGlassBreak
# ADT-Tweak-start
- type: Mirror
reflectionOffset: 0.35
toleratedDistance: 1.2
# ADT-Tweak-end

- type: entity
parent: Mirror
Expand All @@ -43,3 +48,7 @@
components:
- type: Sprite
state: rectangle-mirror
# ADT-Tweak-start
- type: Mirror
reflectionOffset: 0.4
# ADT-Tweak-end
Loading