[Test] public void VertexAccess() { VertexBatch <VertexC1P3> typedBatch = new VertexBatch <VertexC1P3>(); IVertexBatch abstractBatch = typedBatch; typedBatch.Vertices.Add(new VertexC1P3 { Color = new ColorRgba(0) }); typedBatch.Vertices.Add(new VertexC1P3 { Color = new ColorRgba(1) }); typedBatch.Vertices.Add(new VertexC1P3 { Color = new ColorRgba(2) }); typedBatch.Vertices.Add(new VertexC1P3 { Color = new ColorRgba(3) }); Assert.AreEqual(4, typedBatch.Count); Assert.AreEqual(4, typedBatch.Vertices.Count); Assert.AreEqual(new ColorRgba(0), typedBatch.Vertices[0].Color); Assert.AreEqual(new ColorRgba(1), typedBatch.Vertices[1].Color); Assert.AreEqual(new ColorRgba(2), typedBatch.Vertices[2].Color); Assert.AreEqual(new ColorRgba(3), typedBatch.Vertices[3].Color); Assert.AreEqual(4, abstractBatch.Count); typedBatch.Clear(); Assert.AreEqual(0, typedBatch.Count); Assert.AreEqual(0, typedBatch.Vertices.Count); Assert.AreEqual(0, abstractBatch.Count); }
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) { GLWrapper.PushMaskingInfo(MaskingInfo.Value); } if (Children != null) { foreach (DrawNode child in Children) { child.Draw(vertexBatch); } } if (MaskingInfo != null) { GLWrapper.PopMaskingInfo(); } }
/// <summary> /// Sets the last vertex batch used for drawing. /// <para> /// This is done so that various methods that change GL state can force-draw the batch /// before continuing with the state change. /// </para> /// </summary> /// <param name="batch">The batch.</param> internal static void SetActiveBatch(IVertexBatch batch) { if (lastActiveBatch == batch) { return; } FlushCurrentBatch(); lastActiveBatch = batch; }
[Test] public void EmptyArray() { VertexBatch <VertexC1P3> typedBatch = new VertexBatch <VertexC1P3>(); IVertexBatch abstractBatch = typedBatch; Assert.AreEqual(0, typedBatch.Count); Assert.AreEqual(VertexDeclaration.Get <VertexC1P3>(), typedBatch.Declaration); Assert.IsNotNull(typedBatch.Vertices); Assert.AreEqual(0, typedBatch.Vertices.Count); Assert.AreEqual(0, abstractBatch.Count); Assert.AreEqual(VertexDeclaration.Get <VertexC1P3>(), abstractBatch.Declaration); }
/// <summary> /// Sets the last vertex batch used for drawing. /// <para> /// This is done so that various methods that change GL state can force-draw the batch /// before continuing with the state change. /// </para> /// </summary> /// <param name="batch">The batch.</param> internal static void SetActiveBatch(IVertexBatch batch) { if (lastActiveBatch == batch) { return; } batch_reset_list.Add(batch); FlushCurrentBatch(); lastActiveBatch = batch; }
[Test] public void Locking() { VertexBatch <VertexC1P3> typedBatch = new VertexBatch <VertexC1P3>(); IVertexBatch abstractBatch = typedBatch; typedBatch.Vertices.Add(new VertexC1P3 { Color = new ColorRgba(0) }); typedBatch.Vertices.Add(new VertexC1P3 { Color = new ColorRgba(1) }); typedBatch.Vertices.Add(new VertexC1P3 { Color = new ColorRgba(2) }); typedBatch.Vertices.Add(new VertexC1P3 { Color = new ColorRgba(3) }); // Assert that we can retrieve all data via unmanaged pointer access VertexDeclaration layout = typedBatch.Declaration; int vertexSize = layout.Size; int colorElementIndex = layout.Elements.IndexOfFirst(item => item.FieldName == VertexDeclaration.ShaderFieldPrefix + "Color"); int colorOffset = (int)layout.Elements[colorElementIndex].Offset; using (PinnedArrayHandle locked = typedBatch.Lock()) { Assert.AreEqual(new ColorRgba(0), ReadColor(locked.Address, vertexSize * 0 + colorOffset)); Assert.AreEqual(new ColorRgba(1), ReadColor(locked.Address, vertexSize * 1 + colorOffset)); Assert.AreEqual(new ColorRgba(2), ReadColor(locked.Address, vertexSize * 2 + colorOffset)); Assert.AreEqual(new ColorRgba(3), ReadColor(locked.Address, vertexSize * 3 + colorOffset)); } using (PinnedArrayHandle locked = abstractBatch.Lock()) { Assert.AreEqual(new ColorRgba(0), ReadColor(locked.Address, vertexSize * 0 + colorOffset)); Assert.AreEqual(new ColorRgba(1), ReadColor(locked.Address, vertexSize * 1 + colorOffset)); Assert.AreEqual(new ColorRgba(2), ReadColor(locked.Address, vertexSize * 2 + colorOffset)); Assert.AreEqual(new ColorRgba(3), ReadColor(locked.Address, vertexSize * 3 + colorOffset)); } // Make sure that our locks released properly, i.e. allowing the array to be garbage collected WeakReference weakRefToLockedData = new WeakReference(typedBatch.Vertices.Data); Assert.IsTrue(weakRefToLockedData.IsAlive); typedBatch = null; abstractBatch = null; GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true); GC.WaitForPendingFinalizers(); Assert.IsFalse(weakRefToLockedData.IsAlive); }
private void drawChildren(IVertexBatch vertexBatch, Vector2 frameBufferSize) { // Fill the frame buffer with drawn children using (bindFrameBuffer(currentFrameBuffer, frameBufferSize)) { // 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.ClearColour(BackgroundColour); base.Draw(vertexBatch); GLWrapper.PopOrtho(); } }
/// <summary> /// Sets the last vertex batch used for drawing. /// <para> /// This is done so that various methods that change GL state can force-draw the batch /// before continuing with the state change. /// </para> /// </summary> /// <param name="batch">The batch.</param> internal static void SetActiveBatch(IVertexBatch batch) { if (lastActiveBatch == batch) { return; } FlushCurrentBatch(); if (batch != null && !thisFrameBatches.Contains(batch)) { thisFrameBatches.Add(batch); } lastActiveBatch = batch; }
public override void Draw(IVertexBatch vertexBatch) { Vector2 frameBufferSize = new Vector2((float)Math.Ceiling(ScreenSpaceDrawRectangle.Width), (float)Math.Ceiling(ScreenSpaceDrawRectangle.Height)); if (UpdateVersion > DrawVersion.Value || frameBufferSize != FrameBuffers[0].Size) { DrawVersion.Value = UpdateVersion; using (establishFrameBufferViewport(frameBufferSize)) { drawChildren(vertexBatch, frameBufferSize); // Blur post-processing in case a blur radius is defined. int radiusX = BlurSigma.X > 0 ? findBlurRadius(BlurSigma.X) : 0; int radiusY = BlurSigma.Y > 0 ? findBlurRadius(BlurSigma.Y) : 0; if (radiusX > 0 || radiusY > 0) { GL.Disable(EnableCap.ScissorTest); if (radiusX > 0) { drawBlurredFrameBuffer(radiusX, BlurSigma.X, BlurRotation); } if (radiusY > 0) { drawBlurredFrameBuffer(radiusY, BlurSigma.Y, BlurRotation + 90); } GL.Enable(EnableCap.ScissorTest); } } finalizeFrameBuffer(); } RectangleF drawRectangle = FilteringMode == All.Nearest ? new RectangleF(ScreenSpaceDrawRectangle.X, ScreenSpaceDrawRectangle.Y, frameBufferSize.X, frameBufferSize.Y) : ScreenSpaceDrawRectangle; // Blit the final framebuffer to screen. GLWrapper.SetBlend(DrawInfo.Blending); Shader.Bind(); drawFrameBufferToBackBuffer(FrameBuffers[0], drawRectangle, DrawInfo.Colour); Shader.Unbind(); }
/// <summary> /// Uploads all dynamically gathered vertex data to the GPU using the internal <see cref="vertexBuffers"/> pool. /// </summary> private void UploadVertexData() { // Note that there is a 1:1 mapping between gathered vertex batches and vertex buffers. // We'll keep all buffers around until the drawdevice is disposed, in case we might need // them again later. this.vertexBuffers.Count = Math.Max(this.vertexBuffers.Count, this.drawVertices.TypeIndexCount); for (int typeIndex = 0; typeIndex < this.drawVertices.TypeIndexCount; typeIndex++) { // Filter out unused vertex types IReadOnlyList <IVertexBatch> batches = this.drawVertices.GetBatches(typeIndex); if (batches == null) { continue; } if (batches.Count == 0) { continue; } // Upload all vertex batches for this vertex type if (this.vertexBuffers[typeIndex] == null) { this.vertexBuffers[typeIndex] = new RawList <VertexBuffer>(); } this.vertexBuffers[typeIndex].Count = Math.Max(this.vertexBuffers[typeIndex].Count, batches.Count); for (int batchIndex = 0; batchIndex < batches.Count; batchIndex++) { IVertexBatch vertexBatch = batches[batchIndex]; // Generate a VertexBuffer for this vertex type and batch index, if it didn't exist yet if (this.vertexBuffers[typeIndex][batchIndex] == null) { this.vertexBuffers[typeIndex][batchIndex] = new VertexBuffer(); } // Upload the vertex batch to using (PinnedArrayHandle pinned = vertexBatch.Lock()) { this.vertexBuffers[typeIndex][batchIndex].LoadVertexData( vertexBatch.Declaration, pinned.Address, vertexBatch.Count); } } } }
public override void Draw(IVertexBatch vertexBatch) { if (ForceRedraw.Reset() > 0) { Vector2 frameBufferSize = new Vector2((float)Math.Ceiling(ScreenSpaceDrawRectangle.Width), (float)Math.Ceiling(ScreenSpaceDrawRectangle.Height)); using (establishFrameBufferViewport(frameBufferSize)) { drawChildren(vertexBatch, frameBufferSize); // Blur post-processing in case a blur radius is defined. int radiusX = BlurSigma.X > 0 ? findBlurRadius(BlurSigma.X) : 0; int radiusY = BlurSigma.Y > 0 ? findBlurRadius(BlurSigma.Y) : 0; if (radiusX > 0 || radiusY > 0) { GL.Disable(EnableCap.ScissorTest); if (radiusX > 0) { drawBlurredFrameBuffer(radiusX, BlurSigma.X, BlurRotation); } if (radiusY > 0) { drawBlurredFrameBuffer(radiusY, BlurSigma.Y, BlurRotation + 90); } GL.Enable(EnableCap.ScissorTest); } } finalizeFrameBuffer(); } // Blit the final framebuffer to screen. GLWrapper.SetBlend(DrawInfo.Blending); Shader.Bind(); drawFrameBufferToBackBuffer(FrameBuffers[0], ScreenSpaceDrawRectangle); Shader.Unbind(); }
public override void Draw(IVertexBatch vertexBatch) { base.Draw(vertexBatch); if (Texture == null || Texture.IsDisposed) { return; } Shader shader = NeedsRoundedShader ? RoundedTextureShader : TextureShader; if (InflationAmount != Vector2.Zero) { // The shader currently cannot deal with negative width and height. RectangleF drawRect = DrawRectangle.WithPositiveExtent; RoundedTextureShader.GetUniform <Vector4>(@"g_DrawingRect").Value = new Vector4( drawRect.Left, drawRect.Top, drawRect.Right, drawRect.Bottom); RoundedTextureShader.GetUniform <Matrix3>(@"g_ToDrawingSpace").Value = DrawInfo.MatrixInverse; RoundedTextureShader.GetUniform <Vector2>(@"g_DrawingBlendRange").Value = InflationAmount; } shader.Bind(); Texture.TextureGL.WrapMode = WrapTexture ? TextureWrapMode.Repeat : TextureWrapMode.ClampToEdge; Texture.Draw(ScreenSpaceDrawQuad, DrawInfo.Colour, null, vertexBatch as VertexBatch <TexturedVertex2D>, new Vector2(InflationAmount.X / DrawRectangle.Width, InflationAmount.Y / DrawRectangle.Height)); shader.Unbind(); if (InflationAmount != Vector2.Zero) { RoundedTextureShader.GetUniform <Vector2>(@"g_DrawingBlendRange").Value = Vector2.Zero; } }
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(); } }
/// <summary> /// Begins tracking a <see cref="IVertexBatch"/>, resetting its counters every frame. This should be invoked once for every <see cref="IVertexBatch"/> in use. /// </summary> /// <param name="batch">The batch to register.</param> internal static void RegisterVertexBatch(IVertexBatch batch) => reset_scheduler.Add(() => all_batches.Add(batch));
public virtual void Draw(IVertexBatch vertexBatch) { GLWrapper.SetBlend(DrawInfo.Blending); }
void IGraphicsBackend.BeginRendering(IDrawDevice device, VertexBatchStore vertexData, RenderOptions options, RenderStats stats) { DebugCheckOpenGLErrors(); this.CheckContextCaps(); this.currentDevice = device; this.renderOptions = options; this.renderStats = stats; // Upload all vertex data that we'll need during rendering if (vertexData != null) { this.perVertexTypeVBO.Count = Math.Max(this.perVertexTypeVBO.Count, vertexData.Batches.Count); for (int typeIndex = 0; typeIndex < vertexData.Batches.Count; typeIndex++) { // Filter out unused vertex types IVertexBatch vertexBatch = vertexData.Batches[typeIndex]; if (vertexBatch == null) { continue; } if (vertexBatch.Count == 0) { continue; } // Generate a VBO for this vertex type if it didn't exist yet if (this.perVertexTypeVBO[typeIndex] == 0) { GL.GenBuffers(1, out this.perVertexTypeVBO.Data[typeIndex]); } GL.BindBuffer(BufferTarget.ArrayBuffer, this.perVertexTypeVBO[typeIndex]); // Upload all data of this vertex type as a single block int vertexDataLength = vertexBatch.Declaration.Size * vertexBatch.Count; using (PinnedArrayHandle pinned = vertexBatch.Lock()) { GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)vertexDataLength, IntPtr.Zero, BufferUsageHint.StreamDraw); GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)vertexDataLength, pinned.Address, BufferUsageHint.StreamDraw); } } } GL.BindBuffer(BufferTarget.ArrayBuffer, 0); // Prepare the target surface for rendering NativeRenderTarget.Bind(options.Target as NativeRenderTarget); // Determine whether masked blending should use alpha-to-coverage mode if (this.msaaIsDriverDisabled) { this.useAlphaToCoverageBlend = false; } else if (NativeRenderTarget.BoundRT != null) { this.useAlphaToCoverageBlend = NativeRenderTarget.BoundRT.Samples > 0; } else if (this.activeWindow != null) { this.useAlphaToCoverageBlend = this.activeWindow.IsMultisampled; } else { this.useAlphaToCoverageBlend = this.defaultGraphicsMode.Samples > 0; } // Determine the available size on the active rendering surface Point2 availableSize; if (NativeRenderTarget.BoundRT != null) { availableSize = new Point2(NativeRenderTarget.BoundRT.Width, NativeRenderTarget.BoundRT.Height); } else if (this.activeWindow != null) { availableSize = new Point2(this.activeWindow.Width, this.activeWindow.Height); } else { availableSize = this.externalBackbufferSize; } // Translate viewport coordinates to OpenGL screen coordinates (bottom-left, rising), unless rendering // to a texture, which is laid out Duality-like (top-left, descending) Rect openGLViewport = options.Viewport; if (NativeRenderTarget.BoundRT == null) { openGLViewport.Y = (availableSize.Y - openGLViewport.H) - openGLViewport.Y; } // Setup viewport and scissor rects GL.Viewport((int)openGLViewport.X, (int)openGLViewport.Y, (int)MathF.Ceiling(openGLViewport.W), (int)MathF.Ceiling(openGLViewport.H)); GL.Scissor((int)openGLViewport.X, (int)openGLViewport.Y, (int)MathF.Ceiling(openGLViewport.W), (int)MathF.Ceiling(openGLViewport.H)); // Clear buffers ClearBufferMask glClearMask = 0; ColorRgba clearColor = options.ClearColor; if ((options.ClearFlags & ClearFlag.Color) != ClearFlag.None) { glClearMask |= ClearBufferMask.ColorBufferBit; } if ((options.ClearFlags & ClearFlag.Depth) != ClearFlag.None) { glClearMask |= ClearBufferMask.DepthBufferBit; } GL.ClearColor(clearColor.R / 255.0f, clearColor.G / 255.0f, clearColor.B / 255.0f, clearColor.A / 255.0f); GL.ClearDepth((double)options.ClearDepth); // The "float version" is from OpenGL 4.1.. GL.Clear(glClearMask); // Configure Rendering params if (options.RenderMode == RenderMatrix.ScreenSpace) { GL.Enable(EnableCap.ScissorTest); GL.Enable(EnableCap.DepthTest); GL.DepthFunc(DepthFunction.Always); } else { GL.Enable(EnableCap.ScissorTest); GL.Enable(EnableCap.DepthTest); GL.DepthFunc(DepthFunction.Lequal); } OpenTK.Matrix4 openTkModelView; Matrix4 modelView = options.ModelViewMatrix; GetOpenTKMatrix(ref modelView, out openTkModelView); GL.MatrixMode(MatrixMode.Modelview); GL.LoadMatrix(ref openTkModelView); OpenTK.Matrix4 openTkProjection; Matrix4 projection = options.ProjectionMatrix; GetOpenTKMatrix(ref projection, out openTkProjection); GL.MatrixMode(MatrixMode.Projection); GL.LoadMatrix(ref openTkProjection); if (NativeRenderTarget.BoundRT != null) { GL.Scale(1.0f, -1.0f, 1.0f); if (options.RenderMode == RenderMatrix.ScreenSpace) { GL.Translate(0.0f, -device.TargetSize.Y, 0.0f); } } }
void IGraphicsBackend.BeginRendering(IDrawDevice device, VertexBatchStore vertexData, RenderOptions options, RenderStats stats) { DebugCheckOpenGLErrors(); // ToDo: AA is disabled for now //this.CheckContextCaps(); this.currentDevice = device; this.renderOptions = options; this.renderStats = stats; // Upload all vertex data that we'll need during rendering if (vertexData != null) { this.perVertexTypeVBO.Count = Math.Max(this.perVertexTypeVBO.Count, vertexData.Batches.Count); for (int typeIndex = 0; typeIndex < vertexData.Batches.Count; typeIndex++) { // Filter out unused vertex types IVertexBatch vertexBatch = vertexData.Batches[typeIndex]; if (vertexBatch == null) { continue; } if (vertexBatch.Count == 0) { continue; } // Generate a VBO for this vertex type if it didn't exist yet if (this.perVertexTypeVBO[typeIndex] == 0) { GL.GenBuffers(1, out this.perVertexTypeVBO.Data[typeIndex]); } GL.BindBuffer(BufferTarget.ArrayBuffer, this.perVertexTypeVBO[typeIndex]); // Upload all data of this vertex type as a single block int vertexDataLength = vertexBatch.Declaration.Size * vertexBatch.Count; using (PinnedArrayHandle pinned = vertexBatch.Lock()) { GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)vertexDataLength, IntPtr.Zero, BufferUsage.StreamDraw); GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)vertexDataLength, pinned.Address, BufferUsage.StreamDraw); } } } GL.BindBuffer(BufferTarget.ArrayBuffer, 0); // Prepare the target surface for rendering NativeRenderTarget.Bind(options.Target as NativeRenderTarget); // Determine the available size on the active rendering surface //Point2 availableSize; //if (NativeRenderTarget.BoundRT != null) { // availableSize = new Point2(NativeRenderTarget.BoundRT.Width, NativeRenderTarget.BoundRT.Height); //} else { // availableSize = this.externalBackbufferSize; //} Rect openGLViewport = options.Viewport; // Setup viewport and scissor rects GL.Viewport((int)openGLViewport.X, (int)openGLViewport.Y, (int)MathF.Ceiling(openGLViewport.W), (int)MathF.Ceiling(openGLViewport.H)); GL.Scissor((int)openGLViewport.X, (int)openGLViewport.Y, (int)MathF.Ceiling(openGLViewport.W), (int)MathF.Ceiling(openGLViewport.H)); // Clear buffers ClearBufferMask glClearMask = 0; ColorRgba clearColor = options.ClearColor; if ((options.ClearFlags & ClearFlag.Color) != ClearFlag.None) { glClearMask |= ClearBufferMask.ColorBufferBit; } if ((options.ClearFlags & ClearFlag.Depth) != ClearFlag.None) { glClearMask |= ClearBufferMask.DepthBufferBit; } GL.ClearColor(clearColor.R / 255.0f, clearColor.G / 255.0f, clearColor.B / 255.0f, clearColor.A / 255.0f); GL.ClearDepth(options.ClearDepth); GL.Clear(glClearMask); // Configure Rendering params if (options.RenderMode == RenderMatrix.ScreenSpace) { GL.Enable(EnableCap.ScissorTest); GL.Enable(EnableCap.DepthTest); GL.DepthFunc(DepthFunction.Always); } else { GL.Enable(EnableCap.ScissorTest); GL.Enable(EnableCap.DepthTest); GL.DepthFunc(DepthFunction.Lequal); } Matrix4 modelView = options.ModelViewMatrix; Matrix4 projection = options.ProjectionMatrix; if (NativeRenderTarget.BoundRT != null) { modelView = Matrix4.CreateScale(new Vector3(1f, -1f, 1f)) * modelView; if (options.RenderMode == RenderMatrix.ScreenSpace) { modelView = Matrix4.CreateTranslation(new Vector3(0f, -device.TargetSize.Y, 0f)) * modelView; } } // Convert matrices to float arrays GetArrayMatrix(ref modelView, ref modelViewData); GetArrayMatrix(ref projection, ref projectionData); // All EBOs can be used again lastUsedEBO = 0; }
/// <summary> /// Sets the last vertex batch used for drawing. /// <para> /// This is done so that various methods that change GL state can force-draw the batch /// before continuing with the state change. /// </para> /// </summary> /// <param name="batch">The batch.</param> internal static void SetActiveBatch(IVertexBatch batch) { lastActiveBatch = batch; }