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; }
void IGraphicsBackend.BeginRendering(IDrawDevice device, VertexBatchStore vertexData, RenderOptions options, RenderStats stats) { }
[Test] public void RentAndClear() { VertexBatchStore memory = new VertexBatchStore(); // Repeatedly rent slices of varying types from memory { VertexSlice <VertexC1P3> slice = memory.Rent <VertexC1P3>(4); slice[0] = new VertexC1P3 { Color = new ColorRgba(0) }; slice[1] = new VertexC1P3 { Color = new ColorRgba(1) }; slice[2] = new VertexC1P3 { Color = new ColorRgba(2) }; slice[3] = new VertexC1P3 { Color = new ColorRgba(3) }; } { VertexSlice <VertexC1P3T2> slice = memory.Rent <VertexC1P3T2>(3); slice[0] = new VertexC1P3T2 { Color = new ColorRgba(4) }; slice[1] = new VertexC1P3T2 { Color = new ColorRgba(5) }; slice[2] = new VertexC1P3T2 { Color = new ColorRgba(6) }; } { VertexSlice <VertexC1P3> slice = memory.Rent <VertexC1P3>(2); slice[0] = new VertexC1P3 { Color = new ColorRgba(7) }; slice[1] = new VertexC1P3 { Color = new ColorRgba(8) }; } { VertexSlice <VertexC1P3> slice = memory.Rent <VertexC1P3>(1); slice[0] = new VertexC1P3 { Color = new ColorRgba(9) }; } // Assert correct storage property values Assert.AreEqual( 1 + Math.Max( VertexDeclaration.Get <VertexC1P3>().TypeIndex, VertexDeclaration.Get <VertexC1P3T2>().TypeIndex), memory.TypeIndexCount); Assert.AreEqual(1, memory.GetBatchCount <VertexC1P3>()); Assert.AreEqual(1, memory.GetBatchCount <VertexC1P3T2>()); // Retrieve specific internal vertex arrays VertexBatch <VertexC1P3> batchA = memory.GetBatch <VertexC1P3>(0); VertexBatch <VertexC1P3T2> batchB = memory.GetBatch <VertexC1P3T2>(0); RawList <VertexC1P3> verticesA = batchA.Vertices; RawList <VertexC1P3T2> verticesB = batchB.Vertices; // Assert that they contain all the data we submitted in the correct order Assert.AreEqual(7, batchA.Count); Assert.AreEqual(3, batchB.Count); Assert.AreEqual(7, verticesA.Count); Assert.AreEqual(3, verticesB.Count); Assert.AreEqual(new ColorRgba(0), verticesA[0].Color); Assert.AreEqual(new ColorRgba(1), verticesA[1].Color); Assert.AreEqual(new ColorRgba(2), verticesA[2].Color); Assert.AreEqual(new ColorRgba(3), verticesA[3].Color); Assert.AreEqual(new ColorRgba(4), verticesB[0].Color); Assert.AreEqual(new ColorRgba(5), verticesB[1].Color); Assert.AreEqual(new ColorRgba(6), verticesB[2].Color); Assert.AreEqual(new ColorRgba(7), verticesA[4].Color); Assert.AreEqual(new ColorRgba(8), verticesA[5].Color); Assert.AreEqual(new ColorRgba(9), verticesA[6].Color); // Clear all vertices memory.Clear(); // Assert correct storage property values Assert.AreEqual(0, memory.TypeIndexCount); Assert.AreEqual(0, memory.GetBatchCount <VertexC1P3>()); Assert.AreEqual(0, memory.GetBatchCount <VertexC1P3T2>()); // Assert that the vertices are gone, but capacity isn't Assert.AreEqual(0, batchA.Count); Assert.AreEqual(0, batchB.Count); Assert.AreEqual(0, verticesA.Count); Assert.AreEqual(0, verticesB.Count); Assert.GreaterOrEqual(verticesA.Capacity, 7); Assert.GreaterOrEqual(verticesB.Capacity, 3); }
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); } } }
[Test] public void MaxBatchSize() { VertexBatchStore memory = new VertexBatchStore(4); // Rent a few memory slices, but stay below the max batch size { VertexSlice <VertexC1P3> slice = memory.Rent <VertexC1P3>(2); slice[0] = new VertexC1P3 { Color = new ColorRgba(0) }; slice[1] = new VertexC1P3 { Color = new ColorRgba(1) }; } { VertexSlice <VertexC1P3> slice = memory.Rent <VertexC1P3>(1); slice[0] = new VertexC1P3 { Color = new ColorRgba(2) }; } // Assert that we only have one batch, with the correct contents Assert.AreEqual(1, memory.GetBatchCount <VertexC1P3>()); Assert.AreEqual(0, memory.GetBatchCount <VertexC1P3T2>()); VertexBatch <VertexC1P3> batchA = memory.GetBatch <VertexC1P3>(0); RawList <VertexC1P3> verticesA = batchA.Vertices; Assert.AreEqual(3, batchA.Count); Assert.AreEqual(new ColorRgba(0), verticesA[0].Color); Assert.AreEqual(new ColorRgba(1), verticesA[1].Color); Assert.AreEqual(new ColorRgba(2), verticesA[2].Color); // Rent a few more slices, this time exceeding max batch size { VertexSlice <VertexC1P3> slice = memory.Rent <VertexC1P3>(2); slice[0] = new VertexC1P3 { Color = new ColorRgba(3) }; slice[1] = new VertexC1P3 { Color = new ColorRgba(4) }; } { VertexSlice <VertexC1P3> slice = memory.Rent <VertexC1P3>(2); slice[0] = new VertexC1P3 { Color = new ColorRgba(5) }; slice[1] = new VertexC1P3 { Color = new ColorRgba(6) }; } // Assert that we now have two batches, each with the correct contents Assert.AreEqual(2, memory.GetBatchCount <VertexC1P3>()); Assert.AreEqual(0, memory.GetBatchCount <VertexC1P3T2>()); VertexBatch <VertexC1P3> batchB = memory.GetBatch <VertexC1P3>(1); RawList <VertexC1P3> verticesB = batchB.Vertices; Assert.AreEqual(3, batchA.Count); Assert.AreEqual(new ColorRgba(0), verticesA[0].Color); Assert.AreEqual(new ColorRgba(1), verticesA[1].Color); Assert.AreEqual(new ColorRgba(2), verticesA[2].Color); Assert.AreEqual(4, batchB.Count); Assert.AreEqual(new ColorRgba(3), verticesB[0].Color); Assert.AreEqual(new ColorRgba(4), verticesB[1].Color); Assert.AreEqual(new ColorRgba(5), verticesB[2].Color); Assert.AreEqual(new ColorRgba(6), verticesB[3].Color); // Clear all vertices memory.Clear(); // Assert that the vertices are gone, but capacity isn't Assert.AreEqual(0, memory.GetBatchCount <VertexC1P3>()); Assert.AreEqual(0, memory.GetBatchCount <VertexC1P3T2>()); Assert.AreEqual(0, batchA.Count); Assert.AreEqual(0, batchB.Count); Assert.AreEqual(0, verticesA.Count); Assert.AreEqual(0, verticesB.Count); Assert.GreaterOrEqual(verticesA.Capacity, 3); Assert.GreaterOrEqual(verticesB.Capacity, 4); // Rent some vertices again memory.Rent <VertexC1P3>(3); memory.Rent <VertexC1P3>(4); // Assert that the same storage is re-used Assert.AreEqual(2, memory.GetBatchCount <VertexC1P3>()); Assert.AreEqual(0, memory.GetBatchCount <VertexC1P3T2>()); Assert.AreEqual(3, batchA.Count); Assert.AreEqual(4, batchB.Count); Assert.AreEqual(3, verticesA.Count); Assert.AreEqual(4, verticesB.Count); // Assert that we're getting an exception when attempting to rent a slice that is too large Assert.Throws <ArgumentException>(() => memory.Rent <VertexC1P3>(5)); }