Set world framebuffer size based on minimum zoom.

This avoids reallocating buffers each time the player changes zoom level.
This commit is contained in:
Paul Chote
2021-05-15 13:52:12 +01:00
committed by reaperrr
parent bb15bd20c0
commit 0735345674
9 changed files with 138 additions and 9 deletions

View File

@@ -18,15 +18,19 @@ namespace OpenRA.Graphics
public sealed class HardwarePalette : IDisposable public sealed class HardwarePalette : IDisposable
{ {
public ITexture Texture { get; private set; } public ITexture Texture { get; private set; }
public ITexture ColorShifts { get; private set; }
public int Height { get; private set; } public int Height { get; private set; }
readonly Dictionary<string, ImmutablePalette> palettes = new Dictionary<string, ImmutablePalette>(); readonly Dictionary<string, ImmutablePalette> palettes = new Dictionary<string, ImmutablePalette>();
readonly Dictionary<string, MutablePalette> mutablePalettes = new Dictionary<string, MutablePalette>(); readonly Dictionary<string, MutablePalette> mutablePalettes = new Dictionary<string, MutablePalette>();
readonly Dictionary<string, int> indices = new Dictionary<string, int>(); readonly Dictionary<string, int> indices = new Dictionary<string, int>();
byte[] buffer = new byte[0]; byte[] buffer = new byte[0];
float[] colorShiftBuffer = new float[0];
public HardwarePalette() public HardwarePalette()
{ {
Texture = Game.Renderer.Context.CreateTexture(); Texture = Game.Renderer.Context.CreateTexture();
ColorShifts = Game.Renderer.Context.CreateTexture();
} }
public bool Contains(string name) public bool Contains(string name)
@@ -55,14 +59,18 @@ namespace OpenRA.Graphics
if (palettes.ContainsKey(name)) if (palettes.ContainsKey(name))
throw new InvalidOperationException($"Palette {name} has already been defined"); throw new InvalidOperationException($"Palette {name} has already been defined");
int index = palettes.Count; // PERF: the first row in the palette textures is reserved as a placeholder for non-indexed sprites
// that do not have a color-shift applied. This provides a quick shortcut to avoid querying the
// color-shift texture for every pixel only to find that most are not shifted.
var index = palettes.Count + 1;
indices.Add(name, index); indices.Add(name, index);
palettes.Add(name, p); palettes.Add(name, p);
if (palettes.Count > Height) if (index >= Height)
{ {
Height = Exts.NextPowerOf2(palettes.Count); Height = Exts.NextPowerOf2(index + 1);
Array.Resize(ref buffer, Height * Palette.Size * 4); Array.Resize(ref buffer, Height * Palette.Size * 4);
Array.Resize(ref colorShiftBuffer, Height * 4);
} }
if (allowModifiers) if (allowModifiers)
@@ -82,6 +90,21 @@ namespace OpenRA.Graphics
CopyBufferToTexture(); CopyBufferToTexture();
} }
public void SetColorShift(string name, float hueOffset, float satOffset, float minHue, float maxHue)
{
var index = GetPaletteIndex(name);
colorShiftBuffer[4 * index + 0] = hueOffset;
colorShiftBuffer[4 * index + 1] = satOffset;
colorShiftBuffer[4 * index + 2] = minHue;
colorShiftBuffer[4 * index + 3] = maxHue;
}
public bool HasColorShift(string name)
{
var index = GetPaletteIndex(name);
return colorShiftBuffer[4 * index + 2] != 0 || colorShiftBuffer[4 * index + 3] != 0;
}
public void Initialize() public void Initialize()
{ {
CopyModifiablePalettesToBuffer(); CopyModifiablePalettesToBuffer();
@@ -102,6 +125,7 @@ namespace OpenRA.Graphics
void CopyBufferToTexture() void CopyBufferToTexture()
{ {
Texture.SetData(buffer, Palette.Size, Height); Texture.SetData(buffer, Palette.Size, Height);
ColorShifts.SetFloatData(colorShiftBuffer, 1, Height);
} }
public void ApplyModifiers(IEnumerable<IPaletteModifier> paletteMods) public void ApplyModifiers(IEnumerable<IPaletteModifier> paletteMods)
@@ -125,6 +149,7 @@ namespace OpenRA.Graphics
public void Dispose() public void Dispose()
{ {
Texture.Dispose(); Texture.Dispose();
ColorShifts.Dispose();
} }
} }
} }

View File

@@ -28,5 +28,7 @@ namespace OpenRA.Graphics
this.index = index; this.index = index;
this.hardwarePalette = hardwarePalette; this.hardwarePalette = hardwarePalette;
} }
public bool HasColorShift => hardwarePalette.HasColorShift(Name);
} }
} }

View File

