XNA Xbox 360 内容管理器线程冻结绘制线程
我目前有一款游戏可以采用大图像(很容易大于 1MB)作为背景。我确切地知道这种转换应该何时发生,因此我创建了一个加载器类来处理在后台加载这些大图像,但是当我加载图像时,它仍然冻结发生绘图的主线程。由于此代码在 360 上运行,我将线程移至第四个硬件线程,但这似乎没有帮助。下面是我正在使用的类。任何关于为什么我的新内容管理器应该在它自己的线程中中断我的主线程中的绘制的想法将不胜感激。
namespace FileSystem
{
/// <summary>
/// This is used to reference how many objects reference this texture.
/// Everytime someone references a texture we increase the iNumberOfReferences.
/// When a class calls remove on a specific texture we check to see if anything
/// else is referencing the class, if it is we don't remove it. If there isn't
/// anything referencing the texture its safe to dispose of.
/// </summary>
class TextureContainer
{
public uint uiNumberOfReferences = 0;
public Texture2D texture;
}
/// <summary>
/// This class loads all the files from the Content.
/// </summary>
static class FileManager
{
static Microsoft.Xna.Framework.Content.ContentManager Content;
static EventWaitHandle wh = new AutoResetEvent(false);
static Dictionary<string, TextureContainer> Texture2DResourceDictionary;
static List<Texture2D> TexturesToDispose;
static List<String> TexturesToLoad;
static int iProcessor = 4;
private static object threadMutex = new object();
private static object Texture2DMutex = new object();
private static object loadingMutex = new object();
private static bool bLoadingTextures = false;
/// <summary>
/// Returns if we are loading textures or not.
/// </summary>
public static bool LoadingTexture
{
get {
lock (loadingMutex)
{
return bLoadingTextures;
}
}
}
/// <summary>
/// Since this is an static class. This is the constructor for the file loadeder. This is the version
/// for the Xbox 360.
/// </summary>
/// <param name="_Content"></param>
public static void Initalize(IServiceProvider serviceProvider, string rootDirectory, int _iProcessor )
{
Content = new Microsoft.Xna.Framework.Content.ContentManager(serviceProvider, rootDirectory);
Texture2DResourceDictionary = new Dictionary<string, TextureContainer>();
TexturesToDispose = new List<Texture2D>();
iProcessor = _iProcessor;
CreateThread();
}
/// <summary>
/// Since this is an static class. This is the constructor for the file loadeder.
/// </summary>
/// <param name="_Content"></param>
public static void Initalize(IServiceProvider serviceProvider, string rootDirectory)
{
Content = new Microsoft.Xna.Framework.Content.ContentManager(serviceProvider, rootDirectory);
Texture2DResourceDictionary = new Dictionary<string, TextureContainer>();
TexturesToDispose = new List<Texture2D>();
CreateThread();
}
/// <summary>
/// Creates the thread incase we wanted to set up some parameters
/// Outside of the constructor.
/// </summary>
static public void CreateThread()
{
Thread t = new Thread(new ThreadStart(StartThread));
t.Start();
}
// This is the function that we thread.
static public void StartThread()
{
//BBSThreadClass BBSTC = (BBSThreadClass)_oData;
FileManager.Execute();
}
/// <summary>
/// This thread shouldn't be called by the outside world.
/// It allows the File Manager to loop.
/// </summary>
static private void Execute()
{
// Make sure our thread is on the correct processor on the XBox 360.
#if WINDOWS
#else
Thread.CurrentThread.SetProcessorAffinity(new int[] { iProcessor });
Thread.CurrentThread.IsBackground = true;
#endif
// This loop will load textures into ram for us away from the main thread.
while (true)
{
wh.WaitOne();
// Locking down our data while we process it.
lock (threadMutex)
{
lock (loadingMutex)
{
bLoadingTextures = true;
}
bool bContainsKey = false;
for (int con = 0; con < TexturesToLoad.Count; con++)
{
// If we have already loaded the texture into memory reference
// the one in the dictionary.
lock (Texture2DMutex)
{
bContainsKey = Texture2DResourceDictionary.ContainsKey(TexturesToLoad[con]);
}
if (bContainsKey)
{
// Do nothing
}
// Otherwise load it into the dictionary and then reference the
// copy in the dictionary
else
{
TextureContainer TC = new TextureContainer();
TC.uiNumberOfReferences = 1; // We start out with 1 referece.
// Loading the texture into memory.
try
{
TC.texture = Content.Load<Texture2D>(TexturesToLoad[con]);
// This is passed into the dictionary, thus there is only one copy of
// the texture in memory.
// There is an issue with Sprite Batch and disposing textures.
// This will have to wait until its figured out.
lock (Texture2DMutex)
{
bContainsKey = Texture2DResourceDictionary.ContainsKey(TexturesToLoad[con]);
Texture2DResourceDictionary.Add(TexturesToLoad[con], TC);
}
// We don't have the find the reference to the container since we
// already have it.
}
// Occasionally our texture will already by loaded by another thread while
// this thread is operating. This mainly happens on the first level.
catch (Exception e)
{
// If this happens we don't worry about it since this thread only loads
// texture data and if its already there we don't need to load it.
}
}
Thread.Sleep(100);
}
}
lock (loadingMutex)
{
bLoadingTextures = false;
}
}
}
static public void LoadTextureList(List<string> _textureList)
{
// Ensuring that we can't creating threading problems.
lock (threadMutex)
{
TexturesToLoad = _textureList;
}
wh.Set();
}
/// <summary>
/// This loads a 2D texture which represents a 2D grid of Texels.
/// </summary>
/// <param name="_textureName">The name of the picture you wish to load.</param>
/// <returns>Holds the image data.</returns>
public static Texture2D LoadTexture2D( string _textureName )
{
TextureContainer temp;
lock (Texture2DMutex)
{
bool bContainsKey = false;
// If we have already loaded the texture into memory reference
// the one in the dictionary.
lock (Texture2DMutex)
{
bContainsKey = Texture2DResourceDictionary.ContainsKey(_textureName);
if (bContainsKey)
{
temp = Texture2DResourceDictionary[_textureName];
temp.uiNumberOfReferences++; // Incrementing the number of references
}
// Otherwise load it into the dictionary and then reference the
// copy in the dictionary
else
{
TextureContainer TC = new TextureContainer();
TC.uiNumberOfReferences = 1; // We start out with 1 referece.
// Loading the texture into memory.
try
{
TC.texture = Content.Load<Texture2D>(_textureName);
// This is passed into the dictionary, thus there is only one copy of
// the texture in memory.
}
// Occasionally our texture will already by loaded by another thread while
// this thread is operating. This mainly happens on the first level.
catch(Exception e)
{
temp = Texture2DResourceDictionary[_textureName];
temp.uiNumberOfReferences++; // Incrementing the number of references
}
// There is an issue with Sprite Batch and disposing textures.
// This will have to wait until its figured out.
Texture2DResourceDictionary.Add(_textureName, TC);
// We don't have the find the reference to the container since we
// already have it.
temp = TC;
}
}
}
// Return a reference to the texture
return temp.texture;
}
/// <summary>
/// Go through our dictionary and remove any references to the
/// texture passed in.
/// </summary>
/// <param name="texture">Texture to remove from texture dictionary.</param>
public static void RemoveTexture2D(Texture2D texture)
{
foreach (KeyValuePair<string, TextureContainer> pair in Texture2DResourceDictionary)
{
// Do our references match?
if (pair.Value.texture == texture)
{
// Only one object or less holds a reference to the
// texture. Logically it should be safe to remove.
if (pair.Value.uiNumberOfReferences <= 1)
{
// Grabing referenc to texture
TexturesToDispose.Add(pair.Value.texture);
// We are about to release the memory of the texture,
// thus we make sure no one else can call this member
// in the dictionary.
Texture2DResourceDictionary.Remove(pair.Key);
// Once we have removed the texture we don't want to create an exception.
// So we will stop looking in the list since it has changed.
break;
}
// More than one Object has a reference to this texture.
// So we will not be removing it from memory and instead
// simply marking down the number of references by 1.
else
{
pair.Value.uiNumberOfReferences--;
}
}
}
}
/*public static void DisposeTextures()
{
int Count = TexturesToDispose.Count;
// If there are any textures to dispose of.
if (Count > 0)
{
for (int con = 0; con < TexturesToDispose.Count; con++)
{
// =!THIS REMOVES THE TEXTURE FROM MEMORY!=
// This is not like a normal dispose. This will actually
// remove the object from memory. Texture2D is inherited
// from GraphicsResource which removes it self from
// memory on dispose. Very nice for game efficency,
// but "dangerous" in managed land.
Texture2D Temp = TexturesToDispose[con];
Temp.Dispose();
}
// Remove textures we've already disposed of.
TexturesToDispose.Clear();
}
}*/
/// <summary>
/// This loads a 2D texture which represnets a font.
/// </summary>
/// <param name="_textureName">The name of the font you wish to load.</param>
/// <returns>Holds the font data.</returns>
public static SpriteFont LoadFont( string _fontName )
{
SpriteFont temp = Content.Load<SpriteFont>( _fontName );
return temp;
}
/// <summary>
/// This loads an XML document.
/// </summary>
/// <param name="_textureName">The name of the XML document you wish to load.</param>
/// <returns>Holds the XML data.</returns>
public static XmlDocument LoadXML( string _fileName )
{
XmlDocument temp = Content.Load<XmlDocument>( _fileName );
return temp;
}
/// <summary>
/// This loads a sound file.
/// </summary>
/// <param name="_fileName"></param>
/// <returns></returns>
public static SoundEffect LoadSound( string _fileName )
{
SoundEffect temp = Content.Load<SoundEffect>(_fileName);
return temp;
}
}
}
I currently have a game that takes in large images, easily bigger than 1MB, to serve as backgrounds. I know exactly when this transition is supposed to take place, so I made a loader class to handle loading these large images in the background, but when I load the images it still freezes the main thread where the drawing takes place. Since this code runs on the 360 I moved the thread to the 4th hardware thread, but that doesn't seem to help. Below is the class I am using. Any thoughts as to why my new content manager which should be in its own thread is interrupting the draw in my main thread would be appreciated.
namespace FileSystem
{
/// <summary>
/// This is used to reference how many objects reference this texture.
/// Everytime someone references a texture we increase the iNumberOfReferences.
/// When a class calls remove on a specific texture we check to see if anything
/// else is referencing the class, if it is we don't remove it. If there isn't
/// anything referencing the texture its safe to dispose of.
/// </summary>
class TextureContainer
{
public uint uiNumberOfReferences = 0;
public Texture2D texture;
}
/// <summary>
/// This class loads all the files from the Content.
/// </summary>
static class FileManager
{
static Microsoft.Xna.Framework.Content.ContentManager Content;
static EventWaitHandle wh = new AutoResetEvent(false);
static Dictionary<string, TextureContainer> Texture2DResourceDictionary;
static List<Texture2D> TexturesToDispose;
static List<String> TexturesToLoad;
static int iProcessor = 4;
private static object threadMutex = new object();
private static object Texture2DMutex = new object();
private static object loadingMutex = new object();
private static bool bLoadingTextures = false;
/// <summary>
/// Returns if we are loading textures or not.
/// </summary>
public static bool LoadingTexture
{
get {
lock (loadingMutex)
{
return bLoadingTextures;
}
}
}
/// <summary>
/// Since this is an static class. This is the constructor for the file loadeder. This is the version
/// for the Xbox 360.
/// </summary>
/// <param name="_Content"></param>
public static void Initalize(IServiceProvider serviceProvider, string rootDirectory, int _iProcessor )
{
Content = new Microsoft.Xna.Framework.Content.ContentManager(serviceProvider, rootDirectory);
Texture2DResourceDictionary = new Dictionary<string, TextureContainer>();
TexturesToDispose = new List<Texture2D>();
iProcessor = _iProcessor;
CreateThread();
}
/// <summary>
/// Since this is an static class. This is the constructor for the file loadeder.
/// </summary>
/// <param name="_Content"></param>
public static void Initalize(IServiceProvider serviceProvider, string rootDirectory)
{
Content = new Microsoft.Xna.Framework.Content.ContentManager(serviceProvider, rootDirectory);
Texture2DResourceDictionary = new Dictionary<string, TextureContainer>();
TexturesToDispose = new List<Texture2D>();
CreateThread();
}
/// <summary>
/// Creates the thread incase we wanted to set up some parameters
/// Outside of the constructor.
/// </summary>
static public void CreateThread()
{
Thread t = new Thread(new ThreadStart(StartThread));
t.Start();
}
// This is the function that we thread.
static public void StartThread()
{
//BBSThreadClass BBSTC = (BBSThreadClass)_oData;
FileManager.Execute();
}
/// <summary>
/// This thread shouldn't be called by the outside world.
/// It allows the File Manager to loop.
/// </summary>
static private void Execute()
{
// Make sure our thread is on the correct processor on the XBox 360.
#if WINDOWS
#else
Thread.CurrentThread.SetProcessorAffinity(new int[] { iProcessor });
Thread.CurrentThread.IsBackground = true;
#endif
// This loop will load textures into ram for us away from the main thread.
while (true)
{
wh.WaitOne();
// Locking down our data while we process it.
lock (threadMutex)
{
lock (loadingMutex)
{
bLoadingTextures = true;
}
bool bContainsKey = false;
for (int con = 0; con < TexturesToLoad.Count; con++)
{
// If we have already loaded the texture into memory reference
// the one in the dictionary.
lock (Texture2DMutex)
{
bContainsKey = Texture2DResourceDictionary.ContainsKey(TexturesToLoad[con]);
}
if (bContainsKey)
{
// Do nothing
}
// Otherwise load it into the dictionary and then reference the
// copy in the dictionary
else
{
TextureContainer TC = new TextureContainer();
TC.uiNumberOfReferences = 1; // We start out with 1 referece.
// Loading the texture into memory.
try
{
TC.texture = Content.Load<Texture2D>(TexturesToLoad[con]);
// This is passed into the dictionary, thus there is only one copy of
// the texture in memory.
// There is an issue with Sprite Batch and disposing textures.
// This will have to wait until its figured out.
lock (Texture2DMutex)
{
bContainsKey = Texture2DResourceDictionary.ContainsKey(TexturesToLoad[con]);
Texture2DResourceDictionary.Add(TexturesToLoad[con], TC);
}
// We don't have the find the reference to the container since we
// already have it.
}
// Occasionally our texture will already by loaded by another thread while
// this thread is operating. This mainly happens on the first level.
catch (Exception e)
{
// If this happens we don't worry about it since this thread only loads
// texture data and if its already there we don't need to load it.
}
}
Thread.Sleep(100);
}
}
lock (loadingMutex)
{
bLoadingTextures = false;
}
}
}
static public void LoadTextureList(List<string> _textureList)
{
// Ensuring that we can't creating threading problems.
lock (threadMutex)
{
TexturesToLoad = _textureList;
}
wh.Set();
}
/// <summary>
/// This loads a 2D texture which represents a 2D grid of Texels.
/// </summary>
/// <param name="_textureName">The name of the picture you wish to load.</param>
/// <returns>Holds the image data.</returns>
public static Texture2D LoadTexture2D( string _textureName )
{
TextureContainer temp;
lock (Texture2DMutex)
{
bool bContainsKey = false;
// If we have already loaded the texture into memory reference
// the one in the dictionary.
lock (Texture2DMutex)
{
bContainsKey = Texture2DResourceDictionary.ContainsKey(_textureName);
if (bContainsKey)
{
temp = Texture2DResourceDictionary[_textureName];
temp.uiNumberOfReferences++; // Incrementing the number of references
}
// Otherwise load it into the dictionary and then reference the
// copy in the dictionary
else
{
TextureContainer TC = new TextureContainer();
TC.uiNumberOfReferences = 1; // We start out with 1 referece.
// Loading the texture into memory.
try
{
TC.texture = Content.Load<Texture2D>(_textureName);
// This is passed into the dictionary, thus there is only one copy of
// the texture in memory.
}
// Occasionally our texture will already by loaded by another thread while
// this thread is operating. This mainly happens on the first level.
catch(Exception e)
{
temp = Texture2DResourceDictionary[_textureName];
temp.uiNumberOfReferences++; // Incrementing the number of references
}
// There is an issue with Sprite Batch and disposing textures.
// This will have to wait until its figured out.
Texture2DResourceDictionary.Add(_textureName, TC);
// We don't have the find the reference to the container since we
// already have it.
temp = TC;
}
}
}
// Return a reference to the texture
return temp.texture;
}
/// <summary>
/// Go through our dictionary and remove any references to the
/// texture passed in.
/// </summary>
/// <param name="texture">Texture to remove from texture dictionary.</param>
public static void RemoveTexture2D(Texture2D texture)
{
foreach (KeyValuePair<string, TextureContainer> pair in Texture2DResourceDictionary)
{
// Do our references match?
if (pair.Value.texture == texture)
{
// Only one object or less holds a reference to the
// texture. Logically it should be safe to remove.
if (pair.Value.uiNumberOfReferences <= 1)
{
// Grabing referenc to texture
TexturesToDispose.Add(pair.Value.texture);
// We are about to release the memory of the texture,
// thus we make sure no one else can call this member
// in the dictionary.
Texture2DResourceDictionary.Remove(pair.Key);
// Once we have removed the texture we don't want to create an exception.
// So we will stop looking in the list since it has changed.
break;
}
// More than one Object has a reference to this texture.
// So we will not be removing it from memory and instead
// simply marking down the number of references by 1.
else
{
pair.Value.uiNumberOfReferences--;
}
}
}
}
/*public static void DisposeTextures()
{
int Count = TexturesToDispose.Count;
// If there are any textures to dispose of.
if (Count > 0)
{
for (int con = 0; con < TexturesToDispose.Count; con++)
{
// =!THIS REMOVES THE TEXTURE FROM MEMORY!=
// This is not like a normal dispose. This will actually
// remove the object from memory. Texture2D is inherited
// from GraphicsResource which removes it self from
// memory on dispose. Very nice for game efficency,
// but "dangerous" in managed land.
Texture2D Temp = TexturesToDispose[con];
Temp.Dispose();
}
// Remove textures we've already disposed of.
TexturesToDispose.Clear();
}
}*/
/// <summary>
/// This loads a 2D texture which represnets a font.
/// </summary>
/// <param name="_textureName">The name of the font you wish to load.</param>
/// <returns>Holds the font data.</returns>
public static SpriteFont LoadFont( string _fontName )
{
SpriteFont temp = Content.Load<SpriteFont>( _fontName );
return temp;
}
/// <summary>
/// This loads an XML document.
/// </summary>
/// <param name="_textureName">The name of the XML document you wish to load.</param>
/// <returns>Holds the XML data.</returns>
public static XmlDocument LoadXML( string _fileName )
{
XmlDocument temp = Content.Load<XmlDocument>( _fileName );
return temp;
}
/// <summary>
/// This loads a sound file.
/// </summary>
/// <param name="_fileName"></param>
/// <returns></returns>
public static SoundEffect LoadSound( string _fileName )
{
SoundEffect temp = Content.Load<SoundEffect>(_fileName);
return temp;
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
来自博客文章:加载屏幕动画期间锁定争用 作者:Shawn Hargreaves
但在您的情况下,因为您正在加载非常大的连续纹理,所以您可能只需要重新设计加载功能,以便在用户不需要看到任何动画时发生,因为您会期望有一个锁。
From the blog post: Lock Contention during Load Screen Animations by Shawn Hargreaves
In your case though, because you're loading very large contiguous textures, you may just need to redesign your loading feature to happen when the user doesn't need to see any animations because you will expect there to be a lock.
如果没有看到其余的代码,很难说,但我的猜测是你在某个地方存在锁争用。我做了一个加载 10 个大型模型的快速测试(大约 5 秒加载时间),我能够在单独的线程上加载它们,而无需暂停主线程。
Without seeing the rest of the code it is hard to say, but my guess is that you have lock contention someplace. I did a quick test of loading 10 massive models (about a 5 second load time) and I was able to load them on a separate thread without pausing the main thread.
问题是我在主线程中加载了大量较小的图形。我将这些添加到列表中以在线程中加载,问题就消失了。
所以你之前的答案是正确的。我只是没有正确使用东西。
The issue was that I loading a large amount of of smaller graphics in the main thread. I added these to the list for loading in the thread and the issues went away.
So you the previous answers were correct. I was simply not using things correctly.