From b7e224a56f4ae4e44519cb9a6c80cc082a2805cb Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Mon, 13 Jul 2020 16:27:43 +0200 Subject: [PATCH] Moving a ton of rendering code around and it somehow still works somewhat. --- .../Graphics/Clyde/Clyde.GridRendering.cs | 2 +- Robust.Client/Graphics/Clyde/Clyde.HLR.cs | 144 +++++++++++------ .../Graphics/Clyde/Clyde.LightRendering.cs | 152 ++++++++---------- .../Graphics/Clyde/Clyde.RenderTargets.cs | 32 +++- .../Graphics/Clyde/Clyde.Rendering.cs | 3 +- .../Graphics/Clyde/Clyde.Textures.cs | 33 ++-- .../Graphics/Clyde/Clyde.Viewport.cs | 57 ++++++- .../Graphics/Clyde/Clyde.Windowing.cs | 3 +- Robust.Client/Graphics/Clyde/Clyde.cs | 6 +- Robust.Client/Graphics/Clyde/ClydeHeadless.cs | 8 +- Robust.Client/Graphics/Texture.cs | 2 +- Robust.Client/Interfaces/Graphics/IClyde.cs | 2 +- .../Interfaces/Graphics/IClydeViewport.cs | 5 +- 13 files changed, 278 insertions(+), 171 deletions(-) diff --git a/Robust.Client/Graphics/Clyde/Clyde.GridRendering.cs b/Robust.Client/Graphics/Clyde/Clyde.GridRendering.cs index be12d10a0a..74677d69ad 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.GridRendering.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.GridRendering.cs @@ -26,7 +26,7 @@ namespace Robust.Client.Graphics.Clyde } SetTexture(TextureUnit.Texture0, _tileDefinitionManager.TileTextureAtlas); - SetTexture(TextureUnit.Texture1, _lightingReady ? _lightRenderTarget.Texture : _stockTextureWhite); + SetTexture(TextureUnit.Texture1, _lightingReady ? _currentViewport!.LightRenderTarget.Texture : _stockTextureWhite); var (gridProgram, _) = ActivateShaderInstance(_defaultShader.Handle); diff --git a/Robust.Client/Graphics/Clyde/Clyde.HLR.cs b/Robust.Client/Graphics/Clyde/Clyde.HLR.cs index d1aa68decd..2de87d9f06 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.HLR.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.HLR.cs @@ -1,6 +1,6 @@ using System; using System.Buffers; -using System.Linq; +using System.Collections.Generic; using System.Runtime.CompilerServices; using Robust.Client.GameObjects; using Robust.Client.GameObjects.EntitySystems; @@ -60,62 +60,24 @@ namespace Robust.Client.Graphics.Clyde return; } - void RenderOverlays(OverlaySpace space) - { - using (DebugGroup($"Overlays: {space}")) - { - foreach (var overlay in _overlayManager.AllOverlays - .Where(o => o.Space == space) - .OrderBy(o => o.ZIndex)) - { - overlay.ClydeRender(_renderHandle); - } - - FlushRenderQueue(); - } - } - RenderOverlays(OverlaySpace.ScreenSpaceBelowWorld); SetSpaceFull(CurrentSpace.WorldSpace); - // Calculate world-space AABB for camera, to cull off-screen things. - var eye = _eyeManager.CurrentEye; - var worldBounds = Box2.CenteredAround(eye.Position.Position, - _framebufferSize / (float) EyeManager.PixelsPerMeter * eye.Zoom); - - if (_eyeManager.CurrentMap != MapId.Nullspace) - { - using (DebugGroup("Lights")) - { - DrawLightsAndFov(worldBounds, eye); - } - - using (DebugGroup("Grids")) - { - _drawGrids(worldBounds); - } - - using (DebugGroup("Entities")) - { - DrawEntities(worldBounds); - } - - RenderOverlays(OverlaySpace.WorldSpace); - - if (_lightManager.Enabled && eye.DrawFov) - { - ApplyFovToBuffer(eye); - } - } - - _lightingReady = false; + RenderViewport(_mainViewport); SetSpaceFull(CurrentSpace.ScreenSpace); + // TODO: Re-enable this + /* if (DebugLayers == ClydeDebugLayers.Fov) { + // I'm refactoring this code and I found this comment: // NOTE + // Yes, it just says "NOTE". Thank you past me. + // Anyways I'm 99% sure this was about the fact that this debug layer is actually broken. + // Because the math is wrong. + // So there are distortions from incorrect projection. _renderHandle.UseShader(_fovDebugShaderInstance); _renderHandle.DrawingHandleScreen.SetTransform(Matrix3.Identity); var pos = UIBox2.FromDimensions(ScreenSize / 2 - (200, 200), (400, 400)); @@ -128,6 +90,7 @@ namespace Robust.Client.Graphics.Clyde _renderHandle.DrawingHandleScreen.SetTransform(Matrix3.Identity); _renderHandle.DrawingHandleScreen.DrawTextureRect(_wallBleedIntermediateRenderTarget2.Texture, UIBox2.FromDimensions(Vector2.Zero, ScreenSize), new Color(1, 1, 1, 0.5f)); } + */ TakeScreenshot(ScreenshotType.BeforeUI); @@ -145,13 +108,40 @@ namespace Robust.Client.Graphics.Clyde SwapBuffers(); } - private void DrawEntities(Box2 worldBounds) + private void RenderOverlays(OverlaySpace space) + { + using (DebugGroup($"Overlays: {space}")) + { + var list = new List(); + + foreach (var overlay in _overlayManager.AllOverlays) + { + if (overlay.Space == space) + { + list.Add(overlay); + } + } + + list.Sort(OverlayComparer.Instance); + + foreach (var overlay in list) + { + overlay.ClydeRender(_renderHandle); + } + + FlushRenderQueue(); + } + } + + private void DrawEntities(Viewport viewport, Box2 worldBounds) { if (_eyeManager.CurrentMap == MapId.Nullspace || !_mapManager.HasMapEntity(_eyeManager.CurrentMap)) { return; } + var screenSize = viewport.Size; + // So we could calculate the correct size of the entities based on the contents of their sprite... // Or we can just assume that no entity is larger than 10x10 and get a stupid easy check. // TODO: Make this check more accurate. @@ -183,9 +173,9 @@ namespace Robust.Client.Graphics.Clyde var spritePos = entry.sprite.Owner.Transform.WorldPosition; var screenPos = _eyeManager.WorldToScreen(spritePos); var (roundedX, roundedY) = roundedPos = (Vector2i) screenPos; - var flippedPos = new Vector2i(roundedX, ScreenSize.Y - roundedY); + var flippedPos = new Vector2i(roundedX, screenSize.Y - roundedY); flippedPos -= EntityPostRenderTarget.Size / 2; - _renderHandle.Viewport(Box2i.FromDimensions(-flippedPos, ScreenSize)); + _renderHandle.Viewport(Box2i.FromDimensions(-flippedPos, screenSize)); } entry.sprite.Render(_renderHandle.DrawingHandleWorld, entry.worldMatrix, entry.worldRotation); @@ -193,7 +183,7 @@ namespace Robust.Client.Graphics.Clyde if (entry.sprite.PostShader != null) { _renderHandle.UseRenderTarget(null); - _renderHandle.Viewport(Box2i.FromDimensions(Vector2i.Zero, ScreenSize)); + _renderHandle.Viewport(Box2i.FromDimensions(Vector2i.Zero, screenSize)); _renderHandle.UseShader(entry.sprite.PostShader); _renderHandle.SetSpace(CurrentSpace.ScreenSpace); @@ -251,5 +241,57 @@ namespace Robust.Client.Graphics.Clyde handle.DrawingHandleScreen.DrawTexture(texture, (ScreenSize - texture.Size) / 2); } + + private void RenderViewport(Viewport viewport) + { + var oldVp = _currentViewport; + _currentViewport = viewport; + + // Calculate world-space AABB for camera, to cull off-screen things. + var eye = _eyeManager.CurrentEye; + var worldBounds = Box2.CenteredAround(eye.Position.Position, + _framebufferSize / (float) EyeManager.PixelsPerMeter * eye.Zoom); + + if (_eyeManager.CurrentMap != MapId.Nullspace) + { + using (DebugGroup("Lights")) + { + DrawLightsAndFov(viewport, worldBounds, eye); + } + + using (DebugGroup("Grids")) + { + _drawGrids(worldBounds); + } + + using (DebugGroup("Entities")) + { + DrawEntities(viewport, worldBounds); + } + + RenderOverlays(OverlaySpace.WorldSpace); + + if (_lightManager.Enabled && eye.DrawFov) + { + ApplyFovToBuffer(viewport, eye); + } + } + + _lightingReady = false; + + _currentViewport = oldVp; + } + + private sealed class OverlayComparer : IComparer + { + public static readonly OverlayComparer Instance = new OverlayComparer(); + + public int Compare(Overlay x, Overlay y) + { + var zX = x.ZIndex ?? 0; + var zY = y.ZIndex ?? 0; + return zX.CompareTo(zY); + } + } } } diff --git a/Robust.Client/Graphics/Clyde/Clyde.LightRendering.cs b/Robust.Client/Graphics/Clyde/Clyde.LightRendering.cs index 387ba06cc1..408d5b5512 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.LightRendering.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.LightRendering.cs @@ -46,30 +46,6 @@ namespace Robust.Client.Graphics.Clyde // We keep this around so we can reverse the effects while overlaying actual FOV. private Matrix4 _fovProjection; - // Various render targets used in the light rendering process. - - // Lighting is drawn into this. This then gets sampled later while rendering world-space stuff. - private RenderTarget _lightRenderTarget = default!; - - // For depth calculation for FOV. - private RenderTarget _fovRenderTarget = default!; - - // For depth calculation of lighting shadows. - private RenderTarget _shadowRenderTarget = default!; - - // Unused, to be removed. - private RenderTarget _wallMaskRenderTarget = default!; - - // Two render targets used to apply gaussian blur to the _lightRenderTarget so it bleeds "into" walls. - // We need two of them because efficient blur works in two stages and also we're doing multiple iterations. - private RenderTarget _wallBleedIntermediateRenderTarget1 = default!; - private RenderTarget _wallBleedIntermediateRenderTarget2 = default!; - - // Proxies to textures of some of the above render targets. - private ClydeTexture FovTexture => _fovRenderTarget.Texture; - - private ClydeTexture ShadowTexture => _shadowRenderTarget.Texture; - // Sampler used to sample the FovTexture with linear filtering, used in the lighting FOV pass // (it uses VSM unlike final FOV). private GLHandle _fovFilterSampler; @@ -100,12 +76,21 @@ namespace Robust.Client.Graphics.Clyde private GLBuffer _occlusionMaskEbo = default!; private GLHandle _occlusionMaskVao; + // For depth calculation for FOV. + private RenderTarget _fovRenderTarget = default!; + + // For depth calculation of lighting shadows. + private RenderTarget _shadowRenderTarget = default!; + + // Proxies to textures of the above render targets. + private ClydeTexture FovTexture => _fovRenderTarget.Texture; + private ClydeTexture ShadowTexture => _shadowRenderTarget.Texture; + + private unsafe void InitLighting() { LoadLightingShaders(); - RegenerateLightingRenderTargets(); - { // Occlusion VAO. // Only handles positions, no other vertex data necessary. @@ -194,7 +179,7 @@ namespace Robust.Client.Graphics.Clyde _mergeWallLayerShaderHandle = LoadShaderHandle("/Shaders/Internal/wall-merge.swsl"); } - private void DrawFov(IEye eye) + private void DrawFov(Viewport viewport, IEye eye) { using var _ = DebugGroup(nameof(DrawFov)); @@ -203,7 +188,7 @@ namespace Robust.Client.Graphics.Clyde if (eye.DrawFov) { // Calculate maximum distance for the projection based on screen size. - var screenSizeCut = ScreenSize / EyeManager.PixelsPerMeter; + var screenSizeCut = viewport.Size / EyeManager.PixelsPerMeter; var maxDist = (float) Math.Max(screenSizeCut.X, screenSizeCut.Y); // FOV is rendered twice. @@ -312,7 +297,7 @@ namespace Robust.Client.Graphics.Clyde GL.Disable(EnableCap.CullFace); } - private void DrawLightsAndFov(Box2 worldBounds, IEye eye) + private void DrawLightsAndFov(Viewport viewport, Box2 worldBounds, IEye eye) { if (!_lightManager.Enabled) { @@ -325,7 +310,7 @@ namespace Robust.Client.Graphics.Clyde UpdateOcclusionGeometry(map, expandedBounds, eye.Position.Position); - DrawFov(eye); + DrawFov(viewport, eye); var shadowMatrices = new Matrix4[lights.Count]; @@ -347,11 +332,11 @@ namespace Robust.Client.Graphics.Clyde FinalizeDepthDraw(); } - GL.BindFramebuffer(FramebufferTarget.Framebuffer, _lightRenderTarget.ObjectHandle.Handle); + GL.BindFramebuffer(FramebufferTarget.Framebuffer, viewport.LightRenderTarget.ObjectHandle.Handle); GLClearColor(Color.FromSrgb(AmbientLightColor)); GL.Clear(ClearBufferMask.ColorBufferBit); - var (lightW, lightH) = GetLightMapSize(); + var (lightW, lightH) = GetLightMapSize(viewport.Size); GL.Viewport(0, 0, lightW, lightH); var lightShader = _loadedShaders[_lightShaderHandle].Program; @@ -442,14 +427,14 @@ namespace Robust.Client.Graphics.Clyde GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); - ApplyLightingFovToBuffer(eye); + ApplyLightingFovToBuffer(viewport, eye); - BlurOntoWalls(eye); + BlurOntoWalls(viewport, eye); - MergeWallLayer(); + MergeWallLayer(viewport); GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0); - GL.Viewport(0, 0, ScreenSize.X, ScreenSize.Y); + GL.Viewport(0, 0, viewport.Size.X, viewport.Size.Y); _lightingReady = true; } @@ -493,7 +478,7 @@ namespace Robust.Client.Graphics.Clyde return (lights, expandedBounds); } - private void BlurOntoWalls(IEye eye) + private void BlurOntoWalls(Viewport viewport, IEye eye) { using var _ = DebugGroup(nameof(BlurOntoWalls)); @@ -503,20 +488,20 @@ namespace Robust.Client.Graphics.Clyde var shader = _loadedShaders[_wallBleedBlurShaderHandle].Program; shader.Use(); - shader.SetUniformMaybe("size", (Vector2) _wallBleedIntermediateRenderTarget1.Size); + shader.SetUniformMaybe("size", (Vector2) viewport.WallBleedIntermediateRenderTarget1.Size); shader.SetUniformTextureMaybe(UniIMainTexture, TextureUnit.Texture0); - var size = _wallBleedIntermediateRenderTarget1.Size; + var size = viewport.WallBleedIntermediateRenderTarget1.Size; GL.Viewport(0, 0, size.X, size.Y); // Initially we're pulling from the light render target. // So we set it out of the loop so // _wallBleedIntermediateRenderTarget2 gets bound at the end of the loop body. - SetTexture(TextureUnit.Texture0, _lightRenderTarget.Texture); + SetTexture(TextureUnit.Texture0, viewport.LightRenderTarget.Texture); // Have to scale the blurring radius based on viewport size and camera zoom. const float refCameraHeight = 14; - var cameraSize = eye.Zoom.Y * ScreenSize.Y / EyeManager.PixelsPerMeter; + var cameraSize = eye.Zoom.Y * viewport.Size.Y / EyeManager.PixelsPerMeter; // 7e-3f is just a magic factor that makes it look ok. var factor = 7e-3f * (refCameraHeight / cameraSize); @@ -527,39 +512,39 @@ namespace Robust.Client.Graphics.Clyde // Set factor. shader.SetUniformMaybe("radius", scale); - _wallBleedIntermediateRenderTarget1.Bind(); + viewport.WallBleedIntermediateRenderTarget1.Bind(); // Blur horizontally to _wallBleedIntermediateRenderTarget1. shader.SetUniformMaybe("direction", Vector2.UnitX); - _drawQuad(Vector2.Zero, ScreenSize, Matrix3.Identity, shader); + _drawQuad(Vector2.Zero, viewport.Size, Matrix3.Identity, shader); - SetTexture(TextureUnit.Texture0, _wallBleedIntermediateRenderTarget1.Texture); - _wallBleedIntermediateRenderTarget2.Bind(); + SetTexture(TextureUnit.Texture0, viewport.WallBleedIntermediateRenderTarget1.Texture); + viewport.WallBleedIntermediateRenderTarget2.Bind(); // Blur vertically to _wallBleedIntermediateRenderTarget2. shader.SetUniformMaybe("direction", Vector2.UnitY); - _drawQuad(Vector2.Zero, ScreenSize, Matrix3.Identity, shader); + _drawQuad(Vector2.Zero, viewport.Size, Matrix3.Identity, shader); - SetTexture(TextureUnit.Texture0, _wallBleedIntermediateRenderTarget2.Texture); + SetTexture(TextureUnit.Texture0, viewport.WallBleedIntermediateRenderTarget2.Texture); } GL.Enable(EnableCap.Blend); _setSpace(CurrentSpace.WorldSpace); } - private void MergeWallLayer() + private void MergeWallLayer(Viewport viewport) { using var _ = DebugGroup(nameof(MergeWallLayer)); - _lightRenderTarget.Bind(); + viewport.LightRenderTarget.Bind(); - GL.Viewport(0, 0, _lightRenderTarget.Size.X, _lightRenderTarget.Size.Y); + GL.Viewport(0, 0, viewport.LightRenderTarget.Size.X, viewport.LightRenderTarget.Size.Y); GL.Disable(EnableCap.Blend); var shader = _loadedShaders[_mergeWallLayerShaderHandle].Program; shader.Use(); - var tex = _wallBleedIntermediateRenderTarget2.Texture; + var tex = viewport.WallBleedIntermediateRenderTarget2.Texture; SetTexture(TextureUnit.Texture0, tex); shader.SetUniformTextureMaybe(UniIMainTexture, TextureUnit.Texture0); @@ -572,7 +557,7 @@ namespace Robust.Client.Graphics.Clyde GL.Enable(EnableCap.Blend); } - private void ApplyFovToBuffer(IEye eye) + private void ApplyFovToBuffer(Viewport viewport, IEye eye) { // Applies FOV to the final framebuffer. @@ -585,10 +570,10 @@ namespace Robust.Client.Graphics.Clyde fovShader.SetUniformMaybe("shadowMatrix", _fovProjection, false); fovShader.SetUniformMaybe("center", eye.Position.Position); - DrawBlit(fovShader); + DrawBlit(viewport.Size, fovShader); } - private void ApplyLightingFovToBuffer(IEye eye) + private void ApplyLightingFovToBuffer(Viewport viewport, IEye eye) { // Applies FOV to the lighting framebuffer. @@ -605,15 +590,15 @@ namespace Robust.Client.Graphics.Clyde fovShader.SetUniformMaybe("shadowMatrix", _fovProjection, false); fovShader.SetUniformMaybe("center", eye.Position.Position); - DrawBlit(fovShader); + DrawBlit(viewport.Size, fovShader); GL.BindSampler(0, 0); } - private void DrawBlit(GLShaderProgram shader) + private void DrawBlit(Vector2i screenSize, GLShaderProgram shader) { _drawQuad(_eyeManager.ScreenToMap((-1, -1)).Position, - _eyeManager.ScreenToMap(ScreenSize + Vector2i.One).Position, + _eyeManager.ScreenToMap(screenSize + Vector2i.One).Position, Matrix3.Identity, shader); } @@ -811,46 +796,54 @@ namespace Robust.Client.Graphics.Clyde } } - private void RegenerateLightingRenderTargets() + private void RegenLightRts(Viewport viewport) { // All of these depend on screen size so they have to be re-created if it changes. - var lightMapSize = GetLightMapSize(); - var lightMapSizeQuart = GetLightMapSize(true); + var lightMapSize = GetLightMapSize(viewport.Size); + var lightMapSizeQuart = GetLightMapSize(viewport.Size, true); const RenderTargetColorFormat lightMapColorFormat = RenderTargetColorFormat.R11FG11FB10F; var lightMapSampleParameters = new TextureSampleParameters {Filter = true}; - _lightRenderTarget?.Delete(); - _wallMaskRenderTarget?.Delete(); - _wallBleedIntermediateRenderTarget1?.Delete(); - _wallBleedIntermediateRenderTarget2?.Delete(); + viewport.LightRenderTarget?.Delete(); + viewport.WallMaskRenderTarget?.Delete(); + viewport.WallBleedIntermediateRenderTarget1?.Delete(); + viewport.WallBleedIntermediateRenderTarget2?.Delete(); - _wallMaskRenderTarget = CreateRenderTarget(ScreenSize, RenderTargetColorFormat.R8, - name: nameof(_wallMaskRenderTarget)); + viewport.WallMaskRenderTarget = CreateRenderTarget(viewport.Size, RenderTargetColorFormat.R8, + name: $"{viewport.Name}-{nameof(viewport.WallMaskRenderTarget)}"); - _lightRenderTarget = CreateRenderTarget(lightMapSize, lightMapColorFormat, + viewport.LightRenderTarget = CreateRenderTarget(lightMapSize, lightMapColorFormat, lightMapSampleParameters, - nameof(_lightRenderTarget)); + $"{viewport.Name}-{nameof(viewport.LightRenderTarget)}"); - _wallBleedIntermediateRenderTarget1 = CreateRenderTarget(lightMapSizeQuart, lightMapColorFormat, + viewport.WallBleedIntermediateRenderTarget1 = CreateRenderTarget(lightMapSizeQuart, lightMapColorFormat, lightMapSampleParameters, - nameof(_wallBleedIntermediateRenderTarget1)); + $"{viewport.Name}-{nameof(viewport.WallBleedIntermediateRenderTarget1)}"); - _wallBleedIntermediateRenderTarget2 = CreateRenderTarget(lightMapSizeQuart, lightMapColorFormat, + viewport.WallBleedIntermediateRenderTarget2 = CreateRenderTarget(lightMapSizeQuart, lightMapColorFormat, lightMapSampleParameters, - nameof(_wallBleedIntermediateRenderTarget2)); + $"{viewport.Name}-{nameof(viewport.WallBleedIntermediateRenderTarget2)}"); } - private Vector2i GetLightMapSize(bool? overrideSetting = null) + private void RegenAllLightRts() + { + foreach (var viewport in _viewports) + { + RegenLightRts(viewport); + } + } + + private Vector2i GetLightMapSize(Vector2i screenSize, bool? overrideSetting = null) { var setting = overrideSetting ?? _quartResLights; if (!setting) { - return (ScreenSize.X, ScreenSize.Y); + return screenSize; } - var w = (int) Math.Ceiling(ScreenSize.X / 2f); - var h = (int) Math.Ceiling(ScreenSize.Y / 2f); + var w = (int) Math.Ceiling(screenSize.X / 2f); + var h = (int) Math.Ceiling(screenSize.Y / 2f); return (w, h); } @@ -858,12 +851,7 @@ namespace Robust.Client.Graphics.Clyde protected override void HighResLightsChanged(bool newValue) { _quartResLights = !newValue; - if (_lightRenderTarget == null) - { - return; - } - - RegenerateLightingRenderTargets(); + RegenAllLightRts(); } } } diff --git a/Robust.Client/Graphics/Clyde/Clyde.RenderTargets.cs b/Robust.Client/Graphics/Clyde/Clyde.RenderTargets.cs index 63ece86520..7f9c32c576 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.RenderTargets.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.RenderTargets.cs @@ -112,6 +112,30 @@ namespace Robust.Client.Graphics.Clyde } } + /* + This was a dumb idea: + + private void ResizeRenderTarget(RenderTarget rt, Vector2i newSize) + { + var loadedTexture = _loadedTextures[rt.Texture.TextureId]; + var textureInstance = rt.Texture; + + // Set new sizes. + textureInstance.SetSize(newSize); + rt.Size = newSize; + + // Delete old textures. + GL.DeleteTexture(loadedTexture.OpenGLObject.Handle); + if (rt.DepthStencilBuffer != default) + { + GL.DeleteRenderbuffer(rt.DepthStencilBuffer.Handle); + } + + // Delete the entire old framebuffer because bad OpenGL drivers will just explode otherwise. + GL.DeleteFramebuffer(rt.ObjectHandle.Handle); + } + */ + private sealed class RenderTarget : IRenderTarget { private readonly Clyde _clyde; @@ -127,7 +151,7 @@ namespace Robust.Client.Graphics.Clyde DepthStencilBuffer = depthStencilBuffer; } - public Vector2i Size { get; } + public Vector2i Size { get; set; } public ClydeTexture Texture { get; } public ClydeHandle Handle { get; } public GLHandle DepthStencilBuffer { get; } @@ -136,6 +160,12 @@ namespace Robust.Client.Graphics.Clyde public GLHandle ObjectHandle { get; } + /* + // Used to recreate the render target on resize. + public RenderTargetFormatParameters FormatParameters; + public TextureSampleParameters? SampleParameters; + */ + public void Delete() { _clyde.DeleteRenderTarget(this); diff --git a/Robust.Client/Graphics/Clyde/Clyde.Rendering.cs b/Robust.Client/Graphics/Clyde/Clyde.Rendering.cs index e41e087b5d..47064860dd 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.Rendering.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.Rendering.cs @@ -31,6 +31,7 @@ namespace Robust.Client.Graphics.Clyde private CurrentSpace _currentSpace; private bool _lightingReady; + private Viewport? _currentViewport; /// /// The current model matrix we would use. @@ -206,7 +207,7 @@ namespace Robust.Client.Graphics.Clyde if (_lightingReady && loaded.HasLighting) { - SetTexture(TextureUnit.Texture1, _lightRenderTarget.Texture); + SetTexture(TextureUnit.Texture1, _currentViewport!.LightRenderTarget.Texture); } else { diff --git a/Robust.Client/Graphics/Clyde/Clyde.Textures.cs b/Robust.Client/Graphics/Clyde/Clyde.Textures.cs index cde5318c46..a9fba5ab32 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.Textures.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.Textures.cs @@ -8,7 +8,6 @@ using Robust.Client.Utility; using Robust.Shared.Maths; using Robust.Shared.Utility; using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using OGLTextureWrapMode = OpenToolkit.Graphics.OpenGL.TextureWrapMode; @@ -20,7 +19,8 @@ namespace Robust.Client.Graphics.Clyde private ClydeTexture _stockTextureBlack = default!; private ClydeTexture _stockTextureTransparent = default!; - private readonly Dictionary _loadedTextures = new Dictionary(); + private readonly Dictionary _loadedTextures = + new Dictionary(); public Texture LoadTextureFromPNGStream(Stream stream, string? name = null, TextureLoadParameters? loadParams = null) @@ -64,6 +64,7 @@ namespace Robust.Client.Graphics.Clyde { throw new ArgumentException("Alpha8 images must have multiple of 4 sizes."); } + internalFormat = PixelInternalFormat.R8; pixelDataFormat = PixelFormat.Red; pixelDataType = PixelType.UnsignedByte; @@ -160,18 +161,20 @@ namespace Robust.Client.Graphics.Clyde var (width, height) = size; + var id = AllocRid(); + var instance = new ClydeTexture(id, size, this); var loaded = new LoadedTexture { OpenGLObject = glHandle, Width = width, Height = height, - Name = name + Name = name, + // TextureInstance = new WeakReference(instance) }; - var id = AllocRid(); _loadedTextures.Add(id, loaded); - return new ClydeTexture(id, size, this); + return instance; } private void DeleteTexture(ClydeTexture texture) @@ -189,16 +192,16 @@ namespace Robust.Client.Graphics.Clyde private void LoadStockTextures() { var white = new Image(1, 1); - white[0, 0] = new Rgba32(255,255,255,255); - _stockTextureWhite = (ClydeTexture)Texture.LoadFromImage(white); + white[0, 0] = new Rgba32(255, 255, 255, 255); + _stockTextureWhite = (ClydeTexture) Texture.LoadFromImage(white); var black = new Image(1, 1); - black[0, 0] = new Rgba32(0,0,0,255); - _stockTextureBlack = (ClydeTexture)Texture.LoadFromImage(black); + black[0, 0] = new Rgba32(0, 0, 0, 255); + _stockTextureBlack = (ClydeTexture) Texture.LoadFromImage(black); var blank = new Image(1, 1); - blank[0, 0] = new Rgba32(0,0,0,0); - _stockTextureTransparent = (ClydeTexture)Texture.LoadFromImage(blank); + blank[0, 0] = new Rgba32(0, 0, 0, 0); + _stockTextureTransparent = (ClydeTexture) Texture.LoadFromImage(blank); } /// @@ -240,6 +243,7 @@ namespace Robust.Client.Graphics.Clyde public int Height; public string? Name; public Vector2i Size => (Width, Height); + // public WeakReference TextureInstance; } private sealed class ClydeTexture : OwnedTexture @@ -265,8 +269,14 @@ namespace Robust.Client.Graphics.Clyde { return $"ClydeTexture: {loaded.Name} ({TextureId})"; } + return $"ClydeTexture: ({TextureId})"; } + + /*public void SetSize(Vector2i newSize) + { + Size = newSize; + }*/ } public Texture GetStockTexture(ClydeStockTexture stockTexture) @@ -281,4 +291,3 @@ namespace Robust.Client.Graphics.Clyde } } } - diff --git a/Robust.Client/Graphics/Clyde/Clyde.Viewport.cs b/Robust.Client/Graphics/Clyde/Clyde.Viewport.cs index 5020f4cfb0..29b4392e83 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.Viewport.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.Viewport.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using Robust.Client.Interfaces.Graphics; using Robust.Client.Interfaces.Graphics.ClientEye; using Robust.Shared.Maths; @@ -6,28 +7,68 @@ namespace Robust.Client.Graphics.Clyde { internal sealed partial class Clyde { - private Viewport CreateViewport() + private readonly List _viewports = new List(); + + private Viewport CreateViewport(Vector2i size, string? name = null) { - return new Viewport(); + var viewport = new Viewport(name); + viewport.Size = size; + viewport.RenderTarget = CreateRenderTarget(size, + new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8, true), + name: $"{name}-MainRenderTarget"); + + RegenLightRts(viewport); + + _viewports.Add(viewport); + + return viewport; } - IClydeViewport IClyde.CreateViewport() + IClydeViewport IClyde.CreateViewport(Vector2i size, string? name) { - return CreateViewport(); + return CreateViewport(size, name); } private sealed class Viewport : IClydeViewport { + // Primary render target. + public RenderTarget RenderTarget = default!; + + // Various render targets used in the light rendering process. + + // Lighting is drawn into this. This then gets sampled later while rendering world-space stuff. + public RenderTarget LightRenderTarget = default!; + + // Unused, to be removed. + public RenderTarget WallMaskRenderTarget = default!; + + // Two render targets used to apply gaussian blur to the _lightRenderTarget so it bleeds "into" walls. + // We need two of them because efficient blur works in two stages and also we're doing multiple iterations. + public RenderTarget WallBleedIntermediateRenderTarget1 = default!; + public RenderTarget WallBleedIntermediateRenderTarget2 = default!; + + public string? Name { get; } + + public Viewport(string? name) + { + Name = name; + } + + + public Vector2i Size { get; set; } + public void Dispose() { } - public IRenderTarget RenderTarget { get; } - public IEye Eye { get; set; } - public void Resize(Vector2i newSize) + IRenderTarget IClydeViewport.RenderTarget => RenderTarget; + public IEye? Eye { get; set; } + + /*public void Resize(Vector2i newSize) { - } + Size = newSize; + }*/ } } } diff --git a/Robust.Client/Graphics/Clyde/Clyde.Windowing.cs b/Robust.Client/Graphics/Clyde/Clyde.Windowing.cs index aab98bb29c..1ba0dc8889 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.Windowing.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.Windowing.cs @@ -402,7 +402,8 @@ namespace Robust.Client.Graphics.Clyde GL.Viewport(0, 0, fbW, fbH); if (fbW != 0 && fbH != 0) { - RegenerateLightingRenderTargets(); + _mainViewport.Dispose(); + _mainViewport = CreateViewport((fbW, fbH), nameof(_mainViewport)); } OnWindowResized?.Invoke(new WindowResizedEventArgs(oldSize, _framebufferSize)); diff --git a/Robust.Client/Graphics/Clyde/Clyde.cs b/Robust.Client/Graphics/Clyde/Clyde.cs index cd84780d35..d4af8f2010 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.cs @@ -55,7 +55,7 @@ namespace Robust.Client.Graphics.Clyde private GLBuffer QuadVBO = default!; private GLHandle QuadVAO; - private Viewport _mainViewport; + private Viewport _mainViewport = default!; private bool _drawingSplash = true; @@ -113,7 +113,7 @@ namespace Robust.Client.Graphics.Clyde { base.ReloadConfig(); - RegenerateLightingRenderTargets(); + RegenAllLightRts(); } public override void PostInject() @@ -235,7 +235,7 @@ namespace Robust.Client.Graphics.Clyde new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb, true), name: nameof(EntityPostRenderTarget)); - _mainViewport = CreateViewport(); + _mainViewport = CreateViewport(_windowSize, nameof(_mainViewport)); } private void DetectOpenGLFeatures() diff --git a/Robust.Client/Graphics/Clyde/ClydeHeadless.cs b/Robust.Client/Graphics/Clyde/ClydeHeadless.cs index daa8c8069a..bf6ee5ef83 100644 --- a/Robust.Client/Graphics/Clyde/ClydeHeadless.cs +++ b/Robust.Client/Graphics/Clyde/ClydeHeadless.cs @@ -122,7 +122,7 @@ namespace Robust.Client.Graphics.Clyde callback(new Image(ScreenSize.X, ScreenSize.Y)); } - public IClydeViewport CreateViewport() + public IClydeViewport CreateViewport(Vector2i size, string? name = null) { return new Viewport(); } @@ -410,10 +410,8 @@ namespace Robust.Client.Graphics.Clyde public IRenderTarget RenderTarget { get; } = new DummyRenderTarget(Vector2i.One, new DummyTexture(Vector2i.One)); - public IEye Eye { get; set; } - public void Resize(Vector2i newSize) - { - } + public IEye? Eye { get; set; } + } } } diff --git a/Robust.Client/Graphics/Texture.cs b/Robust.Client/Graphics/Texture.cs index 6dc561b6dd..515dd309b1 100644 --- a/Robust.Client/Graphics/Texture.cs +++ b/Robust.Client/Graphics/Texture.cs @@ -30,7 +30,7 @@ namespace Robust.Client.Graphics /// /// The size of the texture, in pixels. /// - public Vector2i Size { get; } + public Vector2i Size { get; /*protected set;*/ } protected Texture(Vector2i size) { diff --git a/Robust.Client/Interfaces/Graphics/IClyde.cs b/Robust.Client/Interfaces/Graphics/IClyde.cs index a7c55d2b66..4651db76f8 100644 --- a/Robust.Client/Interfaces/Graphics/IClyde.cs +++ b/Robust.Client/Interfaces/Graphics/IClyde.cs @@ -65,7 +65,7 @@ namespace Robust.Client.Interfaces.Graphics return tcs.Task; } - IClydeViewport CreateViewport(); + IClydeViewport CreateViewport(Vector2i size, string? name = null); } // TODO: Maybe implement IDisposable for render targets. I got lazy and didn't. diff --git a/Robust.Client/Interfaces/Graphics/IClydeViewport.cs b/Robust.Client/Interfaces/Graphics/IClydeViewport.cs index 7009f204db..839c0cd82b 100644 --- a/Robust.Client/Interfaces/Graphics/IClydeViewport.cs +++ b/Robust.Client/Interfaces/Graphics/IClydeViewport.cs @@ -1,6 +1,5 @@ using System; using Robust.Client.Interfaces.Graphics.ClientEye; -using Robust.Shared.Maths; namespace Robust.Client.Interfaces.Graphics { @@ -10,8 +9,6 @@ namespace Robust.Client.Interfaces.Graphics public interface IClydeViewport : IDisposable { IRenderTarget RenderTarget { get; } - IEye Eye { get; set; } - - void Resize(Vector2i newSize); + IEye? Eye { get; set; } } }