public void LoadSubData(IntPtr offset, IntPtr data, int size) { if (size < 0) { throw new ArgumentException("Size cannot be less than zero."); } if (this.bufferSize == 0) { throw new InvalidOperationException(string.Format( "Cannot update {0}, because its storage was not initialized yet.", typeof(NativeGraphicsBuffer).Name)); } if ((uint)offset + size > this.bufferSize) { throw new ArgumentException(string.Format( "Cannot update {0} with offset {1} and size {2}, as this exceeds the internal " + "storage size {3}.", typeof(NativeGraphicsBuffer).Name, offset, size, this.bufferSize)); } NativeGraphicsBuffer prevBound = GetBound(this.type); Bind(this.type, this); BufferTarget target = ToOpenTKBufferType(this.type); GL.BufferSubData(target, offset, size, data); Bind(this.type, prevBound); }
void IDualityBackend.Shutdown() { if (activeInstance == this) { activeInstance = null; } if (DualityApp.ExecContext != DualityApp.ExecutionContext.Terminated) { DefaultOpenTKBackendPlugin.GuardSingleThreadState(); if (this.sharedBatchIBO != null) { this.sharedBatchIBO.Dispose(); this.sharedBatchIBO = null; } } // Since the window outlives the graphics backend in the usual launcher setup, // we'll need to unhook early, so Duality can complete its cleanup before the window does. if (this.activeWindow != null) { this.activeWindow.UnhookFromDuality(); this.activeWindow = null; } }
public static void Bind(GraphicsBufferType type, NativeGraphicsBuffer buffer) { if (GetBound(type) == buffer) { return; } SetBound(type, buffer); BufferTarget target = ToOpenTKBufferType(type); GL.BindBuffer(target, buffer != null ? buffer.Handle : 0); }
private static void SetBound(GraphicsBufferType type, NativeGraphicsBuffer buffer) { if (type == GraphicsBufferType.Vertex) { boundVertex = buffer; } else if (type == GraphicsBufferType.Index) { boundIndex = buffer; } else { return; } }
public void SetupEmpty(int size) { if (size < 0) { throw new ArgumentException("Size cannot be less than zero."); } NativeGraphicsBuffer prevBound = GetBound(this.type); Bind(this.type, this); BufferTarget target = ToOpenTKBufferType(this.type); GL.BufferData(target, size, IntPtr.Zero, BufferUsageHint.StreamDraw); this.bufferSize = size; Bind(this.type, prevBound); }
/// <summary> /// Draws the vertices of a single <see cref="DrawBatch"/>, after all other rendering state /// has been set up accordingly outside this method. /// </summary> /// <param name="buffer"></param> /// <param name="ranges"></param> /// <param name="mode"></param> private void DrawVertexBatch(VertexBuffer buffer, RawList <VertexDrawRange> ranges, VertexMode mode) { NativeGraphicsBuffer indexBuffer = (buffer.IndexCount > 0 ? buffer.NativeIndex : null) as NativeGraphicsBuffer; IndexDataElementType indexType = buffer.IndexType; // Since the QUADS primitive is deprecated in OpenGL 3.0 and not available in OpenGL ES, // we'll emulate this with an ad-hoc index buffer object that we generate here. if (mode == VertexMode.Quads) { if (indexBuffer != null) { Logs.Core.WriteWarning( "Rendering {0} instances that use index buffers is not supported for quads geometry. " + "To use index buffers, use any other geometry type listed in {1}. Skipping batch.", typeof(DrawBatch).Name, typeof(VertexMode).Name); return; } NativeGraphicsBuffer.Bind(GraphicsBufferType.Index, this.sharedBatchIBO); this.GenerateQuadIndices(ranges, this.sharedBatchIndices); this.sharedBatchIBO.LoadData( this.sharedBatchIndices.Data, 0, this.sharedBatchIndices.Count); GL.DrawElements( PrimitiveType.Triangles, this.sharedBatchIndices.Count, DrawElementsType.UnsignedShort, IntPtr.Zero); this.sharedBatchIndices.Clear(); } // Otherwise, run the regular rendering path else { // Rendering using index buffer if (indexBuffer != null) { if (ranges != null && ranges.Count > 0) { Logs.Core.WriteWarning( "Rendering {0} instances that use index buffers do not support specifying vertex ranges, " + "since the two features are mutually exclusive.", typeof(DrawBatch).Name, typeof(VertexMode).Name); } NativeGraphicsBuffer.Bind(GraphicsBufferType.Index, indexBuffer); PrimitiveType openTkMode = GetOpenTKVertexMode(mode); DrawElementsType openTkIndexType = GetOpenTKIndexType(indexType); GL.DrawElements( openTkMode, buffer.IndexCount, openTkIndexType, IntPtr.Zero); } // Rendering using an array of vertex ranges else { NativeGraphicsBuffer.Bind(GraphicsBufferType.Index, null); PrimitiveType openTkMode = GetOpenTKVertexMode(mode); VertexDrawRange[] rangeData = ranges.Data; int rangeCount = ranges.Count; for (int r = 0; r < rangeCount; r++) { GL.DrawArrays( openTkMode, rangeData[r].Index, rangeData[r].Count); } } } }
void IGraphicsBackend.Render(IReadOnlyList <DrawBatch> batches) { if (batches.Count == 0) { return; } this.RetrieveActiveShaders(batches); this.SetupSharedParameters(this.renderOptions.ShaderParameters); int drawCalls = 0; DrawBatch lastRendered = null; for (int i = 0; i < batches.Count; i++) { DrawBatch batch = batches[i]; VertexDeclaration vertexType = batch.VertexBuffer.VertexType; // Bind the vertex buffer we'll use. Note that this needs to be done // before setting up any vertex format state. NativeGraphicsBuffer vertexBuffer = batch.VertexBuffer.NativeVertex as NativeGraphicsBuffer; NativeGraphicsBuffer.Bind(GraphicsBufferType.Vertex, vertexBuffer); bool first = (i == 0); bool sameMaterial = lastRendered != null && lastRendered.Material.Equals(batch.Material); // Setup vertex bindings. Note that the setup differs based on the // materials shader, so material changes can be vertex binding changes. if (lastRendered != null) { this.FinishVertexFormat(lastRendered.Material, lastRendered.VertexBuffer.VertexType); } this.SetupVertexFormat(batch.Material, vertexType); // Setup material when changed. if (!sameMaterial) { this.SetupMaterial( batch.Material, lastRendered != null ? lastRendered.Material : null); } // Draw the current batch this.DrawVertexBatch( batch.VertexBuffer, batch.VertexRanges, batch.VertexMode); drawCalls++; lastRendered = batch; } // Cleanup after rendering NativeGraphicsBuffer.Bind(GraphicsBufferType.Vertex, null); NativeGraphicsBuffer.Bind(GraphicsBufferType.Index, null); if (lastRendered != null) { this.FinishMaterial(lastRendered.Material); this.FinishVertexFormat(lastRendered.Material, lastRendered.VertexBuffer.VertexType); } if (this.renderStats != null) { this.renderStats.DrawCalls += drawCalls; } this.FinishSharedParameters(); }
void IGraphicsBackend.BeginRendering(IDrawDevice device, RenderOptions options, RenderStats stats) { DebugCheckOpenGLErrors(); this.CheckRenderingCapabilities(); this.currentDevice = device; this.renderOptions = options; this.renderStats = stats; // Prepare a shared index buffer object, in case we don't have one yet if (this.sharedBatchIBO == null) { this.sharedBatchIBO = new NativeGraphicsBuffer(GraphicsBufferType.Index); } // 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 (borrom-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 GL.Enable(EnableCap.ScissorTest); GL.Enable(EnableCap.DepthTest); if (options.DepthTest) { GL.DepthFunc(DepthFunction.Lequal); } else { GL.DepthFunc(DepthFunction.Always); } // Prepare shared matrix stack for rendering Matrix4 viewMatrix = options.ViewMatrix; Matrix4 projectionMatrix = options.ProjectionMatrix; if (NativeRenderTarget.BoundRT != null) { Matrix4 flipOutput = Matrix4.CreateScale(1.0f, -1.0f, 1.0f); projectionMatrix = projectionMatrix * flipOutput; } this.renderOptions.ShaderParameters.Set( BuiltinShaderFields.ViewMatrix, viewMatrix); this.renderOptions.ShaderParameters.Set( BuiltinShaderFields.ProjectionMatrix, projectionMatrix); this.renderOptions.ShaderParameters.Set( BuiltinShaderFields.ViewProjectionMatrix, viewMatrix * projectionMatrix); }