Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
46 changes: 40 additions & 6 deletions source/LibRender2/Textures/TextureManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,8 @@ public bool LoadTexture(ref Texture handle, OpenGlTextureWrapMode wrap, int curr
}
//Set last access time

bool compress = renderer.currentOptions.CompressTextures;

if (texture != null)
{
handle = texture;
Expand All @@ -256,6 +258,38 @@ public bool LoadTexture(ref Texture handle, OpenGlTextureWrapMode wrap, int curr
}
//if (texture.BitsPerPixel == 32)
{
if (compress)
{
double savedFactor = 0;
switch (texture.PixelFormat)
{
case PixelFormat.Grayscale:
savedFactor = 0.5;
break;
case PixelFormat.GrayscaleAlpha:
savedFactor = 1.0;
break;
case PixelFormat.RGB:
savedFactor = 2.5;
break;
case PixelFormat.RGBAlpha:
savedFactor = 3.0;
break;
}
bool alreadyTracked = false;
for (int w = 0; w < 4; w++)
{
if (handle.OpenGlTextures[w].Valid)
{
alreadyTracked = true;
break;
}
}
if (!alreadyTracked)
{
Texture.TotalVramSavedBytes += (long)(texture.Width * texture.Height * savedFactor * (texture.MultipleFrames ? texture.TotalFrames : 1));
}
}
int[] names = new int[1];
GL.GenTextures(1, names);
GL.BindTexture(TextureTarget.Texture2D, names[0]);
Expand Down Expand Up @@ -342,7 +376,7 @@ public bool LoadTexture(ref Texture handle, OpenGlTextureWrapMode wrap, int curr
// n.b. Make sure to set the unpack alignment as otherwise we corrupt textures where stride > width
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1);
GL.TexImage2D(TextureTarget.Texture2D, 0,
noLuminanceChannel ? PixelInternalFormat.R8 : PixelInternalFormat.Luminance,
noLuminanceChannel ? (compress ? PixelInternalFormat.CompressedRed : PixelInternalFormat.R8) : (compress ? PixelInternalFormat.CompressedLuminance : PixelInternalFormat.Luminance),
texture.Width, texture.Height, 0,
noLuminanceChannel ? OpenTK.Graphics.OpenGL.PixelFormat.Red : OpenTK.Graphics.OpenGL.PixelFormat.Luminance,
PixelType.UnsignedByte, texture.Bytes);
Expand All @@ -358,7 +392,7 @@ public bool LoadTexture(ref Texture handle, OpenGlTextureWrapMode wrap, int curr
// n.b. Make sure to set the unpack alignment as otherwise we corrupt textures where stride > width
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1);
GL.TexImage2D(TextureTarget.Texture2D, 0,
PixelInternalFormat.Rgb8,
compress ? PixelInternalFormat.CompressedRgb : PixelInternalFormat.Rgb8,
texture.Width, texture.Height, 0,
OpenTK.Graphics.OpenGL.PixelFormat.Rgb,
PixelType.UnsignedByte, texture.Bytes);
Expand Down Expand Up @@ -386,7 +420,7 @@ public bool LoadTexture(ref Texture handle, OpenGlTextureWrapMode wrap, int curr
// n.b. Must reset the unpack alignment in case of changes
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 4);
GL.TexImage2D(TextureTarget.Texture2D, 0,
PixelInternalFormat.Rgb8,
compress ? PixelInternalFormat.CompressedRgb : PixelInternalFormat.Rgb8,
texture.Width, texture.Height, 0,
OpenTK.Graphics.OpenGL.PixelFormat.Rgb,
PixelType.UnsignedByte, newBytes);
Expand Down Expand Up @@ -420,7 +454,7 @@ public bool LoadTexture(ref Texture handle, OpenGlTextureWrapMode wrap, int curr
j += stride - 3 * texture.Width;
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 4);
GL.TexImage2D(TextureTarget.Texture2D, 0,
PixelInternalFormat.Rgba8,
compress ? PixelInternalFormat.CompressedRgba : PixelInternalFormat.Rgba8,
texture.Width, texture.Height, 0,
OpenTK.Graphics.OpenGL.PixelFormat.Rgba,
PixelType.UnsignedByte, newBytes);
Expand All @@ -430,7 +464,7 @@ public bool LoadTexture(ref Texture handle, OpenGlTextureWrapMode wrap, int curr
{
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1);
GL.TexImage2D(TextureTarget.Texture2D, 0,
PixelInternalFormat.LuminanceAlpha,
compress ? PixelInternalFormat.CompressedLuminanceAlpha : PixelInternalFormat.LuminanceAlpha,
texture.Width, texture.Height, 0,
OpenTK.Graphics.OpenGL.PixelFormat.LuminanceAlpha,
PixelType.UnsignedByte, texture.Bytes);
Expand All @@ -444,7 +478,7 @@ public bool LoadTexture(ref Texture handle, OpenGlTextureWrapMode wrap, int curr
// n.b. Must reset the unpack alignment in case of changes
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 4);
GL.TexImage2D(TextureTarget.Texture2D, 0,
PixelInternalFormat.Rgba8,
compress ? PixelInternalFormat.CompressedRgba : PixelInternalFormat.Rgba8,
texture.Width, texture.Height, 0,
OpenTK.Graphics.OpenGL.PixelFormat.Rgba,
PixelType.UnsignedByte, texture.Bytes);
Expand Down
4 changes: 4 additions & 0 deletions source/ObjectViewer/Hosts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ public override bool LoadTexture(string path, TextureParameters parameters, out
if (Program.CurrentHost.Plugins[i].Texture.LoadTexture(path, out texture)) {
texture.CompatibleTransparencyMode = false;
texture = texture.ApplyParameters(parameters);
if (Interface.CurrentOptions.MaxTextureSize > 0 && !path.StartsWith(Program.FileSystem.DataFolder, StringComparison.OrdinalIgnoreCase))
{
texture.Downscale(Interface.CurrentOptions.MaxTextureSize);
}
return true;
}
if(!FailedTextures.Contains(path))
Expand Down
4 changes: 4 additions & 0 deletions source/ObjectViewer/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ public override void Save(string fileName)
Builder.AppendLine("anisotropicfilteringlevel = " + AnisotropicFilteringLevel.ToString(Culture));
Builder.AppendLine("antialiasinglevel = " + AntiAliasingLevel.ToString(Culture));
Builder.AppendLine("transparencyMode = " + ((int)TransparencyMode).ToString(Culture));
Builder.AppendLine("compresstextures = " + (CompressTextures ? "true" : "false"));
Builder.AppendLine("maxtexturesize = " + MaxTextureSize.ToString(Culture));
Builder.AppendLine("shadowresolution = " + (int)ShadowResolution);
Builder.AppendLine("shadowdrawdistance = " + ShadowDrawDistance);
Builder.AppendLine("shadowcascades = " + (int)ShadowCascades);
Expand Down Expand Up @@ -180,6 +182,8 @@ internal static void LoadOptions()
block.TryGetValue(OptionsKey.AnisotropicFilteringLevel, ref Interface.CurrentOptions.AnisotropicFilteringLevel);
block.TryGetValue(OptionsKey.AntiAliasingLevel, ref Interface.CurrentOptions.AntiAliasingLevel);
block.GetEnumValue(OptionsKey.TransparencyMode, out Interface.CurrentOptions.TransparencyMode);
block.GetValue(OptionsKey.CompressTextures, out Interface.CurrentOptions.CompressTextures);
block.TryGetValue(OptionsKey.MaxTextureSize, ref Interface.CurrentOptions.MaxTextureSize, NumberRange.NonNegative);
block.TryGetEnumValue(OptionsKey.ShadowResolution, ref Interface.CurrentOptions.ShadowResolution);
block.TryGetEnumValue(OptionsKey.ShadowDrawDistance, ref Interface.CurrentOptions.ShadowDrawDistance);
block.TryGetEnumValue(OptionsKey.ShadowCascades, ref Interface.CurrentOptions.ShadowCascades);
Expand Down
7 changes: 7 additions & 0 deletions source/ObjectViewer/ProgramS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,8 @@ internal static void MouseMovement()
internal static void RefreshObjects(bool autoReload = false)
{
LightingRelative = -1.0;
OpenBveApi.Textures.Texture.TotalRamSavedBytes = 0;
OpenBveApi.Textures.Texture.TotalVramSavedBytes = 0;

// Prune cache to allow actual reloading of modified files
var staticKeysToRemove = new List<ValueTuple<string, bool, DateTime>>();
Expand Down Expand Up @@ -455,6 +457,11 @@ internal static void RefreshObjects(bool autoReload = false)
}
LastReloadTime = DateTime.UtcNow;
UpdateWatchers();
if (OpenBveApi.Textures.Texture.TotalRamSavedBytes > 0 || OpenBveApi.Textures.Texture.TotalVramSavedBytes > 0)
{
Console.WriteLine($"[Memory Optimization] RAM Saved from resizing: {OpenBveApi.Textures.Texture.TotalRamSavedBytes / 1024.0 / 1024.0:F2} MB");
Console.WriteLine($"[Memory Optimization] Estimated VRAM Saved from compression: {OpenBveApi.Textures.Texture.TotalVramSavedBytes / 1024.0 / 1024.0:F2} MB");
}
}

/// <summary>Checks if any of the loaded files have been updated externally.</summary>
Expand Down
4 changes: 4 additions & 0 deletions source/OpenBVE/System/Host.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ public override bool LoadTexture(string path, TextureParameters parameters, out

texture.CompatibleTransparencyMode = oldTransparencyMode;
texture = texture.ApplyParameters(parameters);
if (Interface.CurrentOptions.MaxTextureSize > 0 && !path.StartsWith(Program.FileSystem.DataFolder, StringComparison.OrdinalIgnoreCase))
{
texture.Downscale(Interface.CurrentOptions.MaxTextureSize);
}
return true;
}
if (!FailedTextures.Contains(path))
Expand Down
7 changes: 7 additions & 0 deletions source/OpenBVE/System/Loading.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ internal static void LoadAsynchronously(string routeFile, Encoding routeEncoding
// members
Cancel = false;
Complete = false;
OpenBveApi.Textures.Texture.TotalRamSavedBytes = 0;
OpenBveApi.Textures.Texture.TotalVramSavedBytes = 0;
CurrentRouteFile = routeFile;
CurrentRouteEncoding = routeEncoding;
CurrentTrainFolder = trainFolder;
Expand Down Expand Up @@ -239,6 +241,11 @@ private static void LoadThreaded() {
Thread.Sleep(10);
}
Complete = true;
if (OpenBveApi.Textures.Texture.TotalRamSavedBytes > 0 || OpenBveApi.Textures.Texture.TotalVramSavedBytes > 0)
{
Console.WriteLine($"[Memory Optimization] RAM Saved from resizing: {OpenBveApi.Textures.Texture.TotalRamSavedBytes / 1024.0 / 1024.0:F2} MB");
Console.WriteLine($"[Memory Optimization] Estimated VRAM Saved from compression: {OpenBveApi.Textures.Texture.TotalVramSavedBytes / 1024.0 / 1024.0:F2} MB");
}
}
private static void LoadEverythingThreaded() {

Expand Down
5 changes: 4 additions & 1 deletion source/OpenBVE/System/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -311,8 +311,9 @@ public override void Save(string fileName)
Builder.AppendLine("anisotropicFilteringLevel = " + AnisotropicFilteringLevel.ToString(Culture));
Builder.AppendLine("anisotropicFilteringMaximum = " + AnisotropicFilteringMaximum.ToString(Culture));
Builder.AppendLine("antiAliasingLevel = " + AntiAliasingLevel.ToString(Culture));
Builder.AppendLine("transparencyMode = " + ((int)TransparencyMode).ToString(Culture));
Builder.AppendLine("oldtransparencymode = " + (OldTransparencyMode ? "true" : "false"));
Builder.AppendLine("compresstextures = " + (CompressTextures ? "true" : "false"));
Builder.AppendLine("maxtexturesize = " + MaxTextureSize.ToString(Culture));
Builder.AppendLine("viewingDistance = " + ViewingDistance.ToString(Culture));
Builder.AppendLine("nearclipscenery = " + NearClipScenery.ToString(Culture));
Builder.AppendLine("nearclipcab = " + NearClipCab.ToString(Culture));
Expand Down Expand Up @@ -517,6 +518,8 @@ internal static void LoadOptions()
block.TryGetValue(OptionsKey.AntiAliasingLevel, ref Interface.CurrentOptions.AntiAliasingLevel);
block.GetEnumValue(OptionsKey.TransparencyMode, out Interface.CurrentOptions.TransparencyMode);
block.GetValue(OptionsKey.OldTransparencyMode, out CurrentOptions.OldTransparencyMode);
block.GetValue(OptionsKey.CompressTextures, out CurrentOptions.CompressTextures);
block.TryGetValue(OptionsKey.MaxTextureSize, ref CurrentOptions.MaxTextureSize, NumberRange.NonNegative);
block.TryGetValue(OptionsKey.ViewingDistance, ref Interface.CurrentOptions.ViewingDistance, NumberRange.Positive);
block.TryGetValue(OptionsKey.QuadLeafSize, ref Interface.CurrentOptions.QuadTreeLeafSize, NumberRange.Positive);
block.TryGetValue(OptionsKey.NearClipScenery, ref Interface.CurrentOptions.NearClipScenery, NumberRange.Positive);
Expand Down
2 changes: 2 additions & 0 deletions source/OpenBveApi/System/BaseOptions.OptionsKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ public enum OptionsKey
OldTransparencyMode,
MotionBlur,
FPSLimit,
CompressTextures,
MaxTextureSize,
ShadowResolution,
ShadowDrawDistance,
ShadowCascades,
Expand Down
4 changes: 4 additions & 0 deletions source/OpenBveApi/System/BaseOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ public abstract class BaseOptions
public int UserInterfaceScaleFactor;
/// <summary>Whether loaded objects are automatically reloaded on change</summary>
public bool AutoReloadObjects;
/// <summary>Whether to use texture compression to save VRAM</summary>
public bool CompressTextures;
/// <summary>The maximum texture size allowed, or 0 for unlimited</summary>
public int MaxTextureSize;

/// <summary>The near clipping plane for scenery</summary>
public double NearClipScenery = 0.5;
Expand Down
78 changes: 78 additions & 0 deletions source/OpenBveApi/Textures/Textures.cs
Original file line number Diff line number Diff line change
Expand Up @@ -434,5 +434,83 @@ public void InvertLightness()
}
}
}

/// <summary>The total number of RAM bytes saved by downscaling textures.</summary>
public static long TotalRamSavedBytes = 0;
/// <summary>The total number of VRAM bytes saved by compressing textures.</summary>
public static long TotalVramSavedBytes = 0;

/// <summary>Downscales the texture to fit within the specified maximum size, keeping aspect ratio.</summary>
public void Downscale(int maxTextureSize)
{
if (Ignore || MyBytes == null || Size.X <= maxTextureSize && Size.Y <= maxTextureSize)
{
return;
}
int oldWidth = (int)Size.X;
int oldHeight = (int)Size.Y;
int newWidth = oldWidth;
int newHeight = oldHeight;
if (newWidth > newHeight)
{
newHeight = (int)System.Math.Round((double)newHeight * maxTextureSize / newWidth);
newWidth = maxTextureSize;
}
else
{
newWidth = (int)System.Math.Round((double)newWidth * maxTextureSize / newHeight);
newHeight = maxTextureSize;
}
if (newWidth < 1) newWidth = 1;
if (newHeight < 1) newHeight = 1;

int bpp = (int)PixelFormat;
for (int frame = 0; frame < MyBytes.Length; frame++)
{
byte[] oldBytes = MyBytes[frame];
if (oldBytes == null || oldBytes.Length != oldWidth * oldHeight * bpp)
{
continue;
}
byte[] newBytes = new byte[newWidth * newHeight * bpp];

for (int y = 0; y < newHeight; y++)
{
float tempY = (float)y * oldHeight / newHeight;
int yFloor = (int)System.Math.Floor(tempY);
int yCeil = System.Math.Min(oldHeight - 1, yFloor + 1);
float dy = tempY - yFloor;

for (int x = 0; x < newWidth; x++)
{
float tempX = (float)x * oldWidth / newWidth;
int yFloor2 = (int)System.Math.Floor(tempX);
int xCeil = System.Math.Min(oldWidth - 1, yFloor2 + 1);
float dx = tempX - yFloor2;

for (int c = 0; c < bpp; c++)
{
int idx00 = (yFloor * oldWidth + yFloor2) * bpp + c;
int idx10 = (yFloor * oldWidth + xCeil) * bpp + c;
int idx01 = (yCeil * oldWidth + yFloor2) * bpp + c;
int idx11 = (yCeil * oldWidth + xCeil) * bpp + c;

float val = oldBytes[idx00] * (1 - dx) * (1 - dy) +
oldBytes[idx10] * dx * (1 - dy) +
oldBytes[idx01] * (1 - dx) * dy +
oldBytes[idx11] * dx * dy;

newBytes[(y * newWidth + x) * bpp + c] = (byte)System.Math.Min(255, System.Math.Max(0, val));
}
}
}
MyBytes[frame] = newBytes;
}
long bytesBefore = (long)oldWidth * oldHeight * bpp * MyBytes.Length;
long bytesAfter = (long)newWidth * newHeight * bpp * MyBytes.Length;
TotalRamSavedBytes += (bytesBefore - bytesAfter);
Size.X = newWidth;
Size.Y = newHeight;
}
}
}
7 changes: 7 additions & 0 deletions source/RouteViewer/LoadingR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ internal static void Load(string routeFile, Encoding routeEncoding, byte[] textu
Program.Renderer.GameWindow.TargetRenderFrequency = 0;
// reset
Game.Reset();
OpenBveApi.Textures.Texture.TotalRamSavedBytes = 0;
OpenBveApi.Textures.Texture.TotalVramSavedBytes = 0;
Program.Renderer.Loading.InitLoading(Program.FileSystem.GetDataFolder("In-game"), typeof(NewRenderer).Assembly.GetName().Version.ToString(), Interface.CurrentOptions.LoadingLogo, Interface.CurrentOptions.LoadingProgressBar);
if (textureBytes != null && textureBytes.Length > 0)
{
Expand Down Expand Up @@ -167,6 +169,11 @@ private static void LoadEverythingThreaded() {
Program.Renderer.Camera.Alignment = new CameraAlignment(new Vector3(0.0, 2.5, 0.0), 0.0, 0.0, 0.0, FirstStationPosition, 1.0);
World.UpdateAbsoluteCamera(0.0);
Complete = true;
if (Texture.TotalRamSavedBytes > 0 || Texture.TotalVramSavedBytes > 0)
{
Console.WriteLine($"[Memory Optimization] RAM Saved from resizing: {Texture.TotalRamSavedBytes / 1024.0 / 1024.0:F2} MB");
Console.WriteLine($"[Memory Optimization] Estimated VRAM Saved from compression: {Texture.TotalVramSavedBytes / 1024.0 / 1024.0:F2} MB");
}
}

}
Expand Down
4 changes: 4 additions & 0 deletions source/RouteViewer/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ public override void Save(string fileName)
Builder.AppendLine("anisotropicfilteringlevel = " + AnisotropicFilteringLevel.ToString(Culture));
Builder.AppendLine("antialiasinglevel = " + AntiAliasingLevel.ToString(Culture));
Builder.AppendLine("transparencyMode = " + ((int)TransparencyMode).ToString(Culture));
Builder.AppendLine("compresstextures = " + (CompressTextures ? "true" : "false"));
Builder.AppendLine("maxtexturesize = " + MaxTextureSize.ToString(Culture));
Builder.AppendLine("viewingdistance = " + ViewingDistance);
Builder.AppendLine("nearclipbase = " + NearClipBase.ToString(Culture));
Builder.AppendLine("quadleafsize = " + QuadTreeLeafSize);
Expand Down Expand Up @@ -129,6 +131,8 @@ internal static void LoadOptions()
block.TryGetValue(OptionsKey.AnisotropicFilteringLevel, ref Interface.CurrentOptions.AnisotropicFilteringLevel);
block.TryGetValue(OptionsKey.AntiAliasingLevel, ref Interface.CurrentOptions.AntiAliasingLevel);
block.GetEnumValue(OptionsKey.TransparencyMode, out Interface.CurrentOptions.TransparencyMode);
block.GetValue(OptionsKey.CompressTextures, out Interface.CurrentOptions.CompressTextures);
block.TryGetValue(OptionsKey.MaxTextureSize, ref Interface.CurrentOptions.MaxTextureSize, NumberRange.NonNegative);
block.TryGetValue(OptionsKey.ViewingDistance, ref Interface.CurrentOptions.ViewingDistance, NumberRange.Positive);
block.TryGetValue(OptionsKey.QuadLeafSize, ref Interface.CurrentOptions.QuadTreeLeafSize, NumberRange.Positive);
block.TryGetValue(OptionsKey.NearClipBase, ref Interface.CurrentOptions.NearClipBase, NumberRange.Positive);
Expand Down
4 changes: 4 additions & 0 deletions source/RouteViewer/System/Host.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ public override bool LoadTexture(string path, TextureParameters parameters, out
{
texture.CompatibleTransparencyMode = false;
texture = texture.ApplyParameters(parameters);
if (Interface.CurrentOptions.MaxTextureSize > 0 && !path.StartsWith(Program.FileSystem.DataFolder, StringComparison.OrdinalIgnoreCase))
{
texture.Downscale(Interface.CurrentOptions.MaxTextureSize);
}
return true;
}
if (!FailedTextures.Contains(path))
Expand Down