Skip to content
Open

Ss RTX #3343

Show file tree
Hide file tree
Changes from 9 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
7 changes: 7 additions & 0 deletions Content.Client/ADT/Mirror/CanBeSeenInMirrorsEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Content.Client.ADT.Mirror;

[ByRefEvent]
public record struct CanBeSeenInMirrorsEvent()
{
public bool Cancelled = false;
}
194 changes: 194 additions & 0 deletions Content.Client/ADT/Mirror/MirrorOverlay.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
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.Prototypes;
using DrawDepth = Content.Shared.DrawDepth.DrawDepth;
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))
{
var (mirror, mirrorPosition, mirrorRotation) = mirrorData;
var sourcePosition = _transform.GetWorldPosition(transform);
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();

if (!CanReflect(uid, reflection, transform, mirrorData, normal, mapId, eye, worldAabb))
continue;

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

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

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;
}
}

var offsetSourcePosition = sourcePosition + normal * mirror.GatherOffset;
var reflectedPosition = offsetSourcePosition - 2f * Vector2.Dot(offsetSourcePosition - mirrorPosition, normal) * normal;
var reflectedFacing = normalAngle * 2f - _transform.GetWorldRotation(transform);
Comment thread
Inconnu1337 marked this conversation as resolved.
Outdated

_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 bool CanReflect(EntityUid uid, MirrorReflectionComponent reflection, TransformComponent transform,
(MirrorComponent, Vector2, Angle) mirrorData, Vector2 normal,

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.

Безымянный кортеж (MirrorComponent, Vector2, Angle)
Лучше сделай чет типа readonly record struct MirrorData(MirrorComponent Comp, Vector2 Position, Angle Rotation)

MapId mapId, IEye eye, Box2 worldAabb)
{
if (_entityManager.HasComponent<MirrorComponent>(uid) || transform.MapID != mapId)

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.

Порядок подобных проверок должен идти от дешевых к дорогим.
Active > MapID > AABB > дистанция > HasComponent/IsEntityInContaine

return false;

if (!reflection.Active)
return false;

if (!reflection.ReflectIfInvisible && _entityManager.TryGetComponent<StealthComponent>(uid, out var stealth) && stealth.Enabled)
return false;

var sourcePosition = _transform.GetWorldPosition(transform);
if (!worldAabb.Contains(sourcePosition) || _container.IsEntityInContainer(uid))
return false;

var (mirror, mirrorPosition, _) = mirrorData;

var entitySide = Vector2.Dot(sourcePosition - mirrorPosition, normal);
var viewerSide = Vector2.Dot(eye.Position.Position - mirrorPosition, normal);
if (entitySide * viewerSide <= 0f)
return false;

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

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.

скви sqrt опять на каждую сущность, на каждое зеркало. Проще сравнивать квадраты.

return false;

return true;
}

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();
}
}
30 changes: 30 additions & 0 deletions Content.Client/ADT/Mirror/MirrorSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Content.Shared.ADT.Mirror;
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.

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

}

public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);

var query = EntityQueryEnumerator<MirrorReflectionComponent>();
while (query.MoveNext(out var uid, out var comp))
{
var ev = new CanBeSeenInMirrorsEvent();

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.

RaiseLocalEvent для каждой сущности с MirrorReflectionComponent каждый кадр, а так как компач есть у BaseMob - будет пиздец как дохуя сетевого трафика лишнего.

Ивент CanBeSeenInMirrorsEvent при этом нигде не юзается.
Лучше считать Active лениво прямо в CanReflect, только для сущностей реально попавших в кадр зеркала, и кэшировать результат на кадр

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

RaiseLocalEvent для каждой сущности с MirrorReflectionComponent каждый кадр, а так как компач есть у BaseMob - будет пиздец как дохуя сетевого трафика лишнего.

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

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.

Подчеркну еще раз -
RaiseLocalEvent вызывается на каждой, абсолютно каждой сущности, которая имеет компонент миррор рефлекшин, а так как компач у BaseMob - на всех мобов соответственно каждый тик. Т.е. 60 раз в секунду умножить на количество мобов вроде так

При этом ивент нигде не используется

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.

Это самая не оптимизированная часть во всем коде

RaiseLocalEvent(uid, ref ev);

comp.Active = !ev.Cancelled;
}
}
}
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;
}
13 changes: 13 additions & 0 deletions Content.Shared/ADT/Mirror/ReflectionComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
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;

[ViewVariables]
public bool Active = true;
}
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: functional mirrors

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.

Если не ошибаюсь, то госты тоже получат этот компонент

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Да, но госты по идее не существуют на клиенте в обычных обстоятельствах


- 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