Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow Texture2D.GetData On Non UI Threads #7569

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
99 changes: 50 additions & 49 deletions MonoGame.Framework/Platform/Graphics/Texture2D.OpenGL.cs
Expand Up @@ -225,64 +225,65 @@ private void PlatformSetData<T>(int level, int arraySlice, Rectangle rect, T[] d
private void PlatformGetData<T>(int level, int arraySlice, Rectangle rect, T[] data, int startIndex, int elementCount)
where T : struct
{
Threading.EnsureUIThread();

Threading.BlockOnUIThread(() =>
{
#if GLES
// TODO: check for for non renderable formats (formats that can't be attached to FBO)

var framebufferId = 0;
GL.GenFramebuffers(1, out framebufferId);
GraphicsExtensions.CheckGLError();
GL.BindFramebuffer(FramebufferTarget.Framebuffer, framebufferId);
GraphicsExtensions.CheckGLError();
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.Texture2D, this.glTexture, 0);
GraphicsExtensions.CheckGLError();

GL.ReadPixels(rect.X, rect.Y, rect.Width, rect.Height, this.glFormat, this.glType, data);
GraphicsExtensions.CheckGLError();
GL.DeleteFramebuffers(1, ref framebufferId);
#else
var tSizeInByte = ReflectionHelpers.SizeOf<T>.Get();
GL.BindTexture(TextureTarget.Texture2D, this.glTexture);
GL.PixelStore(PixelStoreParameter.PackAlignment, Math.Min(tSizeInByte, 8));
// TODO: check for for non renderable formats (formats that can't be attached to FBO)

if (glFormat == GLPixelFormat.CompressedTextureFormats)
{
// Note: for compressed format Format.GetSize() returns the size of a 4x4 block
var pixelToT = Format.GetSize() / tSizeInByte;
var tFullWidth = Math.Max(this.width >> level, 1) / 4 * pixelToT;
var temp = new T[Math.Max(this.height >> level, 1) / 4 * tFullWidth];
GL.GetCompressedTexImage(TextureTarget.Texture2D, level, temp);
var framebufferId = 0;
GL.GenFramebuffers(1, out framebufferId);
GraphicsExtensions.CheckGLError();
GL.BindFramebuffer(FramebufferTarget.Framebuffer, framebufferId);
GraphicsExtensions.CheckGLError();
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.Texture2D, this.glTexture, 0);
GraphicsExtensions.CheckGLError();

var rowCount = rect.Height / 4;
var tRectWidth = rect.Width / 4 * Format.GetSize() / tSizeInByte;
for (var r = 0; r < rowCount; r++)
{
var tempStart = rect.X / 4 * pixelToT + (rect.Top / 4 + r) * tFullWidth;
var dataStart = startIndex + r * tRectWidth;
Array.Copy(temp, tempStart, data, dataStart, tRectWidth);
}
}
else
{
// we need to convert from our format size to the size of T here
var tFullWidth = Math.Max(this.width >> level, 1) * Format.GetSize() / tSizeInByte;
var temp = new T[Math.Max(this.height >> level, 1) * tFullWidth];
GL.GetTexImage(TextureTarget.Texture2D, level, glFormat, glType, temp);
GL.ReadPixels(rect.X, rect.Y, rect.Width, rect.Height, this.glFormat, this.glType, data);
GraphicsExtensions.CheckGLError();
GL.DeleteFramebuffers(1, ref framebufferId);
#else
var tSizeInByte = ReflectionHelpers.SizeOf<T>.Get();
GL.BindTexture(TextureTarget.Texture2D, this.glTexture);
GL.PixelStore(PixelStoreParameter.PackAlignment, Math.Min(tSizeInByte, 8));

var pixelToT = Format.GetSize() / tSizeInByte;
var rowCount = rect.Height;
var tRectWidth = rect.Width * pixelToT;
for (var r = 0; r < rowCount; r++)
if (glFormat == GLPixelFormat.CompressedTextureFormats)
{
var tempStart = rect.X * pixelToT + (r + rect.Top) * tFullWidth;
var dataStart = startIndex + r * tRectWidth;
Array.Copy(temp, tempStart, data, dataStart, tRectWidth);
// Note: for compressed format Format.GetSize() returns the size of a 4x4 block
var pixelToT = Format.GetSize() / tSizeInByte;
var tFullWidth = Math.Max(this.width >> level, 1) / 4 * pixelToT;
var temp = new T[Math.Max(this.height >> level, 1) / 4 * tFullWidth];
GL.GetCompressedTexImage(TextureTarget.Texture2D, level, temp);
GraphicsExtensions.CheckGLError();

var rowCount = rect.Height / 4;
var tRectWidth = rect.Width / 4 * Format.GetSize() / tSizeInByte;
for (var r = 0; r < rowCount; r++)
{
var tempStart = rect.X / 4 * pixelToT + (rect.Top / 4 + r) * tFullWidth;
var dataStart = startIndex + r * tRectWidth;
Array.Copy(temp, tempStart, data, dataStart, tRectWidth);
}
}
else
{
// we need to convert from our format size to the size of T here
var tFullWidth = Math.Max(this.width >> level, 1) * Format.GetSize() / tSizeInByte;
var temp = new T[Math.Max(this.height >> level, 1) * tFullWidth];
GL.GetTexImage(TextureTarget.Texture2D, level, glFormat, glType, temp);
GraphicsExtensions.CheckGLError();

var pixelToT = Format.GetSize() / tSizeInByte;
var rowCount = rect.Height;
var tRectWidth = rect.Width * pixelToT;
for (var r = 0; r < rowCount; r++)
{
var tempStart = rect.X * pixelToT + (r + rect.Top) * tFullWidth;
var dataStart = startIndex + r * tRectWidth;
Array.Copy(temp, tempStart, data, dataStart, tRectWidth);
}
}
}
#endif
});
}

#if IOS
Expand Down
2 changes: 2 additions & 0 deletions MonoGame.Framework/Platform/Threading.cs
Expand Up @@ -139,6 +139,7 @@ internal static void BlockOnUIThread<TState>(Action<TState> action, TState state

try
{
Debug.WriteLine("Thread blocked, waiting for action to be completed by the UI thread");
resetEvent.Wait(); // we don't know how much time the operation will take, so let's wait indefinitely
}
finally
Expand Down Expand Up @@ -196,6 +197,7 @@ internal static void Run()
foreach (Action queuedAction in _queuedActions)
{
queuedAction.Invoke();
Debug.WriteLine("Pending action completed by the UI thread");
}
_queuedActions.Clear();
}
Expand Down