@@ -41,6 +41,12 @@ namespace OpenRA.Graphics
this.isDecoration = isDecoration; this.isDecoration = isDecoration;
this.tintModifiers = tintModifiers; this.tintModifiers = tintModifiers;
this.alpha = alpha; this.alpha = alpha;
// PERF: Remove useless palette assignments for RGBA sprites
// HACK: This is working around the fact that palettes are defined on traits rather than sequences
// and can be removed once this has been fixed
if (sprite.Channel == TextureChannel.RGBA && !(palette?.HasColorShift ?? false))
this.palette = null;
} }
public WPos Pos => pos + offset; public WPos Pos => pos + offset;

View File

@@ -116,14 +116,28 @@ namespace OpenRA.Graphics
nv += 6; nv += 6;
} }
float ResolveTextureIndex(Sprite s, PaletteReference pal)
{
if (pal == null)
return 0;
// PERF: Remove useless palette assignments for RGBA sprites
// HACK: This is working around the limitation that palettes are defined on traits rather than on sequences,
// and can be removed once this has been fixed
if (s.Channel == TextureChannel.RGBA && !pal.HasColorShift)
return 0;
return pal.TextureIndex;
}
public void DrawSprite(Sprite s, in float3 location, PaletteReference pal) public void DrawSprite(Sprite s, in float3 location, PaletteReference pal)
{ {
DrawSprite(s, location, pal.TextureIndex, s.Size); DrawSprite(s, location, ResolveTextureIndex(s, pal), s.Size);
} }
public void DrawSprite(Sprite s, in float3 location, PaletteReference pal, float3 size) public void DrawSprite(Sprite s, in float3 location, PaletteReference pal, float3 size)
{ {
DrawSprite(s, location, pal.TextureIndex, size); DrawSprite(s, location, ResolveTextureIndex(s, pal), size);
} }
public void DrawSprite(Sprite s, in float3 a, in float3 b, in float3 c, in float3 d) public void DrawSprite(Sprite s, in float3 a, in float3 b, in float3 c, in float3 d)
@@ -142,7 +156,7 @@ namespace OpenRA.Graphics
public void DrawSprite(Sprite s, in float3 location, PaletteReference pal, in float3 size, in float3 tint, float alpha) public void DrawSprite(Sprite s, in float3 location, PaletteReference pal, in float3 size, in float3 tint, float alpha)
{ {
DrawSprite(s, location, pal.TextureIndex, size, tint, alpha); DrawSprite(s, location, ResolveTextureIndex(s, pal), size, tint, alpha);
} }
public void DrawSprite(Sprite s, in float3 a, in float3 b, in float3 c, in float3 d, in float3 tint, float alpha) public void DrawSprite(Sprite s, in float3 a, in float3 b, in float3 c, in float3 d, in float3 tint, float alpha)
@@ -189,9 +203,10 @@ namespace OpenRA.Graphics
nv += v.Length; nv += v.Length;
} }
public void SetPalette(ITexture palette) public void SetPalette(ITexture palette, ITexture colorShifts)
{ {
shader.SetTexture("Palette", palette); shader.SetTexture("Palette", palette);
shader.SetTexture("ColorShifts", colorShifts);
} }
public void SetViewportParams(Size screen, float depthScale, float depthOffset, int2 scroll) public void SetViewportParams(Size screen, float depthScale, float depthOffset, int2 scroll)

View File

@@ -164,6 +164,12 @@ namespace OpenRA.Graphics
throw new InvalidDataException("Attempted to add sprite with a different blend mode"); throw new InvalidDataException("Attempted to add sprite with a different blend mode");
samplers = new int2(GetOrAddSheetIndex(sprite.Sheet), GetOrAddSheetIndex((sprite as SpriteWithSecondaryData)?.SecondarySheet)); samplers = new int2(GetOrAddSheetIndex(sprite.Sheet), GetOrAddSheetIndex((sprite as SpriteWithSecondaryData)?.SecondarySheet));
// PERF: Remove useless palette assignments for RGBA sprites
// HACK: This is working around the limitation that palettes are defined on traits rather than on sequences,
// and can be removed once this has been fixed
if (sprite.Channel == TextureChannel.RGBA && !(palette?.HasColorShift ?? false))
palette = null;
} }
else else
{ {

View File

@@ -32,6 +32,12 @@ namespace OpenRA.Graphics
this.palette = palette; this.palette = palette;
this.scale = scale; this.scale = scale;
this.alpha = alpha; this.alpha = alpha;
// PERF: Remove useless palette assignments for RGBA sprites
// HACK: This is working around the fact that palettes are defined on traits rather than sequences
// and can be removed once this has been fixed
if (sprite.Channel == TextureChannel.RGBA && !(palette?.HasColorShift ?? false))
this.palette = null;
} }
// Does not exist in the world, so a world positions don't make sense // Does not exist in the world, so a world positions don't make sense

View File

