private void getImageGL(Action <Image <Rgba32> > callback) { host.DrawThread.Scheduler.Add(() => { var image = new Image <Rgba32>((int)DrawWidth, (int)DrawHeight); var sharedData = (BufferedDrawNodeSharedData)typeof(BufferedContainer <Drawable>).GetField("sharedData", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(this); var fbo = new FrameBuffer(); fbo.Bind(); GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget2d.Texture2D, sharedData.MainBuffer.Texture.TextureId, 0); bool success = image.TryGetSinglePixelSpan(out var span); Debug.Assert(success); GL.ReadPixels(0, 0, (int)DrawWidth, (int)DrawHeight, PixelFormat.Rgba, PixelType.UnsignedByte, ref MemoryMarshal.GetReference(span)); fbo.Dispose(); GLWrapper.BindFrameBuffer(GLWrapper.DefaultFrameBuffer); callback(image); }); }
public void Bind() { if (IsBound) { return; } ensureLoaded(); GLWrapper.UseProgram(this); foreach (var uniform in uniformsArray) { if (uniform.HasChanged) { uniform.Update(); } } IsBound = true; }
public override void SetData(TextureUpload upload) { if (IsDisposed) { throw new ObjectDisposedException(ToString(), "Can not set data of a disposed texture."); } if (upload.Bounds.IsEmpty) { upload.Bounds = new RectangleI(0, 0, width, height); } IsTransparent = false; bool requireUpload = uploadQueue.Count == 0; uploadQueue.Enqueue(upload); if (requireUpload) { GLWrapper.EnqueueTextureUpload(this); } }
public override void Draw(Action <TexturedVertex2D> vertexAction) { base.Draw(vertexAction); if (texture?.Available != true || segments.Count == 0) { return; } GLWrapper.PushDepthInfo(DepthInfo.Default); Shader.Bind(); texture.TextureGL.WrapMode = TextureWrapMode.ClampToEdge; texture.TextureGL.Bind(); updateVertexBuffer(); Shader.Unbind(); GLWrapper.PopDepthInfo(); }
public void Bind() { if (IsBound) { return; } if (!Loaded) { return; } GLWrapper.UseProgram(this); previousShader = GLWrapper.CurrentShader; foreach (var kvp in uniforms) { kvp.Value.Update(); } IsBound = true; }
private ValueInvokeOnDisposal establishFrameBufferViewport(Vector2 roundedSize) { // Disable masking for generating the frame buffer since masking will be re-applied // when actually drawing later on anyways. This allows more information to be captured // in the frame buffer and helps with cached buffers being re-used. RectangleI screenSpaceMaskingRect = new RectangleI((int)Math.Floor(screenSpaceDrawRectangle.X), (int)Math.Floor(screenSpaceDrawRectangle.Y), (int)roundedSize.X + 1, (int)roundedSize.Y + 1); GLWrapper.PushMaskingInfo(new MaskingInfo { ScreenSpaceAABB = screenSpaceMaskingRect, MaskingRect = screenSpaceDrawRectangle, ToMaskingSpace = Matrix3.Identity, BlendRange = 1, AlphaExponent = 1, }, true); // Match viewport to FrameBuffer such that we don't draw unnecessary pixels. GLWrapper.PushViewport(new RectangleI(0, 0, (int)roundedSize.X, (int)roundedSize.Y)); return(new ValueInvokeOnDisposal(returnViewport)); }
/// <summary> /// Removes texture from GL memory. /// </summary> private void unload() { lock (this) { if (dataToBeUploaded != null) { FreeBuffer(dataToBeUploaded); } dataToBeUploaded = null; } int disposableId = textureId; if (disposableId <= 0) { return; } GLWrapper.DeleteTextures(disposableId); textureId = 0; }
public void TestOrtho() { var pixels = new[] { new Pixel(), new Pixel(), new Pixel(), new Pixel() }; _window = new GameWindow { Width = 200, Height = 200 }; _window.RenderFrame += (caller, args) => { IGL gl = new GLWrapper(); gl.MatrixMode(MatrixMode.Projection); gl.LoadIdentity(); gl.Ortho(0.0, 2.0, 0.0, 2.0, 0.0, 4.0); gl.Viewport(0, 0, 200, 200); gl.MatrixMode(MatrixMode.Modelview); gl.ReadBuffer(ReadBufferMode.Front); gl.Clear(ClearBufferMask.ColorBufferBit); gl.Begin(PrimitiveType.Quads); { gl.Color3(1.0f, 1.0f, 1.0f); gl.Vertex2(0.0f, 0.0f); gl.Vertex2(1.0f, 0.0f); gl.Vertex2(1.0f, 1.0f); gl.Vertex2(0.0f, 1.0f); } gl.End(); _window.SwapBuffers(); pixels[0].ReadBuffer(99, 99); pixels[1].ReadBuffer(99, 100); pixels[2].ReadBuffer(100, 100); pixels[3].ReadBuffer(100, 99); _window.Close(); }; _window.Run(); Assert.AreEqual(new Pixel(1.0f, 1.0f, 1.0f), pixels[0]); Assert.AreEqual(new Pixel(0.0f, 0.0f, 0.0f), pixels[1]); Assert.AreEqual(new Pixel(0.0f, 0.0f, 0.0f), pixels[2]); Assert.AreEqual(new Pixel(0.0f, 0.0f, 0.0f), pixels[3]); }
private void resize(int amountVertices) { Debug.Assert(!IsDisposed); T[] oldVertices = Vertices; Vertices = new T[amountVertices]; if (oldVertices != null) { for (int i = 0; i < oldVertices.Length && i < Vertices.Length; ++i) { Vertices[i] = oldVertices[i]; } } if (GLWrapper.BindBuffer(BufferTarget.ArrayBuffer, vboId)) { bind_attributes(); } GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(Vertices.Length * stride), IntPtr.Zero, usage); }
protected virtual void DrawChildrenOpaqueInteriors(DepthValue depthValue, Action <TexturedVertex2D> vertexAction) { bool canIncrement = depthValue.CanIncrement; // Assume that if we can't increment the depth value, no child can, thus nothing will be drawn. if (canIncrement) { updateTriangleBatch(); // Prefer to use own vertex batch instead of the parent-owned one. if (triangleBatch != null) { vertexAction = triangleBatch.AddAction; } if (maskingInfo != null) { GLWrapper.PushMaskingInfo(maskingInfo.Value); } } // We still need to invoke this method recursively for all children so their depth value is updated if (Children != null) { for (int i = Children.Count - 1; i >= 0; i--) { Children[i].DrawOpaqueInteriorSubTree(depthValue, vertexAction); } } // Assume that if we can't increment the depth value, no child can, thus nothing will be drawn. if (canIncrement) { if (maskingInfo != null) { GLWrapper.PopMaskingInfo(); } } }
public override void Draw(Action <TexturedVertex2D> vertexAction) { updateQuadBatch(); // Prefer to use own vertex batch instead of the parent-owned one. if (quadBatch != null) { vertexAction = quadBatch.AddAction; } base.Draw(vertexAction); drawEdgeEffect(); if (maskingInfo != null) { MaskingInfo info = maskingInfo.Value; if (info.BorderThickness > 0) { info.BorderColour *= DrawColourInfo.Colour.AverageColour; } GLWrapper.PushMaskingInfo(info); } if (Children != null) { for (int i = 0; i < Children.Count; i++) { Children[i].Draw(vertexAction); } } if (maskingInfo != null) { GLWrapper.PopMaskingInfo(); } }
private void drawEdgeEffect() { if (MaskingInfo == null || EdgeEffect.Type == EdgeEffectType.None || EdgeEffect.Radius <= 0.0f || EdgeEffect.Colour.A <= 0.0f) { return; } RectangleF effectRect = MaskingInfo.Value.MaskingRect.Inflate(EdgeEffect.Radius).Offset(EdgeEffect.Offset); if (!ScreenSpaceMaskingQuad.HasValue) { ScreenSpaceMaskingQuad = Quad.FromRectangle(effectRect) * DrawInfo.Matrix; } MaskingInfo edgeEffectMaskingInfo = MaskingInfo.Value; edgeEffectMaskingInfo.MaskingRect = effectRect; edgeEffectMaskingInfo.ScreenSpaceAABB = ScreenSpaceMaskingQuad.Value.AABB; edgeEffectMaskingInfo.CornerRadius += EdgeEffect.Radius + EdgeEffect.Roundness; edgeEffectMaskingInfo.BorderThickness = 0; edgeEffectMaskingInfo.BlendRange = EdgeEffect.Radius; GLWrapper.PushMaskingInfo(edgeEffectMaskingInfo); GLWrapper.SetBlend(new BlendingInfo(EdgeEffect.Type == EdgeEffectType.Glow ? BlendingMode.Additive : BlendingMode.Mixture)); Shader.Bind(); Color4 colour = EdgeEffect.Colour; colour.A *= DrawInfo.Colour.A; Texture.WhitePixel.Draw(ScreenSpaceMaskingQuad.Value, colour); Shader.Unbind(); GLWrapper.PopMaskingInfo(); }
public void Bind() { if (IsDisposed) { throw new ObjectDisposedException(ToString(), "Can not bind a disposed shader."); } if (IsBound) { return; } EnsureShaderCompiled(); GLWrapper.UseProgram(this); foreach (var uniform in uniformsValues) { uniform?.Update(); } IsBound = true; }
public override void Draw() { base.Draw(); // Set camera uniforms Shader.SetGlobalProperty(@"g_ProjMatrix", ProjectionMatrix); Shader.SetGlobalProperty(@"g_ViewMatrix", ViewMatrix); Shader.SetGlobalProperty(@"g_InvViewMatrix", InverseViewMatrix); // Invert culling //GL.CullFace(CullFaceMode.Front); // Flip culling for 3D foreach (var child in Children) { child.Draw(); } // Flush batch before disabling state GLWrapper.FlushCurrentBatch(); // Restore 3D states //GL.CullFace(CullFaceMode.Back); }
public override void Draw() { if (Texture == null) { return; } // For billboarding Shader.SetGlobalProperty("g_InverseViewMatrix", InverseViewMatrix); // Taken from DrawNode3D, since we don't need the world transform for particle systems GLWrapper.SetBlend(Blending); Shader.Bind(); // Force create a separate batch so we can use instanced rendering if (particleInstancedBatch == null) { particleInstancedBatch = new QuadBatch <TexturedVertex2D>(TexturedVertex2D.Stride * 4, 1); } var colourInfo = new ColourInfo { Colour = Color4.White }; Texture.DrawQuad(new Quad(-0.5f, -0.5f, 1.0f, 1.0f), colourInfo, vertexAction: v => particleInstancedBatch.Add(v)); // Set GPU buffer data Buffer.SetData(BufferData); Buffer.Bind(); // Render instanced particleInstancedBatch.DrawInstanced(InstanceCount); Shader.Unbind(); }
public override void Draw(IVertexBatch vertexBatch) { updateVertexBatch(); // Prefer to use own vertex batch instead of the parent-owned one. if (Shared.VertexBatch != null) { vertexBatch = Shared.VertexBatch; } base.Draw(vertexBatch); drawEdgeEffect(); if (MaskingInfo != null) { MaskingInfo info = MaskingInfo.Value; if (info.BorderThickness > 0) { info.BorderColour *= DrawInfo.Colour.AverageColour; } GLWrapper.PushMaskingInfo(info); } if (Children != null) { foreach (DrawNode child in Children) { child.Draw(vertexBatch); } } if (MaskingInfo != null) { GLWrapper.PopMaskingInfo(); } }
private void drawBlurredFrameBuffer(int kernelRadius, float sigma, float blurRotation) { FrameBuffer current = SharedData.CurrentEffectBuffer; FrameBuffer target = SharedData.GetNextEffectBuffer(); GLWrapper.SetBlend(BlendingParameters.None); using (BindFrameBuffer(target)) { blurShader.GetUniform <int>(@"g_Radius").UpdateValue(ref kernelRadius); blurShader.GetUniform <float>(@"g_Sigma").UpdateValue(ref sigma); Vector2 size = current.Size; blurShader.GetUniform <Vector2>(@"g_TexSize").UpdateValue(ref size); float radians = -MathUtils.DegreesToRadians(blurRotation); Vector2 blur = new Vector2(MathF.Cos(radians), MathF.Sin(radians)); blurShader.GetUniform <Vector2>(@"g_BlurDirection").UpdateValue(ref blur); blurShader.Bind(); DrawFrameBuffer(current, new RectangleF(0, 0, current.Texture.Width, current.Texture.Height), ColourInfo.SingleColour(Color4.White)); blurShader.Unbind(); } }
protected override void Initialise() { base.Initialise(); if (amountIndices > QuadIndexData.MaxAmountIndices) { ushort[] indices = new ushort[amountIndices]; for (ushort i = 0, j = 0; j < amountIndices; i += TextureGLSingle.VERTICES_PER_QUAD, j += indices_per_quad) { indices[j] = i; indices[j + 1] = (ushort)(i + 1); indices[j + 2] = (ushort)(i + 3); indices[j + 3] = (ushort)(i + 2); indices[j + 4] = (ushort)(i + 3); indices[j + 5] = (ushort)(i + 1); } GLWrapper.BindBuffer(BufferTarget.ElementArrayBuffer, QuadIndexData.EBO_ID); GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(amountIndices * sizeof(ushort)), indices, BufferUsageHint.StaticDraw); QuadIndexData.MaxAmountIndices = amountIndices; } }
protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); memoryLease?.Dispose(); GLWrapper.ScheduleDisposal(v => { int[] ids = v.textureIds; if (ids == null) { return; } for (int i = 0; i < ids.Length; i++) { if (ids[i] >= 0) { GL.DeleteTextures(1, new[] { ids[i] }); } } }, this); }
public override void Draw(Action <TexturedVertex2D> vertexAction) { base.Draw(vertexAction); if (texture?.Available != true || segments.Count == 0) { return; } GLWrapper.PushDepthInfo(DepthInfo.Default); // Blending is removed to allow for correct blending between the wedges of the path. GLWrapper.SetBlend(BlendingParameters.None); pathShader.Bind(); texture.TextureGL.Bind(); updateVertexBuffer(); pathShader.Unbind(); GLWrapper.PopDepthInfo(); }
private void drawBlurredFrameBuffer(int kernelRadius, float sigma, float blurRotation) { FrameBuffer source = currentFrameBuffer; FrameBuffer target = advanceFrameBuffer(); GLWrapper.SetBlend(new BlendingInfo(BlendingMode.None)); using (bindFrameBuffer(target, source.Size)) { blurShader.GetUniform <int>(@"g_Radius").UpdateValue(ref kernelRadius); blurShader.GetUniform <float>(@"g_Sigma").UpdateValue(ref sigma); Vector2 size = source.Size; blurShader.GetUniform <Vector2>(@"g_TexSize").UpdateValue(ref size); float radians = -MathHelper.DegreesToRadians(blurRotation); Vector2 blur = new Vector2((float)Math.Cos(radians), (float)Math.Sin(radians)); blurShader.GetUniform <Vector2>(@"g_BlurDirection").UpdateValue(ref blur); blurShader.Bind(); drawFrameBufferToBackBuffer(source, new RectangleF(0, 0, source.Texture.Width, source.Texture.Height), ColourInfo.SingleColour(Color4.White)); blurShader.Unbind(); } }
public sealed override void Draw(Action <TexturedVertex2D> vertexAction) { if (RequiresRedraw) { FrameStatistics.Increment(StatisticsCounterType.FBORedraw); SharedData.ResetCurrentEffectBuffer(); using (establishFrameBufferViewport()) { // Fill the frame buffer with drawn children using (BindFrameBuffer(SharedData.MainBuffer)) { // We need to draw children as if they were zero-based to the top-left of the texture. // We can do this by adding a translation component to our (orthogonal) projection matrix. GLWrapper.PushOrtho(screenSpaceDrawRectangle); GLWrapper.Clear(new ClearInfo(backgroundColour)); Child.Draw(vertexAction); GLWrapper.PopOrtho(); } PopulateContents(); } SharedData.DrawVersion = GetDrawVersion(); } Shader.Bind(); base.Draw(vertexAction); DrawContents(); Shader.Unbind(); }
public override void Draw(Action <TexturedVertex2D> vertexAction) { base.Draw(vertexAction); if (Texture == null || Texture.IsDisposed || Segments.Count == 0) { return; } GLWrapper.SetDepthTest(true); Shader shader = needsRoundedShader ? RoundedTextureShader : TextureShader; shader.Bind(); Texture.TextureGL.WrapMode = TextureWrapMode.ClampToEdge; Texture.TextureGL.Bind(); updateVertexBuffer(); shader.Unbind(); GLWrapper.SetDepthTest(false); }
// =========================================================== // Getter & Setter // =========================================================== /** * Set the glWrapper. If the glWrapper is not null, its * {@link GLWrapper#wrap(GL)} method is called whenever a surface is * created. A GLWrapper can be used to wrap the GL object that's passed to * the renderer. Wrapping a GL object enables examining and modifying the * behavior of the GL calls made by the renderer. * <p> * Wrapping is typically used for debugging purposes. * <p> * The default value is null. * * @param glWrapper * the new GLWrapper */ public void SetGLWrapper(GLWrapper glWrapper) { this.mGLWrapper = glWrapper; }
internal override bool Upload() { // We should never run raw OGL calls on another thread than the main thread due to race conditions. ThreadSafety.EnsureDrawThread(); if (IsDisposed) { throw new ObjectDisposedException(ToString(), "Can not upload data to a disposed texture."); } bool didUpload = false; while (uploadQueue.TryDequeue(out TextureUpload upload)) { IntPtr dataPointer; GCHandle?h0; if (upload.Data.Length == 0) { h0 = null; dataPointer = IntPtr.Zero; } else { h0 = GCHandle.Alloc(upload.Data, GCHandleType.Pinned); dataPointer = h0.Value.AddrOfPinnedObject(); didUpload = true; } try { // Do we need to generate a new texture? if (textureId <= 0 || internalWidth != width || internalHeight != height) { internalWidth = width; internalHeight = height; // We only need to generate a new texture if we don't have one already. Otherwise just re-use the current one. if (textureId <= 0) { int[] textures = new int[1]; GL.GenTextures(1, textures); textureId = textures[0]; GLWrapper.BindTexture(this); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)(manualMipmaps ? filteringMode : (filteringMode == All.Linear ? All.LinearMipmapLinear : All.Nearest))); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)filteringMode); // 33085 is GL_TEXTURE_MAX_LEVEL, which is not available within TextureParameterName. // It controls the amount of mipmap levels generated by GL.GenerateMipmap later on. GL.TexParameter(TextureTarget.Texture2D, (TextureParameterName)33085, MAX_MIPMAP_LEVELS); updateWrapMode(); } else { GLWrapper.BindTexture(this); } if (width == upload.Bounds.Width && height == upload.Bounds.Height || dataPointer == IntPtr.Zero) { GL.TexImage2D(TextureTarget2d.Texture2D, upload.Level, TextureComponentCount.Srgb8Alpha8, width, height, 0, upload.Format, PixelType.UnsignedByte, dataPointer); } else { initializeLevel(upload.Level, width, height); GL.TexSubImage2D(TextureTarget2d.Texture2D, upload.Level, upload.Bounds.X, upload.Bounds.Y, upload.Bounds.Width, upload.Bounds.Height, upload.Format, PixelType.UnsignedByte, dataPointer); } } // Just update content of the current texture else if (dataPointer != IntPtr.Zero) { GLWrapper.BindTexture(this); if (!manualMipmaps && upload.Level > 0) { //allocate mipmap levels int level = 1; int d = 2; while (width / d > 0) { initializeLevel(level, width / d, height / d); level++; d *= 2; } manualMipmaps = true; } int div = (int)Math.Pow(2, upload.Level); GL.TexSubImage2D(TextureTarget2d.Texture2D, upload.Level, upload.Bounds.X / div, upload.Bounds.Y / div, upload.Bounds.Width / div, upload.Bounds.Height / div, upload.Format, PixelType.UnsignedByte, dataPointer); } } finally { h0?.Free(); upload.Dispose(); } } if (didUpload && !manualMipmaps) { GL.Hint(HintTarget.GenerateMipmapHint, HintMode.Nicest); GL.GenerateMipmap(TextureTarget.Texture2D); } return(didUpload); }
public void Dispose() { GLWrapper.ScheduleDisposal(d => d.Dispose(true), this); GC.SuppressFinalize(this); }
/// <summary> /// Draws this draw node to the screen. /// </summary> /// <param name="vertexAction">The action to be performed on each vertex of /// the draw node in order to draw it if required. This is primarily used by /// textured sprites.</param> public virtual void Draw(Action <TexturedVertex2D> vertexAction) { GLWrapper.SetBlend(DrawColourInfo.Blending); }
/// <summary> /// Binds the renderbuffer to the specfied framebuffer. /// </summary> /// <param name="frameBuffer">The framebuffer this renderbuffer should be bound to.</param> internal void Bind(int frameBuffer) { // Check if we're already bound if (info != null) { return; } if (!renderBufferCache.ContainsKey(Format)) { renderBufferCache[Format] = new Stack <RenderBufferInfo>(); } // Make sure we have renderbuffers available if (renderBufferCache[Format].Count == 0) { renderBufferCache[Format].Push(new RenderBufferInfo() { RenderBufferID = GL.GenRenderbuffer(), FrameBufferID = -1 }); } // Get a renderbuffer from the cache info = renderBufferCache[Format].Pop(); // Check if we need to update the size if (info.Size != Size) { GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, info.RenderBufferID); GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, Format, (int)Math.Ceiling(Size.X), (int)Math.Ceiling(Size.Y)); info.Size = Size; } // For performance reasons, we only need to re-bind the renderbuffer to // the framebuffer if it is not already attached to it if (info.FrameBufferID != frameBuffer) { // Make sure the framebuffer we want to attach to is bound int lastFrameBuffer = GLWrapper.BindFrameBuffer(frameBuffer); switch (Format) { case RenderbufferInternalFormat.DepthComponent16: GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferSlot.DepthAttachment, RenderbufferTarget.Renderbuffer, info.RenderBufferID); break; case RenderbufferInternalFormat.Rgb565: case RenderbufferInternalFormat.Rgb5A1: case RenderbufferInternalFormat.Rgba4: GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferSlot.ColorAttachment0, RenderbufferTarget.Renderbuffer, info.RenderBufferID); break; case RenderbufferInternalFormat.StencilIndex8: GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferSlot.DepthAttachment, RenderbufferTarget.Renderbuffer, info.RenderBufferID); break; } GLWrapper.BindFrameBuffer(lastFrameBuffer); } info.FrameBufferID = frameBuffer; }
void panelGL_PaintCanvas(object sender, GLWrapper.CanvasEventArgs e) { Draw(new GLGraphics(e), DrawingMode.DrawLines); }
public override bool Upload() { // We should never run raw OGL calls on another thread than the main thread due to race conditions. //Debug.Assert(Game.MainThread == Thread.CurrentThread); //todo: thread safety via GLWrapper. if (isDisposed) { return(false); } lock (this) { if (dataToBeUploaded == null) { return(false); } IntPtr dataPointer; GCHandle?h0; if (dataToBeUploaded.Length == 0) { h0 = null; dataPointer = IntPtr.Zero; } else { h0 = GCHandle.Alloc(dataToBeUploaded, GCHandleType.Pinned); dataPointer = h0.Value.AddrOfPinnedObject(); } try { // Do we need to generate a new texture? if (textureId <= 0 || internalWidth < width || internalHeight < height) { internalWidth = width; internalHeight = height; // We only need to generate a new texture if we don't have one already. Otherwise just re-use the current one. if (textureId <= 0) { int[] textures = new int[1]; GL.GenTextures(1, textures); textureId = textures[0]; GLWrapper.BindTexture(textureId); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)All.LinearMipmapLinear); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)All.LinearMipmapLinear); updateWrapMode(); } else { GLWrapper.BindTexture(textureId); } if (width == boundsToBeUploaded.Width && height == boundsToBeUploaded.Height || dataPointer == IntPtr.Zero) { GL.TexImage2D(TextureTarget2d.Texture2D, levelToBeUploaded, TextureComponentCount.Rgba, width, height, 0, formatToBeUploaded, PixelType.UnsignedByte, dataPointer); } else { if (transparentBlack.Length < width * height * 4) { transparentBlack = new byte[width * height * 4]; // Default value is 0, exactly what we need. } GCHandle h1 = GCHandle.Alloc(transparentBlack, GCHandleType.Pinned); GL.TexImage2D(TextureTarget2d.Texture2D, levelToBeUploaded, TextureComponentCount.Rgba, width, height, 0, formatToBeUploaded, PixelType.UnsignedByte, h1.AddrOfPinnedObject()); h1.Free(); GL.TexSubImage2D(TextureTarget2d.Texture2D, levelToBeUploaded, boundsToBeUploaded.X, boundsToBeUploaded.Y, boundsToBeUploaded.Width, boundsToBeUploaded.Height, formatToBeUploaded, PixelType.UnsignedByte, dataPointer); } GL.Hint(HintTarget.GenerateMipmapHint, HintMode.Nicest); GL.GenerateMipmap(TextureTarget.Texture2D); } // Just update content of the current texture else if (dataPointer != IntPtr.Zero) { GLWrapper.BindTexture(textureId); int div = (int)Math.Pow(2, levelToBeUploaded); GL.TexSubImage2D(TextureTarget2d.Texture2D, levelToBeUploaded, boundsToBeUploaded.X / div, boundsToBeUploaded.Y / div, boundsToBeUploaded.Width / div, boundsToBeUploaded.Height / div, formatToBeUploaded, PixelType.UnsignedByte, dataPointer); } return(true); } finally { if (h0.HasValue) { h0.Value.Free(); } if (dataToBeUploaded != null) { FreeBuffer(dataToBeUploaded); } dataToBeUploaded = null; } } }
protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); GLWrapper.ScheduleDisposal(unload); }
protected virtual void Dispose(bool isDisposing) => GLWrapper.ScheduleDisposal(() => Available = false);