public AsyncTextureReader(RenderTexture texture) { Status = AsyncTextureReaderStatus.Idle; Texture = texture; // WARNING - if you change this, you'll need to update code below Debug.Assert( texture.format == RenderTextureFormat.ARGBFloat || texture.format == RenderTextureFormat.RGFloat || texture.format == RenderTextureFormat.RFloat || texture.format == RenderTextureFormat.ARGB32); Debug.Assert(texture.dimension == TextureDimension.Tex2D); var sync = System.Environment.GetEnvironmentVariable("FORCE_SYNC_GPU_READBACK"); if (sync == null) { if (SystemInfo.supportsAsyncGPUReadback) { int length; Type = ReadType.Native; if (texture.format == RenderTextureFormat.ARGBFloat) { BytesPerPixel = 16; NativeReadFormat = TextureFormat.RGBAFloat; length = Texture.width * Texture.height; } else if (texture.format == RenderTextureFormat.RGFloat) { BytesPerPixel = 8; NativeReadFormat = TextureFormat.RGFloat; length = Texture.width * Texture.height; } else if (texture.format == RenderTextureFormat.RFloat) { BytesPerPixel = 4; NativeReadFormat = TextureFormat.RFloat; length = Texture.width * Texture.height; } else // if (texture.format == RenderTextureFormat.ARGB32) { BytesPerPixel = 3; NativeReadFormat = TextureFormat.RGB24; length = Texture.width * Texture.height * BytesPerPixel; } Data = new NativeArray <T>(length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); return; } if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLCore && SystemInfo.operatingSystemFamily == OperatingSystemFamily.Linux) { var version = SystemInfo.graphicsDeviceVersion; version = version.Split(new char[] { ' ' }, 3)[1]; var parts = version.Split(new char[] { '.' }); int major = int.Parse(parts[0]); int minor = int.Parse(parts[1]); //Debug.Log($"OpenGL version = {major}.{minor}"); if (major > 3 || major == 3 && minor >= 2) // GL_ARB_sync { debug = new DebugDelegate(DebugCallback); AsyncTextureReaderImports.AsyncTextureReaderSetDebug(Marshal.GetFunctionPointerForDelegate(debug)); int length; if (texture.format == RenderTextureFormat.ARGBFloat) { BytesPerPixel = 16; length = Texture.width * Texture.height; } else if (texture.format == RenderTextureFormat.RGFloat) { BytesPerPixel = 8; length = Texture.width * Texture.height; } else if (texture.format == RenderTextureFormat.RFloat) { BytesPerPixel = 4; length = Texture.width * Texture.height; } else // if (texture.format == RenderTextureFormat.ARGB32) { BytesPerPixel = 4; length = Texture.width * Texture.height * BytesPerPixel; } texture.Create(); Data = new NativeArray <T>(length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); LinuxId = AsyncTextureReaderImports.AsyncTextureReaderCreate(texture.GetNativeTexturePtr(), Data.Length); if (LinuxId >= 0) { LinuxUpdate = AsyncTextureReaderImports.AsyncTextureReaderGetUpdate(); GL.IssuePluginEvent(LinuxUpdate, LinuxId); Type = ReadType.LinuxOpenGL; } else { Debug.Log("ERROR: failed to create native AsyncTextureReader"); } return; } } } if (texture.format != RenderTextureFormat.ARGB32) { Debug.Log("ERROR: fallback AsyncTextureReader supports only ARGB32 texture format"); Type = ReadType.None; return; } Type = ReadType.Sync; BytesPerPixel = 3; Data = new NativeArray <T>(texture.width * texture.height * BytesPerPixel, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); ReadTexture = new Texture2D(texture.width, texture.height, TextureFormat.RGB24, false); }
public AsyncTextureReader(RenderTexture texture) { Status = AsyncTextureReaderStatus.Idle; Texture = texture; // WARNING - if you change this, you'll need to update code below Debug.Assert( texture.format == RenderTextureFormat.ARGBFloat || texture.format == RenderTextureFormat.RGFloat || texture.format == RenderTextureFormat.RFloat || texture.format == RenderTextureFormat.ARGB32); Debug.Assert(texture.dimension == TextureDimension.Tex2D); var sync = System.Environment.GetEnvironmentVariable("FORCE_SYNC_GPU_READBACK"); if (sync == null) { if (SystemInfo.supportsAsyncGPUReadback) { Type = ReadType.Native; if (texture.format == RenderTextureFormat.ARGBFloat) { BytesPerPixel = 16; NativeReadFormat = TextureFormat.RGBAFloat; ElementCount = Texture.width * Texture.height; SizeInBytes = ElementCount * BytesPerPixel; } else if (texture.format == RenderTextureFormat.RGFloat) { BytesPerPixel = 8; NativeReadFormat = TextureFormat.RGFloat; ElementCount = Texture.width * Texture.height; SizeInBytes = ElementCount * BytesPerPixel; } else if (texture.format == RenderTextureFormat.RFloat) { BytesPerPixel = 4; NativeReadFormat = TextureFormat.RFloat; ElementCount = Texture.width * Texture.height; SizeInBytes = ElementCount * BytesPerPixel; } else // if (texture.format == RenderTextureFormat.ARGB32) { BytesPerPixel = 3; NativeReadFormat = TextureFormat.RGB24; ElementCount = Texture.width * Texture.height * BytesPerPixel; SizeInBytes = ElementCount; } Data = new T[ElementCount]; return; } if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLCore && SystemInfo.operatingSystemFamily == OperatingSystemFamily.Linux) { var version = SystemInfo.graphicsDeviceVersion; version = version.Split(new char[] { ' ' }, 3)[1]; var parts = version.Split(new char[] { '.' }); int major = int.Parse(parts[0]); int minor = int.Parse(parts[1]); //Debug.Log($"OpenGL version = {major}.{minor}"); // OpenGL extensions required: // ARB_buffer_storage (from 4.4) // ARB_shader_image_load_store (from 4.2) // ARB_sync (from 3.2) if (major > 4 || major == 4 && minor >= 4) { debug = new DebugDelegate(DebugCallback); AsyncTextureReaderImports.AsyncTextureReaderSetDebug(Marshal.GetFunctionPointerForDelegate(debug)); if (texture.format == RenderTextureFormat.ARGBFloat) { BytesPerPixel = 16; ElementCount = Texture.width * Texture.height; SizeInBytes = ElementCount * BytesPerPixel; } else if (texture.format == RenderTextureFormat.RGFloat) { BytesPerPixel = 8; ElementCount = Texture.width * Texture.height; SizeInBytes = ElementCount * BytesPerPixel; } else if (texture.format == RenderTextureFormat.RFloat) { BytesPerPixel = 4; ElementCount = Texture.width * Texture.height; SizeInBytes = ElementCount * BytesPerPixel; } else // if (texture.format == RenderTextureFormat.ARGB32) { BytesPerPixel = 4; ElementCount = Texture.width * Texture.height * BytesPerPixel; SizeInBytes = ElementCount; } Data = new T[ElementCount]; texture.Create(); LinuxId = AsyncTextureReaderImports.AsyncTextureReaderCreate(texture.GetNativeTexturePtr(), SizeInBytes); if (LinuxId >= 0) { LinuxUpdate = AsyncTextureReaderImports.AsyncTextureReaderGetUpdate(); GL.IssuePluginEvent(LinuxUpdate, LinuxId); Type = ReadType.LinuxOpenGL; } else { Debug.Log("ERROR: failed to create native AsyncTextureReader"); } return; } } } if (texture.format != RenderTextureFormat.ARGB32) { Debug.Log("ERROR: fallback AsyncTextureReader supports only ARGB32 texture format"); Type = ReadType.None; return; } Type = ReadType.Sync; BytesPerPixel = 3; ElementCount = Texture.width * Texture.height * BytesPerPixel; SizeInBytes = ElementCount; Data = new T[ElementCount]; ReadTexture = new Texture2D(texture.width, texture.height, TextureFormat.RGB24, false); }
public void Update() { if (Texture.IsCreated() == false) { // need to recreate native RenderTexture handle, because it is lost // this happens, for example, when you resize Unity Editor window on Linux if (Type == ReadType.Native) { NativeReadRequest.WaitForCompletion(); } else if (Type == ReadType.LinuxOpenGL) { AsyncTextureReaderImports.AsyncTextureReaderDestroy(LinuxId); GL.IssuePluginEvent(LinuxUpdate, LinuxId); Texture.Create(); LinuxId = AsyncTextureReaderImports.AsyncTextureReaderCreate(Texture.GetNativeTexturePtr(), Data.Length); GL.IssuePluginEvent(LinuxUpdate, LinuxId); } Status = AsyncTextureReaderStatus.Idle; return; } if (Status != AsyncTextureReaderStatus.Reading) { return; } if (Type == ReadType.Native) { if (NativeReadRequest.done) { if (NativeReadRequest.layerCount == 0) { // start reading request was not issued yet return; } // this will happen only if AsyncGPUReadback.Request was issued if (NativeReadRequest.hasError) { return; } Data.CopyFrom(NativeReadRequest.GetData <T>()); Status = AsyncTextureReaderStatus.Finished; } } else if (Type == ReadType.LinuxOpenGL) { if (LinuxId >= 0) { Status = AsyncTextureReaderImports.AsyncTextureReaderGetStatus(LinuxId); if (Status != AsyncTextureReaderStatus.Finished) { GL.IssuePluginEvent(LinuxUpdate, LinuxId); Status = AsyncTextureReaderImports.AsyncTextureReaderGetStatus(LinuxId); } } else { Status = AsyncTextureReaderStatus.Finished; } } else if (Type == ReadType.Sync) { var current = RenderTexture.active; RenderTexture.active = Texture; ReadTexture.ReadPixels(new Rect(0, 0, Texture.width, Texture.height), 0, 0); ReadTexture.Apply(); RenderTexture.active = current; int size = ReadTexture.width * ReadTexture.height * BytesPerPixel; byte[] bytes = ReadTexture.GetRawTextureData(); unsafe { fixed(void *ptr = bytes) { Buffer.MemoryCopy(ptr, Data.GetUnsafePtr(), size, size); } } Status = AsyncTextureReaderStatus.Finished; } }
public void Update() { if (Texture.IsCreated() == false) { // need to recreate native RenderTexture handle, because it is lost // this happens, for example, when you resize Unity Editor window on Linux if (Type == ReadType.Native) { NativeReadRequest.WaitForCompletion(); } else if (Type == ReadType.LinuxOpenGL) { AsyncTextureReaderImports.AsyncTextureReaderDestroy(LinuxId); GL.IssuePluginEvent(LinuxUpdate, LinuxId); Texture.Create(); LinuxId = AsyncTextureReaderImports.AsyncTextureReaderCreate(Texture.GetNativeTexturePtr(), SizeInBytes); GL.IssuePluginEvent(LinuxUpdate, LinuxId); } Status = AsyncTextureReaderStatus.Idle; return; } if (Status != AsyncTextureReaderStatus.Reading) { return; } if (Type == ReadType.Native) { if (NativeReadRequest.done) { if (NativeReadRequest.layerCount == 0) { // start reading request was not issued yet return; } // this will happen only if AsyncGPUReadback.Request was issued if (NativeReadRequest.hasError) { return; } NativeReadRequest.GetData <T>().CopyTo(Data); Status = AsyncTextureReaderStatus.Finished; } } else if (Type == ReadType.LinuxOpenGL) { if (LinuxId >= 0) { Status = AsyncTextureReaderImports.AsyncTextureReaderGetStatus(LinuxId); if (Status != AsyncTextureReaderStatus.Finished) { GL.IssuePluginEvent(LinuxUpdate, LinuxId); Status = AsyncTextureReaderImports.AsyncTextureReaderGetStatus(LinuxId); } if (Status == AsyncTextureReaderStatus.Finished) { IntPtr src = AsyncTextureReaderImports.AsyncTextureReaderGetBuffer(LinuxId); var handle = GCHandle.Alloc(Data, GCHandleType.Pinned); try { IntPtr dst = handle.AddrOfPinnedObject(); unsafe { Buffer.MemoryCopy((void *)src, (void *)dst, SizeInBytes, SizeInBytes); } } finally { handle.Free(); } } } else { Status = AsyncTextureReaderStatus.Finished; } } else if (Type == ReadType.Sync) { var current = RenderTexture.active; RenderTexture.active = Texture; ReadTexture.ReadPixels(new Rect(0, 0, Texture.width, Texture.height), 0, 0); ReadTexture.Apply(); RenderTexture.active = current; ReadTexture.GetRawTextureData <T>().CopyTo(Data); Status = AsyncTextureReaderStatus.Finished; } }