/// <summary> /// 2D Arrays or 3D from the bound read framebuffer (from sx/sy) into this texture at x/y -2D Array or 3D /// See <href>https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glCopyTexSubImage3D.xhtml</href> /// </summary> /// <param name="miplevel">Mip Level to store data into</param> /// <param name="xoffset">Left offset to store in the texture</param> /// <param name="yoffset">Bottom offset to store in the texture</param> /// <param name="zoffset">Depth offset to store in the texture</param> /// <param name="x">Left corner to copy</param> /// <param name="y">Bottom corner to copy</param> /// <param name="width">Width to copy</param> /// <param name="height">Height to copy</param> public void CopyFromReadFrameBuffer2da3d(int miplevel, int xoffset, int yoffset, int zoffset, int x, int y, int width, int height) { System.Diagnostics.Debug.Assert(this is GLTexture2DArray || this is GLTexture3D); System.Diagnostics.Debug.Assert(context == GLStatics.GetContext(), "Context incorrect"); GL.CopyTextureSubImage3D(Id, miplevel, xoffset, yoffset, zoffset, x, y, width, height); GLStatics.Check(); }
/// <summary> /// Constructor /// </summary> /// <param name="name">Name of bitmap collection</param> /// <param name="rlist">Render list to draw into</param> /// <param name="bitmapsize">Bit map size</param> /// <param name="mipmaplevels">Mip map levels</param> /// <param name="textureformat">Texture format of bitmaps</param> /// <param name="cullface">True to cull face</param> /// <param name="depthtest">True to depth test</param> /// <param name="maxpergroup">Maximum number of bitmaps per group</param> /// <param name="yfixed">Set true to fix Y co-ord externally</param> public GLBitmaps(string name, GLRenderProgramSortedList rlist, Size bitmapsize, int mipmaplevels = 3, OpenTK.Graphics.OpenGL4.SizedInternalFormat textureformat = OpenTK.Graphics.OpenGL4.SizedInternalFormat.Rgba8, bool cullface = true, bool depthtest = true, int maxpergroup = int.MaxValue, bool yfixed = false) { this.name = name; this.context = GLStatics.GetContext(); int maxdepthpertexture = GL4Statics.GetMaxTextureDepth(); // limits the number of textures per 2darray int max = Math.Min(maxdepthpertexture, maxpergroup); //note RI uses a VertexArray to load the matrix in, so not limited by that (max size of uniform buffer) matrixbuffers = new GLSetOfMatrixBufferWithGenerations(items, max); matrixbuffers.AddedNewGroup += AddedNewGroup; // hook up call back to say i've made a group renderlist = rlist; this.bitmapsize = bitmapsize; shader = new GLShaderPipeline(new GLPLVertexShaderMatrixQuadTexture(yfixed), new GLPLFragmentShaderTexture2DIndexed(0, alphablend: true)); items.Add(shader); renderstate = GLRenderState.Quads(); renderstate.CullFace = cullface; renderstate.DepthTest = depthtest; renderstate.ClipDistanceEnable = 1; // we are going to cull primitives which are deleted texmipmaplevels = mipmaplevels; this.textureformat = textureformat; }
/// <summary> /// 1D Textures from the bound read framebuffer (from x/y) into this texture at xoffset /// See <href>https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glCopyTexSubImage2D.xhtml</href> /// </summary> /// <param name="miplevel">Mip Level to store data into</param> /// <param name="xoffset">Left offset to store in the texture</param> /// <param name="x">Left corner to copy</param> /// <param name="y">Bottom corner to copy</param> /// <param name="width">Width to copy</param> public void CopyFromReadFrameBuffer1d(int miplevel, int xoffset, int x, int y, int width) { System.Diagnostics.Debug.Assert(this is GLTexture1D); System.Diagnostics.Debug.Assert(context == GLStatics.GetContext(), "Context incorrect"); GL.CopyTextureSubImage1D(Id, miplevel, xoffset, x, y, width); GLStatics.Check(); }
/// <summary>Zero the buffer from this position and length</summary> public void Zero(int pos, int length) { System.Diagnostics.Debug.Assert(context == GLStatics.GetContext(), "Context incorrect"); // safety System.Diagnostics.Debug.Assert(Length != 0 && pos >= 0 && length <= Length && pos + length <= Length); GL.ClearNamedBufferSubData(Id, PixelInternalFormat.R32ui, (IntPtr)pos, length, PixelFormat.RedInteger, PixelType.UnsignedInt, (IntPtr)0); GLStatics.Check(); }
/// <summary> /// Set the sampler mode (GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER, GL_MIRRORED_REPEAT, GL_REPEAT, or GL_MIRROR_CLAMP_TO_EDGE) (Use OpenTK names) /// </summary> /// <param name="s">Texture wrap width</param> public void SetSamplerMode(TextureWrapMode s) { System.Diagnostics.Debug.Assert(context == GLStatics.GetContext(), "Context incorrect"); int st = (int)s; GL.TextureParameterI(Id, TextureParameterName.TextureWrapS, ref st); }
/// <summary> /// Fill vertex buffer with vector4's, and write an indirect to indirectbuffer N /// </summary> /// <param name="vertices">Array of vertices to store</param> /// <param name="sourceoffset">Start position in array</param> /// <param name="sourcelength">Length of store</param> /// <param name="indirectbuffer">Which indirect buffer to store indexes into</param> /// <param name="vertexcount">If greater or equal to zero, use this count for indirects, else user source length</param> /// <param name="vertexbaseindex">Vertex base index for indirect</param> /// <param name="ic">Instance count for indirect</param> /// <param name="baseinstance">If greater or equal to zero, use this, else estimate the base instance number based on vertex position. </param> /// <returns>True if filled</returns> public bool Fill(Vector4[] vertices, int sourceoffset, int sourcelength, int indirectbuffer, int vertexcount = -1, int vertexbaseindex = 0, int ic = 1, int baseinstance = -1) // baseinstance, <0 use CurrentPos on vertex buffer to estimate instance number, else use this { System.Diagnostics.Debug.Assert(context == GLStatics.GetContext(), "Context incorrect"); CreateIndirect(indirectbuffer); if (EnoughSpaceVertex(sourcelength, indirectbuffer)) { Vertex.Fill(vertices, sourceoffset, sourcelength); // creates a position //Vertex.Fill(vertices); // creates a position // System.Diagnostics.Debug.WriteLine($"Vertex buf {Vertex.Positions.Last()} size {vertices.Length * GLBuffer.Vec4size}"); vertexcount = vertexcount >= 0 ? vertexcount : sourcelength; baseinstance = baseinstance >= 0 ? baseinstance : (Vertex.Positions.Last() / GLBuffer.Vec4size); int pos = Indirects[indirectbuffer].Positions.Count * GLBuffer.WriteIndirectArrayStride; Indirects[indirectbuffer].AddPosition(pos); Indirects[indirectbuffer].StartWrite(pos, GLBuffer.WriteIndirectArrayStride); Indirects[indirectbuffer].WriteIndirectArray(vertexcount, ic, vertexbaseindex, baseinstance); Indirects[indirectbuffer].StopReadWrite(); return(true); } else { return(false); } }
/// <summary> Stop a read or write sequence, release buffer back to use </summary> public void StopReadWrite() { System.Diagnostics.Debug.Assert(context == GLStatics.GetContext(), "Context incorrect"); // safety GL.UnmapNamedBuffer(Id); mapmode = MapMode.None; GLStatics.Check(); }
/// <summary> /// Add a bitmap to the collection. /// </summary> /// <param name="tag">Tag for this bitmap, may be null</param> /// <param name="bmp">Bitmap</param> /// <param name="bmpmipmaplevels">The bitmap mip map levels</param> /// <param name="worldpos">Position of bitmap in world</param> /// <param name="size">Size to draw bitmap in world.</param> /// <param name="rotationradians">Rotation of bitmap (ignored if rotates below are on)</param> /// <param name="rotatetoviewer">True to rotate to viewer in azimuth</param> /// <param name="rotateelevation">True to rotate to viewer in elevation</param> /// <param name="alphafadescalar">Alpha Fade scalar on distance</param> /// <param name="alphafadepos">Alpha fade distance. Negative for fade in, positive for fade out </param> /// <param name="ownbitmap"></param> /// <param name="visible">True if visible on start</param> /// <returns></returns> // add a bitmap, indicate if owned by class or you. Gives back group no, position in group, total in group public virtual Tuple <int, int, int> Add(object tag, Bitmap bmp, int bmpmipmaplevels, Vector3 worldpos, Vector3 size, Vector3 rotationradians, bool rotatetoviewer = false, bool rotateelevation = false, float alphafadescalar = 0, float alphafadepos = 0, bool ownbitmap = false, bool visible = true ) { System.Diagnostics.Debug.Assert(context == GLStatics.GetContext(), "Bitmaps detected context incorrect"); Matrix4 mat = GLPLVertexShaderMatrixQuadTexture.CreateMatrix(worldpos, size, rotationradians, rotatetoviewer, rotateelevation, alphafadescalar, alphafadepos, 0, visible); var gpc = matrixbuffers.Add(tag, ownbitmap ? bmp : null, mat); // group, pos, total in group // System.Diagnostics.Debug.WriteLine("Make bitmap {0} {1} {2} at {3}", gpc.Item1, gpc.Item2, gpc.Item3 , worldpos); grouptextureslist[gpc.Item1].LoadBitmap(bmp, gpc.Item2, false, bmpmipmaplevels); // texture does not own them, we may do grouprenderlist[gpc.Item1].InstanceCount = gpc.Item3; // update instance count to items in group return(gpc); }
///<summary>Create an empty buffer of this standard, default is std130. Standard defines the layout of members of the buffer. See OpenGL</summary> public GLBuffer(bool std430 = false) : base(std430) { GL.CreateBuffers(1, out int id); // this actually makes the buffer, GenBuffer does not - just gets a name GLStatics.RegisterAllocation(typeof(GLBuffer)); GLStatics.Check(); Id = id; context = GLStatics.GetContext(); }
/// <summary> Set Min Mag filter to linear </summary> public void SetMinMagLinear() { System.Diagnostics.Debug.Assert(context == GLStatics.GetContext(), "Context incorrect"); var textureFilter = (int)All.Linear; GL.TextureParameterI(Id, TextureParameterName.TextureMinFilter, ref textureFilter); GL.TextureParameterI(Id, TextureParameterName.TextureMagFilter, ref textureFilter); }
/// <summary> /// Copy to another buffer. Other buffer must be Allocated to the size otherpos+length /// </summary> /// <param name="other">Other buffer</param> /// <param name="startpos">Start posiiton in buffer</param> /// <param name="otherpos">Position to store it in other buffer</param> /// <param name="length">Copy length</param> /// <param name="hint"></param> public void CopyTo(GLBuffer other, int startpos, int otherpos, int length, BufferUsageHint hint = BufferUsageHint.StaticDraw) // newlength can be zero, meaning discard and go back to start { System.Diagnostics.Debug.Assert(context == GLStatics.GetContext(), "Context incorrect"); // safety int ourend = startpos + length; int otherend = otherpos + length; System.Diagnostics.Debug.Assert(Length >= ourend && other.Length >= otherend); GL.CopyNamedBufferSubData(Id, other.Id, (IntPtr)startpos, (IntPtr)otherpos, length); }
/// <summary> /// Construct a control display /// </summary> /// <param name="items">Items to store GL data to</param> /// <param name="win">GLWindowControl to hook to</param> /// <param name="mc">Matrix Calc to use</param> /// <param name="depthtest">Enable depth test</param> /// <param name="startz">Start Z for nearest top level window</param> /// <param name="deltaz">Delta Z between each top level window</param> /// <param name="arbbufferid">ARB buffer to use for texture bindless storage</param> public GLControlDisplay(GLItemsList items, GLWindowControl win, GLMatrixCalc mc, bool depthtest = true, // do depth testing or not float startz = 0.001f, // z for the deepest window (only will apply if depth testing float deltaz = 0.001f, // delta betwwen them int arbbufferid = 10 ) : base("displaycontrol", new Rectangle(0, 0, mc.ScreenCoordMax.Width, mc.ScreenCoordMax.Height)) { glwin = win; MatrixCalc = mc; context = GLStatics.GetContext(); this.items = items; vertexes = items.NewBuffer(); vertexarray = items.NewVertexArray(); vertexes.Bind(vertexarray, 0, 0, vertexesperentry * sizeof(float)); // bind to 0, from 0, 2xfloats. Must bind after vertexarray is made as its bound during construction vertexarray.Attribute(0, 0, vertexesperentry, OpenTK.Graphics.OpenGL4.VertexAttribType.Float); // bind 0 on attr 0, 2 components per vertex GLRenderState rc = GLRenderState.Tri(); rc.PrimitiveRestart = 0xff; rc.DepthTest = depthtest; this.startz = startz; this.deltaz = deltaz; ri = new GLRenderableItem(PrimitiveType.TriangleStrip, rc, 0, vertexarray); // create a renderable item ri.CreateRectangleElementIndexByte(items.NewBuffer(), 255 / 5); // note this limits top level controls number to 255/5. ri.DrawCount = 0; // nothing to draw at this point shader = new GLShaderPipeline(new GLPLVertexShaderScreenTexture(), new GLPLFragmentShaderBindlessTexture(arbbufferid, true, discardiftransparent: true)); items.Add(shader); textures = new Dictionary <GLBaseControl, GLTexture2D>(); size = new Dictionary <GLBaseControl, Size>(); visible = new Dictionary <GLBaseControl, bool>(); texturebinds = items.NewBindlessTextureHandleBlock(arbbufferid); glwin.MouseMove += Gc_MouseMove; glwin.MouseClick += Gc_MouseClick; glwin.MouseDoubleClick += Gc_MouseDoubleClick; glwin.MouseDown += Gc_MouseDown; glwin.MouseUp += Gc_MouseUp; glwin.MouseEnter += Gc_MouseEnter; glwin.MouseLeave += Gc_MouseLeave; glwin.MouseWheel += Gc_MouseWheel; glwin.KeyDown += Gc_KeyDown; glwin.KeyUp += Gc_KeyUp; glwin.KeyPress += Gc_KeyPress; glwin.Resize += Gc_Resize; glwin.Paint += Gc_Paint; suspendLayoutCount = 0; }
/// <summary> /// Constructor /// </summary> /// <param name="items">Item list to store buffers into</param> /// <param name="vertsize">Size of vertex buffer</param> /// <param name="indirectsize">Size of indirect buffer</param> /// <param name="std430">Std430 layout</param> /// <param name="bufferusagehint">Buffer usage hint</param> public GLVertexBufferIndirect(GLItemsList items, int vertsize, int indirectsize, bool std430 = false, BufferUsageHint bufferusagehint = BufferUsageHint.StaticDraw) { this.items = items; this.indirectsize = indirectsize; this.bufferusage = bufferusagehint; this.context = GLStatics.GetContext(); Vertex = new GLBuffer(vertsize, std430, bufferusagehint); items.Add(Vertex); }
/// <summary> /// Set Min Mag filter on texture /// </summary> /// <param name="minfilter">Min filter (default LinearMipmapLinear)</param> /// <param name="maxfilter">Max filter (default Linear)</param> public void SetMinMagFilter(All minfilter = All.LinearMipmapLinear, All maxfilter = All.Linear) { System.Diagnostics.Debug.Assert(context == GLStatics.GetContext(), "Context incorrect"); var textureMinFilter = (int)minfilter; GL.TextureParameterI(Id, TextureParameterName.TextureMinFilter, ref textureMinFilter); var textureMagFilter = (int)maxfilter; GL.TextureParameterI(Id, TextureParameterName.TextureMagFilter, ref textureMagFilter); }
/// <summary>Call to ensure this texture has an ARB texture ID</summary> public long AcquireArbId() { System.Diagnostics.Debug.Assert(context == GLStatics.GetContext(), "Context incorrect"); if (arbid == -1) { arbid = OpenTK.Graphics.OpenGL.GL.Arb.GetTextureHandle(Id); OpenTK.Graphics.OpenGL.GL.Arb.MakeTextureHandleResident(arbid); // can't do this twice! } return(arbid); }
/// <summary>Bind buffer to vertext array at this bindingindex, with the start position stride and divisor</summary> public void Bind(GLVertexArray va, int bindingindex, int start, int stride, int divisor = 0) // set buffer binding to a VA { System.Diagnostics.Debug.Assert(context == GLStatics.GetContext(), "Context incorrect"); // safety System.Diagnostics.Debug.Assert(mapmode == MapMode.None); // catch unmap missing. Since binding to VA can be done before buffer is full, then don't check BufferSize va.Bind(); GL.BindVertexBuffer(bindingindex, Id, (IntPtr)start, stride); // this buffer to binding index GL.VertexBindingDivisor(bindingindex, divisor); GLStatics.Check(); //System.Diagnostics.Debug.WriteLine("BUFBIND " + bindingindex + " To B" + Id + " pos " + start + " stride " + stride + " divisor " + divisor); }
/// <summary> /// From any type of ImageTarget into this - All types /// See <href>https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glCopyImageSubData.xhtml</href> /// </summary> /// <param name="srcid">Texture or Renderbuffer source</param> /// <param name="srcTarget">Source image type</param> /// <param name="srcmiplevel">Mipmap level to copy from</param> /// <param name="sx">Source X</param> /// <param name="sy">Source Y</param> /// <param name="sz">Source Z level</param> /// <param name="dmiplevel">Destination mip level</param> /// <param name="dx">Destination X</param> /// <param name="dy">Destination Y</param> /// <param name="dz">Destination Z (0 for 2D/1D</param> /// <param name="width">Destination Width</param> /// <param name="height">Destination Height</param> public void CopyFrom(int srcid, ImageTarget srcTarget, int srcmiplevel, int sx, int sy, int sz, int dmiplevel, int dx, int dy, int dz, int width, int height) { System.Diagnostics.Debug.Assert(context == GLStatics.GetContext(), "Context incorrect"); // not sure, according to spec, the dest target may just need to be a texture type.. ImageTarget it = this is GLTexture2D ? ImageTarget.Texture2D : this is GLTexture2DArray ? ImageTarget.Texture2DArray : this is GLTexture1D ? ImageTarget.Texture1D : ImageTarget.Texture3D; GL.CopyImageSubData(srcid, srcTarget, srcmiplevel, sx, sy, sz, Id, it, dmiplevel, dx, dy, dz, width, height, 1); GLStatics.Check(); }
/// <summary>Bind buffer to parameter binding point</summary> public void BindParameter() { System.Diagnostics.Debug.Assert(context == GLStatics.GetContext(), "Context incorrect"); // safety System.Diagnostics.Debug.Assert(mapmode == MapMode.None && Length > 0); // catch unmap missing or nothing in buffer // if (parameterbindindex != Id) // removed for testing { GL.BindBuffer((BufferTarget)0x80ee, Id); // fudge due to ID not being there in 3.3.2 GLStatics.Check(); parameterbindindex = Id; } }
///<summary> Bind to query buffer</summary> public void BindQuery() { System.Diagnostics.Debug.Assert(context == GLStatics.GetContext(), "Context incorrect"); // safety System.Diagnostics.Debug.Assert(mapmode == MapMode.None && Length > 0); // catch unmap missing or nothing in buffer if (querybindindex != Id) { GL.BindBuffer(BufferTarget.QueryBuffer, Id); GLStatics.Check(); querybindindex = Id; } }
/// <summary>Bind buffer to indirect binding point</summary> public void BindIndirect() { System.Diagnostics.Debug.Assert(context == GLStatics.GetContext(), "Context incorrect"); // safety System.Diagnostics.Debug.Assert(mapmode == MapMode.None && Length > 0); // catch unmap missing or nothing in buffer //if (indirectbindindex != Id) // removed for testing { GL.BindBuffer(BufferTarget.DrawIndirectBuffer, Id); GLStatics.Check(); indirectbindindex = Id; } }
///<summary>Allocate or reallocate buffer size. Call first to set buffer size. Allow for alignment in your size</summary> public void AllocateBytes(int bytessize, BufferUsageHint hint = BufferUsageHint.StaticDraw) { System.Diagnostics.Debug.Assert(context == GLStatics.GetContext(), "Context incorrect"); // safety if (bytessize > 0) // can call twice - get fresh buffer each time { Length = bytessize; GL.NamedBufferData(Id, Length, (IntPtr)0, hint); // set buffer size var err = GL.GetError(); System.Diagnostics.Debug.Assert(err == ErrorCode.NoError, $"GL NamedBuffer error {err}"); // check for any errors, always. ResetPositions(); } }
/// <summary> /// Clear part of an image /// See <href>https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glClearTexSubImage.xhtml</href> /// </summary> /// <param name="level">Level to clear</param> /// <param name="x">Left edge</param> /// <param name="y">Lower edge</param> /// <param name="z">Front of region</param> /// <param name="width">Width to clear</param> /// <param name="height">Height to clear</param> /// <param name="depth">Depth to clear</param> /// <param name="red">Red value, 0-1</param> /// <param name="green">Green value, 0-1</param> /// <param name="blue">Blue value, 0-1</param> /// <param name="alpha">Alpha value, 0-1</param> public void ClearSubImage(int level, int x, int y, int z, int width, int height, int depth, float red, float green, float blue, float alpha) { System.Diagnostics.Debug.Assert(context == GLStatics.GetContext(), "Context incorrect"); int size = Marshal.SizeOf <float>() * 4; IntPtr pnt = Marshal.AllocHGlobal(size); float[] a = new float[] { red, green, blue, alpha }; Marshal.Copy(a, 0, pnt, a.Length); GL.ClearTexSubImage(Id, level, x, y, z, width, height, depth, PixelFormat.Rgba, PixelType.Float, pnt); Marshal.FreeHGlobal(pnt); GLStatics.Check(); }
/// <summary> /// Clear image to this color /// </summary> /// <param name="level">Level to clear</param> /// <param name="red">Red value, 0-1</param> /// <param name="green">Green value, 0-1</param> /// <param name="blue">Blue value, 0-1</param> /// <param name="alpha">Alpha value, 0-1</param> public void ClearImage(int level, float red, float green, float blue, float alpha) // confirmed { System.Diagnostics.Debug.Assert(context == GLStatics.GetContext(), "Context incorrect"); int size = Marshal.SizeOf <float>() * 4; IntPtr pnt = Marshal.AllocHGlobal(size); float[] a = new float[] { red, green, blue, alpha }; Marshal.Copy(a, 0, pnt, a.Length); GL.ClearTexImage(Id, level, PixelFormat.Rgba, PixelType.Float, pnt); Marshal.FreeHGlobal(pnt); GLStatics.Check(); }
/// <summary> /// Bind buffer to the specific transform feedback buffer object at binding index /// See <href>https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTransformFeedbackBufferBase.xhtml</href> /// </summary> /// <param name="bindingindex">Index of binding point within xfb</param> /// <param name="xfb">Name of the transformation feeback buffer object. 0 means default transform</param> /// <param name="offset">Offset into buffer if size != -1</param> /// <param name="size">Buffer area allocated. If size == -1, all of buffer</param> public void BindTransformFeedback(int bindingindex, int xfb = 0, int offset = 0, int size = -1) { System.Diagnostics.Debug.Assert(context == GLStatics.GetContext(), "Context incorrect"); // safety System.Diagnostics.Debug.Assert(mapmode == MapMode.None && Length > 0); // catch unmap missing or nothing in buffer if (size == -1) { GL.TransformFeedbackBufferBase(xfb, bindingindex, Id); } else { GL.TransformFeedbackBufferRange(xfb, bindingindex, Id, (IntPtr)offset, size); } GLStatics.Check(); }
///<summary> Begin a write. Select position and size. (datasize=0=all buffer). /// Buffer access mask decides what to keep about the range, default is to wipe the mapped area. Use 0 in bam not to do this. Trap for young players here</summary> public void StartWrite(int fillpos, int datasize = 0, BufferAccessMask bufferaccessmask = BufferAccessMask.MapInvalidateRangeBit) { if (datasize == 0) { datasize = Length - fillpos; } System.Diagnostics.Debug.Assert(mapmode == MapMode.None && fillpos >= 0 && fillpos + datasize <= Length); // catch double maps System.Diagnostics.Debug.Assert(context == GLStatics.GetContext(), "Context incorrect"); // safety CurrentPtr = GL.MapNamedBufferRange(Id, (IntPtr)fillpos, datasize, BufferAccessMask.MapWriteBit | bufferaccessmask); CurrentPos = fillpos; mapmode = MapMode.Write; GLStatics.Check(); }
/// <summary> /// Start read on definable area /// </summary> public void StartRead(int fillpos, int datasize = 0) // read the buffer (datasize=0=all buffer) { if (datasize == 0) { datasize = Length - fillpos; } System.Diagnostics.Debug.Assert(mapmode == MapMode.None && fillpos >= 0 && fillpos + datasize <= Length); // catch double maps System.Diagnostics.Debug.Assert(context == GLStatics.GetContext(), "Context incorrect"); // safety CurrentPtr = GL.MapNamedBufferRange(Id, (IntPtr)fillpos, datasize, BufferAccessMask.MapReadBit); CurrentPos = fillpos; mapmode = MapMode.Read; GLStatics.Check(); }
/// <summary> /// Resize the buffer. Note this results in a new GL Buffer, so any VertexArrays will need remaking. Also note the Positions are maintained, you may want to delete those manually. /// </summary> public void Resize(int newlength, BufferUsageHint hint = BufferUsageHint.StaticDraw) // newlength can be zero, meaning discard and go back to start { System.Diagnostics.Debug.Assert(context == GLStatics.GetContext(), "Context incorrect"); // safety if (Length != newlength) { GL.CreateBuffers(1, out int newid); if (newlength > 0) { GL.NamedBufferData(newid, newlength, (IntPtr)0, hint); // set buffer size var err = GL.GetError(); System.Diagnostics.Debug.Assert(err == ErrorCode.NoError, $"GL NamedBuffer error {err}"); // check for any errors, always. if (Length > 0) // if previous buffer had data { GL.CopyNamedBufferSubData(Id, newid, (IntPtr)0, (IntPtr)0, Math.Min(Length, newlength)); } } GL.DeleteBuffer(Id); // delete old buffer Id = newid; // swap to new Length = newlength; } }
/// <summary>Dispose of texture, will free bitmaps if owned</summary> public void Dispose() // you can double dispose. { System.Diagnostics.Debug.Assert(context == GLStatics.GetContext(), "Context incorrect"); if (Id >= 0) { if (arbid != -1) // if its been arb'd, de-arb it { OpenTK.Graphics.OpenGL.GL.Arb.MakeTextureHandleNonResident(arbid); // can't do this twice! arbid = -1; } GL.DeleteTexture(Id); GLStatics.RegisterDeallocation(GetType()); Id = -2; // -2 means made, then destroyed if (BitMaps != null) { for (int i = 0; i < BitMaps.Length; i++) { if (OwnBitMaps[i] && BitMaps[i] != null) // we may have empty spaces in the bitmap list { BitMaps[i].Dispose(); } } BitMaps = null; OwnBitMaps = null; } } else { if (Id == -2) // goes -1 -> ID -> -2, never uses stays as -1 { System.Diagnostics.Trace.WriteLine($"OFC Warning - double disposing of a texture block in {GetType().FullName}"); // only an warning due to the fact you can create and not use } } }
/// <summary> /// Make a GL Control and attach to control. /// </summary> /// <param name="attachcontrol">Control to attach the GL Control to</param> /// <param name="mode">TK graphics mode</param> public GLWinFormControl(Control attachcontrol, OpenTK.Graphics.GraphicsMode mode = null) { if (mode == null) { mode = OpenTK.Graphics.GraphicsMode.Default; } glControl = new GLControlKeyOverride(mode); glControl.MakeCurrent(); // make sure GLControl is current context selected, in case operating with multiples glControl.Dock = DockStyle.Fill; glControl.BackColor = System.Drawing.Color.Black; glControl.Name = "glControl"; glControl.TabIndex = 0; glControl.VSync = true; glControl.PreviewKeyDown += Gl_PreviewKeyDown; attachcontrol.Controls.Add(glControl); glControl.MouseDown += Gc_MouseDown; glControl.MouseUp += Gc_MouseUp; glControl.MouseMove += Gc_MouseMove; glControl.MouseEnter += Gc_MouseEnter; glControl.MouseLeave += Gc_MouseLeave; glControl.MouseClick += Gc_MouseClick; glControl.MouseDoubleClick += Gc_MouseDoubleClick; glControl.MouseWheel += Gc_MouseWheel; glControl.KeyDown += Gc_KeyDown; glControl.KeyUp += Gc_KeyUp; glControl.KeyPress += Gc_KeyPress; glControl.Resize += Gc_Resize; glControl.Paint += GlControl_Paint; context = GLStatics.GetContext(); System.Diagnostics.Debug.WriteLine($"GL Context {context} created"); gltime.Start(); }
/// <summary> /// Creator of this draw set /// </summary> /// <param name="textures"> number of 2D textures to allow maximum (limited by GL)</param> /// <param name="estimateditemspergroup">Estimated objects per group, this adds on vertext buffer space to allow for mat4 alignment. Smaller means more allowance.</param> /// <param name="mingroups">Minimum groups to have</param> /// <param name="objectbuffer">Object buffer to use</param> /// <param name="objectvertexes">Number of object vertexes</param> /// <param name="objrc">The object render state control</param> /// <param name="objpt">The object draw primitive type</param> /// <param name="texturesize">The size of the label</param> /// <param name="textrc">The text render state</param> /// <param name="textureformat">The texture format for the text</param> /// <param name="debuglimittexture">For debug, set this to limit maximum number of entries. 0 = off</param> /// <returns></returns> public Tuple <GLRenderableItem, GLRenderableItem> Create( int textures, int estimateditemspergroup, int mingroups, GLBuffer objectbuffer, int objectvertexes, GLRenderState objrc, PrimitiveType objpt, Size texturesize, GLRenderState textrc, SizedInternalFormat textureformat, int debuglimittexture = 0) { this.objectvertexescount = objectvertexes; this.context = GLStatics.GetContext(); // Limit number of 2d textures in a single 2d array int maxtextper2darray = GL4Statics.GetMaxTextureDepth(); if (debuglimittexture > 0) { maxtextper2darray = debuglimittexture; } // set up number of textmaps bound int maxtexturesbound = GL4Statics.GetMaxFragmentTextures(); int textmaps = Math.Min(textures, maxtexturesbound); // which then give us the number of stars we can do int objectcount = textmaps * maxtextper2darray; int groupcount = objectcount / estimateditemspergroup; groupcount = Math.Max(mingroups, groupcount); // min groups // System.Diagnostics.Debug.WriteLine($"GLObjectWithLabels oc {objectcount} gc {groupcount}"); // estimate maximum vert buffer needed, allowing for extra due to the need to align the mat4 int vertbufsize = objectcount * (GLBuffer.Vec4size + GLBuffer.Mat4size) + // for a vec4 + mat4 per object groupcount * GLBuffer.Mat4size; // and for groupcount Mat4 fragmentation per group // create the vertex indirect buffer dataindirectbuffer = new GLVertexBufferIndirect(items, vertbufsize, GLBuffer.WriteIndirectArrayStride * groupcount, true, BufferUsageHint.DynamicDraw); // objects ObjectRenderer = GLRenderableItem.CreateVector4Vector4(items, objpt, objrc, objectbuffer, 0, 0, // binding 0 is shapebuf, offset 0, no draw count yet dataindirectbuffer.Vertex, 0, // binding 1 is vertex's world positions, offset 0 null, 0, 1); // no ic, second divisor 1 ObjectRenderer.BaseIndexOffset = 0; // offset in bytes where commands are stored ObjectRenderer.MultiDrawCountStride = GLBuffer.WriteIndirectArrayStride; // text this.textures = new GLTexture2DArray[textmaps]; for (int i = 0; i < this.textures.Length; i++) { int n = Math.Min(objectcount, maxtextper2darray); this.textures[i] = new GLTexture2DArray(texturesize.Width, texturesize.Height, n, textureformat, 1); items.Add(this.textures[i]); objectcount -= maxtextper2darray; } TextRenderer = GLRenderableItem.CreateMatrix4(items, PrimitiveType.Quads, textrc, dataindirectbuffer.Vertex, 0, 0, //attach buffer with matrices, no draw count new GLRenderDataTexture(this.textures, 0), // binding 0 assign to our texture 2d 0, 1); //no ic, and matrix divide so 1 matrix per vertex set TextRenderer.BaseIndexOffset = 0; // offset in bytes where commands are stored TextRenderer.MultiDrawCountStride = GLBuffer.WriteIndirectArrayStride; return(new Tuple <GLRenderableItem, GLRenderableItem>(ObjectRenderer, TextRenderer)); }