@@ -109,6 +109,11 @@ namespace OpenRA.Graphics
palettes[name].Palette = pal; palettes[name].Palette = pal;
} }
public void SetPaletteColorShift(string name, float hueOffset, float satOffset, float minHue, float maxHue)
{
palette.SetColorShift(name, hueOffset, satOffset, minHue, maxHue);
}
// PERF: Avoid LINQ. // PERF: Avoid LINQ.
void GenerateRenderables() void GenerateRenderables()
{ {

View File

@@ -249,14 +249,16 @@ namespace OpenRA
public void SetPalette(HardwarePalette palette) public void SetPalette(HardwarePalette palette)
{ {
// Note: palette.Texture and palette.ColorShifts are updated at the same time
// so we only need to check one of the two to know whether we must update the textures
if (palette.Texture == currentPaletteTexture) if (palette.Texture == currentPaletteTexture)
return; return;
Flush(); Flush();
currentPaletteTexture = palette.Texture; currentPaletteTexture = palette.Texture;
SpriteRenderer.SetPalette(currentPaletteTexture); SpriteRenderer.SetPalette(currentPaletteTexture, palette.ColorShifts);
WorldSpriteRenderer.SetPalette(currentPaletteTexture); WorldSpriteRenderer.SetPalette(currentPaletteTexture, palette.ColorShifts);
WorldModelRenderer.SetPalette(currentPaletteTexture); WorldModelRenderer.SetPalette(currentPaletteTexture);
} }

View File

@@ -12,6 +12,7 @@ uniform sampler2D Texture5;
uniform sampler2D Texture6; uniform sampler2D Texture6;
uniform sampler2D Texture7; uniform sampler2D Texture7;
uniform sampler2D Palette; uniform sampler2D Palette;
uniform sampler2D ColorShifts;
uniform bool EnableDepthPreview; uniform bool EnableDepthPreview;
uniform float DepthTextureScale; uniform float DepthTextureScale;
@@ -69,6 +70,49 @@ float jet_b(float x)
return x < 0.3 ? 4.0 * x + 0.5 : -4.0 * x + 2.5; return x < 0.3 ? 4.0 * x + 0.5 : -4.0 * x + 2.5;
} }
vec3 rgb2hsv(vec3 c)
{
// From http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
vec4 p = c.g < c.b ? vec4(c.bg, K.wz) : vec4(c.gb, K.xy);
vec4 q = c.r < p.x ? vec4(p.xyw, c.r) : vec4(c.r, p.yzx);
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
vec3 hsv2rgb(vec3 c)
{
// From http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
float srgb2linear(float c)
{
// Standard gamma conversion equation: see e.g. http://entropymine.com/imageworsener/srgbformula/
return c <= 0.04045f ? c / 12.92f : pow((c + 0.055f) / 1.055f, 2.4f);
}
vec4 srgb2linear(vec4 c)
{
// The SRGB color has pre-multiplied alpha which we must undo before removing the the gamma correction
return c.a * vec4(srgb2linear(c.r / c.a), srgb2linear(c.g / c.a), srgb2linear(c.b / c.a), 1.0f);
}
float linear2srgb(float c)
{
// Standard gamma conversion equation: see e.g. http://entropymine.com/imageworsener/srgbformula/
return c <= 0.0031308 ? c * 12.92f : 1.055f * pow(c, 1.0f / 2.4f) - 0.055f;
}
vec4 linear2srgb(vec4 c)
{
// The linear color has pre-multiplied alpha which we must undo before applying the the gamma correction
return c.a * vec4(linear2srgb(c.r / c.a), linear2srgb(c.g / c.a), linear2srgb(c.b / c.a), 1.0f);
}
#if __VERSION__ == 120 #if __VERSION__ == 120
vec2 Size(float samplerIndex) vec2 Size(float samplerIndex)
{ {
@@ -178,6 +222,21 @@ vec4 SamplePalettedBilinear(float samplerIndex, vec2 coords, vec2 textureSize)
return mix(mix(c1, c2, interp.x), mix(c3, c4, interp.x), interp.y); return mix(mix(c1, c2, interp.x), mix(c3, c4, interp.x), interp.y);
} }
vec4 ColorShift(vec4 c, float p)
{
#if __VERSION__ == 120
vec4 shift = texture2D(ColorShifts, vec2(0.5, p));
#else
vec4 shift = texture(ColorShifts, vec2(0.5, p));
#endif
vec3 hsv = rgb2hsv(srgb2linear(c).rgb);
if (hsv.r >= shift.b && shift.a >= hsv.r)
c = linear2srgb(vec4(hsv2rgb(vec3(hsv.r + shift.r, clamp(hsv.g + shift.g, 0.0, 1.0), hsv.b)), c.a));
return c;
}
void main() void main()
{ {
vec2 coords = vTexCoord.st; vec2 coords = vTexCoord.st;
@@ -215,6 +274,9 @@ void main()
if (c.a == 0.0) if (c.a == 0.0)
discard; discard;
if (vRGBAFraction.r > 0.0 && vTexMetadata.s > 0.0)
c = ColorShift(c, vTexMetadata.s);
float depth = gl_FragCoord.z; float depth = gl_FragCoord.z;
if (length(vDepthMask) > 0.0) if (length(vDepthMask) > 0.0)
{ {