tangled
alpha
login
or
join now
keii.dev
/
osu-framework
0
fork
atom
A game framework written with osu! in mind.
0
fork
atom
overview
issues
pulls
pipelines
Fix LargeTextureStore race conditions
smoogipoo
5 years ago
0ca4a51f
3906554d
+64
-32
2 changed files
expand all
collapse all
unified
split
osu.Framework
Graphics
Textures
LargeTextureStore.cs
TextureStore.cs
+26
-16
osu.Framework/Graphics/Textures/LargeTextureStore.cs
···
4
4
using System.Collections.Generic;
5
5
using System.Diagnostics;
6
6
using System.Threading;
7
7
+
using JetBrains.Annotations;
7
8
using osu.Framework.IO.Stores;
8
9
using osuTK.Graphics.ES30;
9
9
-
using osu.Framework.Graphics.OpenGL.Textures;
10
10
11
11
namespace osu.Framework.Graphics.Textures
12
12
{
···
23
23
{
24
24
}
25
25
26
26
-
/// <summary>
27
27
-
/// Retrieves a texture.
28
28
-
/// This texture should only be assigned once, as reference counting is being used internally.
29
29
-
/// If you wish to use the same texture multiple times, call this method an equal number of times.
30
30
-
/// </summary>
31
31
-
/// <param name="name">The name of the texture.</param>
32
32
-
/// <param name="wrapModeS">The horizontal wrap mode of the texture.</param>
33
33
-
/// <param name="wrapModeT">The vertical wrap mode of the texture.</param>
34
34
-
/// <returns>The texture.</returns>
35
35
-
public override Texture Get(string name, WrapMode wrapModeS, WrapMode wrapModeT)
26
26
+
protected override bool TryGetCached(string lookupKey, out Texture texture)
36
27
{
37
37
-
var tex = base.Get(name, wrapModeS, wrapModeT);
28
28
+
lock (referenceCountLock)
29
29
+
{
30
30
+
if (base.TryGetCached(lookupKey, out var tex))
31
31
+
{
32
32
+
texture = createTextureWithRefCount(lookupKey, tex);
33
33
+
return true;
34
34
+
}
38
35
39
39
-
if (tex?.TextureGL == null)
36
36
+
texture = null;
37
37
+
return false;
38
38
+
}
39
39
+
}
40
40
+
41
41
+
protected override Texture CacheAndReturnTexture(string lookupKey, Texture texture)
42
42
+
{
43
43
+
lock (referenceCountLock)
44
44
+
return createTextureWithRefCount(lookupKey, base.CacheAndReturnTexture(lookupKey, texture));
45
45
+
}
46
46
+
47
47
+
private TextureWithRefCount createTextureWithRefCount([NotNull] string lookupKey, [CanBeNull] Texture baseTexture)
48
48
+
{
49
49
+
if (baseTexture == null)
40
50
return null;
41
51
42
52
lock (referenceCountLock)
43
53
{
44
44
-
if (!referenceCounts.TryGetValue(tex.LookupKey, out TextureWithRefCount.ReferenceCount count))
45
45
-
referenceCounts[tex.LookupKey] = count = new TextureWithRefCount.ReferenceCount(referenceCountLock, () => onAllReferencesLost(tex));
54
54
+
if (!referenceCounts.TryGetValue(lookupKey, out TextureWithRefCount.ReferenceCount count))
55
55
+
referenceCounts[lookupKey] = count = new TextureWithRefCount.ReferenceCount(referenceCountLock, () => onAllReferencesLost(baseTexture));
46
56
47
47
-
return new TextureWithRefCount(tex.TextureGL, count);
57
57
+
return new TextureWithRefCount(baseTexture.TextureGL, count);
48
58
}
49
59
}
50
60
+38
-16
osu.Framework/Graphics/Textures/TextureStore.cs
···
7
7
using osu.Framework.IO.Stores;
8
8
using System.Collections.Generic;
9
9
using System.Threading.Tasks;
10
10
+
using JetBrains.Annotations;
10
11
using osu.Framework.Logging;
11
12
using osuTK.Graphics.ES30;
12
13
···
61
62
if (Atlas != null)
62
63
{
63
64
if ((glTexture = Atlas.Add(upload.Width, upload.Height, wrapModeS, wrapModeT)) == null)
64
64
-
Logger.Log($"Texture requested ({upload.Width}x{upload.Height}) which exceeds {nameof(TextureStore)}'s atlas size ({max_atlas_size}x{max_atlas_size}) - bypassing atlasing. Consider using {nameof(LargeTextureStore)}.", LoggingTarget.Performance);
65
65
+
{
66
66
+
Logger.Log(
67
67
+
$"Texture requested ({upload.Width}x{upload.Height}) which exceeds {nameof(TextureStore)}'s atlas size ({max_atlas_size}x{max_atlas_size}) - bypassing atlasing. Consider using {nameof(LargeTextureStore)}.",
68
68
+
LoggingTarget.Performance);
69
69
+
}
65
70
}
66
71
67
72
glTexture ??= new TextureGLSingle(upload.Width, upload.Height, manualMipmaps, filteringMode, wrapModeS, wrapModeT);
···
86
91
/// <param name="wrapModeS">The texture wrap mode in horizontal direction.</param>
87
92
/// <param name="wrapModeT">The texture wrap mode in vertical direction.</param>
88
93
/// <returns>The texture.</returns>
89
89
-
public Task<Texture> GetAsync(string name, WrapMode wrapModeT, WrapMode wrapModeS) => Task.Run(() => Get(name, wrapModeS, wrapModeT)); // TODO: best effort. need to re-think textureCache data structure to fix this.
94
94
+
public Task<Texture> GetAsync(string name, WrapMode wrapModeT, WrapMode wrapModeS) =>
95
95
+
Task.Run(() => Get(name, wrapModeS, wrapModeT)); // TODO: best effort. need to re-think textureCache data structure to fix this.
90
96
91
97
/// <summary>
92
98
/// Retrieves a texture from the store and adds it to the atlas.
···
111
117
this.LogIfNonBackgroundThread(key);
112
118
113
119
// Check if the texture exists in the cache.
114
114
-
lock (textureCache)
115
115
-
{
116
116
-
if (textureCache.TryGetValue(key, out var tex))
117
117
-
return tex;
118
118
-
}
120
120
+
if (TryGetCached(key, out var cached))
121
121
+
return cached;
119
122
120
123
// Take an exclusive lock on retrieval of the texture.
121
124
lock (retrievalLock)
122
125
{
123
126
// If another retrieval of the texture happened before us, we should check if the texture exists in the cache again.
124
124
-
lock (textureCache)
125
125
-
{
126
126
-
if (textureCache.TryGetValue(key, out var tex))
127
127
-
return tex;
128
128
-
}
127
127
+
if (TryGetCached(key, out cached))
128
128
+
return cached;
129
129
130
130
try
131
131
{
···
133
133
if (tex != null)
134
134
tex.LookupKey = key;
135
135
136
136
-
lock (textureCache)
137
137
-
textureCache[key] = tex;
138
138
-
139
139
-
return tex;
136
136
+
return CacheAndReturnTexture(key, tex);
140
137
}
141
138
catch (TextureTooLargeForGLException)
142
139
{
···
145
142
}
146
143
147
144
return null;
145
145
+
}
146
146
+
147
147
+
/// <summary>
148
148
+
/// Attempts to retrieve an existing cached texture.
149
149
+
/// </summary>
150
150
+
/// <param name="lookupKey">The lookup key that uniquely identifies textures in the cache.</param>
151
151
+
/// <param name="texture">The returned texture. Null if the texture did not exist in the cache.</param>
152
152
+
/// <returns>Whether a cached texture was retrieved.</returns>
153
153
+
protected virtual bool TryGetCached([NotNull] string lookupKey, [CanBeNull] out Texture texture)
154
154
+
{
155
155
+
lock (textureCache)
156
156
+
return textureCache.TryGetValue(lookupKey, out texture);
157
157
+
}
158
158
+
159
159
+
/// <summary>
160
160
+
/// Caches and returns the given texture.
161
161
+
/// </summary>
162
162
+
/// <param name="lookupKey">The lookup key that uniquely identifies textures in the cache.</param>
163
163
+
/// <param name="texture">The texture to be cached and returned.</param>
164
164
+
/// <returns>The texture to be returned.</returns>
165
165
+
[CanBeNull]
166
166
+
protected virtual Texture CacheAndReturnTexture([NotNull] string lookupKey, [CanBeNull] Texture texture)
167
167
+
{
168
168
+
lock (textureCache)
169
169
+
return textureCache[lookupKey] = texture;
148
170
}
149
171
150
172
/// <summary>