/// <summary> /// Sets the data contained by the vertex buffer. /// </summary> private void SetDataInternal <T>(T[] data, Int32 offsetInBytes, Int32 countInBytes, SetDataOptions options) { var handle = GCHandle.Alloc(data, GCHandleType.Pinned); try { using (OpenGLState.ScopedBindElementArrayBuffer(buffer)) { var isPartialUpdate = (offsetInBytes > 0 || countInBytes < SizeInBytes); var isDiscarding = (options == SetDataOptions.Discard); if (isDiscarding || !isPartialUpdate) { gl.NamedBufferData(buffer, gl.GL_ELEMENT_ARRAY_BUFFER, this.size, isPartialUpdate ? null : handle.AddrOfPinnedObject().ToPointer(), usage); gl.ThrowIfError(); } if (isPartialUpdate) { gl.NamedBufferSubData(buffer, gl.GL_ELEMENT_ARRAY_BUFFER, (IntPtr)offsetInBytes, (IntPtr)countInBytes, handle.AddrOfPinnedObject().ToPointer()); gl.ThrowIfError(); } } } finally { handle.Free(); } }
/// <summary> /// Sets the data contained by the index buffer. /// </summary> /// <param name="data">An array containing the data to set in the index buffer.</param> /// <param name="offset">The offset into <paramref name="data"/> at which to begin setting elements into the buffer.</param> /// <param name="count">The number of elements from <paramref name="data"/> to set into the buffer.</param> /// <param name="options">A hint to the underlying driver indicating whether data will be overwritten by this operation.</param> public override void SetData <T>(T[] data, Int32 offset, Int32 count, SetDataOptions options) { Contract.Require(data, nameof(data)); Contract.EnsureRange(count > 0, nameof(count)); Contract.EnsureRange(offset >= 0 && offset + count <= data.Length, nameof(offset)); Contract.Ensure(count <= IndexCount, OpenGLStrings.DataTooLargeForBuffer); var handle = GCHandle.Alloc(data, GCHandleType.Pinned); try { using (OpenGLState.ScopedBindElementArrayBuffer(buffer)) { if (options == SetDataOptions.Discard) { gl.NamedBufferData(buffer, gl.GL_ELEMENT_ARRAY_BUFFER, this.size, null, usage); gl.ThrowIfError(); } var elementsize = GetElementSize(); var start = new IntPtr(offset * elementsize); var size = new IntPtr(elementsize * count); gl.NamedBufferSubData(buffer, gl.GL_ELEMENT_ARRAY_BUFFER, start, size, handle.AddrOfPinnedObject().ToPointer()); gl.ThrowIfError(); } } finally { handle.Free(); } }
/// <inheritdoc/> protected override void Dispose(Boolean disposing) { if (Disposed) { return; } if (disposing) { if (!Ultraviolet.Disposed) { if (vao != 0) { Ultraviolet.QueueWorkItem((state) => { var vaoName = ((OpenGLGeometryStream)state).vao; gl.DeleteVertexArray(vaoName); gl.ThrowIfError(); OpenGLState.DeleteVertexArrayObject(vaoName, 0, glElementArrayBufferBinding ?? 0); }, this); } } vbuffers.Clear(); ibuffer = null; } base.Dispose(disposing); }
/// <summary> /// Reallocates the renderbuffer's storage. /// </summary> /// <param name="width">The renderbuffer's width in pixels.</param> /// <param name="height">The renderbuffer's height in pixels.</param> private void AllocateRenderbufferStorage(Int32 width, Int32 height) { using (OpenGLState.ScopedBindRenderbuffer(renderbuffer, true)) { switch (format) { case RenderBufferFormat.Color: gl.RenderbufferStorage(gl.GL_RENDERBUFFER, gl.IsGLES2 ? gl.GL_RGBA4 : gl.GL_RGBA8, width, height); gl.ThrowIfError(); break; case RenderBufferFormat.Depth24Stencil8: gl.RenderbufferStorage(gl.GL_RENDERBUFFER, gl.GL_DEPTH24_STENCIL8, width, height); gl.ThrowIfError(); break; case RenderBufferFormat.Depth32: gl.RenderbufferStorage(gl.GL_RENDERBUFFER, gl.GL_DEPTH_COMPONENT32, width, height); gl.ThrowIfError(); break; case RenderBufferFormat.Depth16: gl.RenderbufferStorage(gl.GL_RENDERBUFFER, gl.GL_DEPTH_COMPONENT16, width, height); gl.ThrowIfError(); break; case RenderBufferFormat.Stencil8: gl.RenderbufferStorage(gl.GL_RENDERBUFFER, gl.GL_STENCIL_INDEX8, width, height); break; default: throw new NotSupportedException("format"); } } }
/// <summary> /// Initializes a new instance of the OpenGLRenderTarget2D class. /// </summary> /// <param name="uv">The Ultraviolet context.</param> /// <param name="width">The render target's width in pixels.</param> /// <param name="height">The render target's height in pixels.</param> /// <param name="usage">A <see cref="RenderTargetUsage"/> value specifying whether the /// render target's data is discarded or preserved when it is bound to the graphics device.</param> /// <param name="buffers">The collection of render buffers to attach to the target.</param> public OpenGLRenderTarget2D(UltravioletContext uv, Int32 width, Int32 height, RenderTargetUsage usage, IEnumerable <RenderBuffer2D> buffers = null) : base(uv) { Contract.EnsureRange(width > 0, nameof(width)); Contract.EnsureRange(height > 0, nameof(height)); // NOTE: If we're in an older version of GLES, we need to use glFramebufferTexture2D() glFramebufferTextureIsSupported = !gl.IsGLES || gl.IsVersionAtLeast(3, 2); var framebuffer = 0u; uv.QueueWorkItemAndWait(() => { using (OpenGLState.ScopedCreateFramebuffer(out framebuffer)) { if (buffers != null) { foreach (OpenGLRenderBuffer2D buffer in buffers) { AttachRenderBuffer(buffer); } } } }); this.width = width; this.height = height; this.framebuffer = framebuffer; this.renderTargetUsage = usage; }
/// <inheritdoc/> public override void Attach(RenderBuffer2D buffer) { Contract.Require(buffer, nameof(buffer)); Contract.Ensure <ArgumentException>( buffer.Width == width && buffer.Height == height, OpenGLStrings.RenderBufferIsWrongSize); Contract.EnsureNotDisposed(this, Disposed); Ultraviolet.ValidateResource(buffer); var sdlBuffer = (OpenGLRenderBuffer2D)buffer; Ultraviolet.QueueWorkItemAndWait(() => { using (OpenGLState.ScopedBindFramebuffer(framebuffer)) { AttachRenderBuffer(sdlBuffer); framebufferStatus = gl.CheckNamedFramebufferStatus(framebuffer, gl.GL_FRAMEBUFFER); gl.ThrowIfError(); } }); sdlBuffer.MarkAttached(); buffers.Add(sdlBuffer); }
/// <inheritdoc/> public override void Attach(IndexBuffer ibuffer) { Contract.Require(ibuffer, nameof(ibuffer)); Contract.EnsureNot(HasIndices, UltravioletStrings.GeometryStreamAlreadyHasIndices); Contract.EnsureNotDisposed(this, Disposed); Ultraviolet.ValidateResource(ibuffer); var sdlIndexBuffer = (OpenGLIndexBuffer)ibuffer; var sdlIndexBufferName = sdlIndexBuffer.OpenGLName; this.ibuffer = sdlIndexBuffer; if (IsUsingVertexArrayObject) { using (OpenGLState.ScopedBindVertexArrayObject(vao, 0, 0, true)) { gl.BindBuffer(gl.GL_ELEMENT_ARRAY_BUFFER, sdlIndexBufferName); gl.ThrowIfError(); } } this.glElementArrayBufferBinding = sdlIndexBufferName; this.indexBufferElementType = ibuffer.IndexElementType; }
/// <summary> /// Processes a resize operation for this texture. /// </summary> private void ProcessResize() { using (OpenGLState.ScopedBindTexture2D(texture, true)) { gl.TexImage2D(gl.GL_TEXTURE_2D, 0, (int)internalformat, width, height, 0, format, type, null); } }
/// <summary> /// Binds the geometry stream's buffers to the device in preparation for rendering. /// </summary> private void BindBuffers(UInt32?program, UInt32?offset) { unsafe { var previousBuffer = (uint)OpenGLState.GL_ARRAY_BUFFER_BINDING; if (IsUsingVertexArrayObject) { using (OpenGLState.ScopedBindVertexArrayObject(vao, glElementArrayBufferBinding ?? 0, force: !gl.IsVertexAttribBindingAvailable)) { if (program.HasValue) { DisableVertexAttributesOnCachedProgram(); } if (gl.IsVertexAttribBindingAvailable) { for (int i = 0; i < vbuffers.Count; i++) { var binding = vbuffers[i]; BindVertexAttributesForBuffer_NewAPI(binding.VertexBuffer, (UInt32)i, (UInt32)binding.InstanceFrequency, program, offset); } } else { for (int i = 0; i < vbuffers.Count; i++) { var binding = vbuffers[i]; BindVertexAttributesForBuffer_OldAPI(binding.VertexBuffer, (UInt32)i, (UInt32)binding.InstanceFrequency, program, offset); } } } } else { if (program.HasValue) { DisableVertexAttributesOnCachedProgram(); } for (int i = 0; i < vbuffers.Count; i++) { var binding = vbuffers[i]; BindVertexAttributesForBuffer_OldAPI(binding.VertexBuffer, (UInt32)i, (UInt32)binding.InstanceFrequency, program, offset); } OpenGLState.BindArrayBuffer(previousBuffer); } } }
/// <summary> /// Applies the effect pass state to the device. /// </summary> public override void Apply() { Contract.EnsureNotDisposed(this, Disposed); var program = programs[programIndex]; OpenGLState.UseProgram(program); foreach (var uniform in program.Uniforms) { uniform.Apply(); } }
/// <summary> /// Initializes a new instance of the OpenGLRenderBuffer2D class. /// </summary> /// <param name="uv">The Ultraviolet context.</param> /// <param name="format">The render buffer's format.</param> /// <param name="width">The render buffer's width in pixels.</param> /// <param name="height">The render buffer's height in pixels.</param> /// <param name="options">The render buffer's configuration options.</param> public OpenGLRenderBuffer2D(UltravioletContext uv, RenderBufferFormat format, Int32 width, Int32 height, RenderBufferOptions options) : base(uv) { Contract.EnsureRange(width > 0, nameof(width)); Contract.EnsureRange(height > 0, nameof(height)); this.format = format; this.width = width; this.height = height; this.immutable = (options & RenderBufferOptions.ImmutableStorage) == RenderBufferOptions.ImmutableStorage; this.willNotBeSampled = (options & RenderBufferOptions.WillNotBeSampled) == RenderBufferOptions.WillNotBeSampled; if (willNotBeSampled) { using (var state = OpenGLState.ScopedCreateRenderbuffer(out renderbuffer)) { AllocateRenderbufferStorage(width, height); } } else { switch (format) { case RenderBufferFormat.Color: this.texture = new OpenGLTexture2D(uv, gl.IsGLES2 ? gl.GL_RGBA : gl.GL_RGBA8, width, height, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, IntPtr.Zero, immutable, true); break; case RenderBufferFormat.Depth24Stencil8: this.texture = new OpenGLTexture2D(uv, gl.GL_DEPTH24_STENCIL8, width, height, gl.GL_DEPTH_STENCIL, gl.GL_UNSIGNED_INT_24_8, IntPtr.Zero, immutable, true); break; case RenderBufferFormat.Depth32: this.texture = new OpenGLTexture2D(uv, gl.GL_DEPTH_COMPONENT32, width, height, gl.GL_DEPTH_COMPONENT, gl.GL_UNSIGNED_INT, IntPtr.Zero, immutable, true); break; case RenderBufferFormat.Depth16: this.texture = new OpenGLTexture2D(uv, gl.GL_DEPTH_COMPONENT16, width, height, gl.GL_DEPTH_COMPONENT, gl.GL_UNSIGNED_SHORT, IntPtr.Zero, immutable, true); break; case RenderBufferFormat.Stencil8: this.texture = new OpenGLTexture2D(uv, gl.GL_STENCIL_INDEX8, width, height, gl.GL_STENCIL, gl.GL_UNSIGNED_INT, IntPtr.Zero, immutable, true); break; default: throw new NotSupportedException(nameof(format)); } } }
/// <summary> /// Applies the geometry stream to the graphics device. /// </summary> public void Apply() { Contract.EnsureNotDisposed(this, Disposed); if (IsUsingVertexArrayObject) { OpenGLState.BindVertexArrayObject(vao, 0, glElementArrayBufferBinding ?? 0); } else { if (ibuffer != null) { OpenGLState.BindElementArrayBuffer(ibuffer.OpenGLName); } } }
/// <summary> /// Applies the state object's values to the OpenGL context. /// </summary> public void Apply() { switch (stateType) { case OpenGLStateType.None: break; case OpenGLStateType.ActiveTexture: Apply_ActiveTexture(); break; case OpenGLStateType.BindTexture2D: Apply_BindTexture2D(); break; case OpenGLStateType.BindVertexArrayObject: Apply_BindVertexArrayObject(); break; case OpenGLStateType.BindArrayBuffer: Apply_BindArrayBuffer(); break; case OpenGLStateType.BindElementArrayBuffer: Apply_BindElementArrayBuffer(); break; case OpenGLStateType.BindFramebuffer: Apply_BindFramebuffer(); break; case OpenGLStateType.BindRenderbuffer: Apply_BindRenderbuffer(); break; case OpenGLStateType.UseProgram: Apply_UseProgram(); break; default: throw new InvalidOperationException(); } OpenGLState.VerifyCache(); }
/// <summary> /// Sets the texture's data. /// </summary> /// <param name="level">The mipmap level for which to set data.</param> /// <param name="rect">A rectangle describing the position and size of the data to set.</param> /// <param name="data">A pointer to the data to set.</param> /// <param name="offset">The index of the first element to set.</param> /// <param name="count">The number of elements to set.</param> /// <param name="stride">The number of elements in one row of data.</param> /// <param name="format">The format of the data being set.</param> /// <param name="origin">A <see cref="SetDataOrigin"/> value specifying the origin of the texture data in <paramref name="data"/>.</param> private unsafe void SetDataInternal(Int32 level, Rectangle?rect, IntPtr data, Int32 offset, Int32 count, Int32 stride, TextureDataFormat format, SetDataOrigin origin) { var region = rect ?? new Rectangle(0, 0, width, height); if (region.Width * region.Height != count) { throw new InvalidOperationException(UltravioletStrings.BufferIsWrongSize); } const Int32 SizeOfTextureElementInBytes = 4; var flipHorizontally = (origin == SetDataOrigin.TopRight || origin == SetDataOrigin.BottomRight); var flipVertically = (origin == SetDataOrigin.TopLeft || origin == SetDataOrigin.TopRight); TextureUtil.ReorientTextureData(data.ToPointer(), region.Width, region.Height, SizeOfTextureElementInBytes, flipHorizontally, flipVertically); if (flipHorizontally) { region = new Rectangle((width - region.Width) - region.X, region.Y, region.Width, region.Height); } if (flipVertically) { region = new Rectangle(region.X, (height - region.Height) - region.Y, region.Width, region.Height); } using (OpenGLState.ScopedBindTexture2D(texture)) { gl.PixelStorei(gl.GL_UNPACK_ROW_LENGTH, stride); gl.ThrowIfError(); gl.PixelStorei(gl.GL_UNPACK_ALIGNMENT, 1); gl.ThrowIfError(); gl.TextureSubImage2D(texture, gl.GL_TEXTURE_2D, level, region.X, region.Y, region.Width, region.Height, GetOpenGLTextureFormat(format), gl.GL_UNSIGNED_BYTE, data.ToPointer()); gl.ThrowIfError(); gl.PixelStorei(gl.GL_UNPACK_ROW_LENGTH, 0); gl.ThrowIfError(); } }
/// <summary> /// Sets the data contained by the vertex buffer. /// </summary> /// <param name="data">An array containing the data to set in the vertex buffer.</param> /// <param name="offset">The offset into <paramref name="data"/> at which to begin setting elements into the buffer.</param> /// <param name="count">The number of elements from <paramref name="data"/> to set into the buffer.</param> /// <param name="options">A hint to the underlying driver indicating whether data will be overwritten by this operation.</param> public override void SetData <T>(T[] data, Int32 offset, Int32 count, SetDataOptions options) { Contract.Require(data, nameof(data)); Contract.EnsureRange(count > 0, nameof(count)); Contract.EnsureRange(offset >= 0 && offset + count <= data.Length, nameof(offset)); Contract.Ensure(count <= VertexCount, OpenGLStrings.DataTooLargeForBuffer); var handle = GCHandle.Alloc(data, GCHandleType.Pinned); try { using (OpenGLState.ScopedBindArrayBuffer(buffer)) { if (options == SetDataOptions.Discard) { gl.NamedBufferData(buffer, gl.GL_ARRAY_BUFFER, this.size, null, usage); gl.ThrowIfError(); /* FIX: * I have no idea why the following code is necessary, but * it seems to fix flickering sprites on Intel HD 4000 devices. */ if (OpenGLState.SupportsVertexArrayObjects) { var vao = (uint)OpenGLState.GL_VERTEX_ARRAY_BINDING; gl.BindVertexArray(vao); gl.ThrowIfError(); } } var start = new IntPtr(offset * vdecl.VertexStride); var size = new IntPtr(vdecl.VertexStride * count); gl.NamedBufferSubData(buffer, gl.GL_ARRAY_BUFFER, start, size, handle.AddrOfPinnedObject().ToPointer()); gl.ThrowIfError(); } } finally { handle.Free(); } }
/// <summary> /// Initializes a new instance of the OpenGLIndexBuffer. /// </summary> /// <param name="uv">The Ultraviolet context.</param> /// <param name="itype">The index element type.</param> /// <param name="icount">The index element count.</param> /// <param name="usage">The buffer's usage type.</param> public OpenGLIndexBuffer(UltravioletContext uv, IndexBufferElementType itype, Int32 icount, UInt32 usage) : base(uv, itype, icount) { Contract.EnsureRange(icount >= 0, nameof(icount)); this.usage = usage; this.size = new IntPtr(GetElementSize() * icount); var buffer = 0u; uv.QueueWorkItemAndWait(() => { using (OpenGLState.ScopedCreateElementArrayBuffer(out buffer)) { gl.NamedBufferData(buffer, gl.GL_ELEMENT_ARRAY_BUFFER, size, null, usage); gl.ThrowIfError(); } }); this.buffer = buffer; }
/// <summary> /// Sets the data contained by the index buffer. /// </summary> /// <param name="data">An array containing the data to set in the index buffer.</param> public override void SetData <T>(T[] data) { Contract.Require(data, nameof(data)); Contract.Ensure(data.Length <= IndexCount, OpenGLStrings.DataTooLargeForBuffer); var handle = GCHandle.Alloc(data, GCHandleType.Pinned); try { using (OpenGLState.ScopedBindElementArrayBuffer(buffer)) { var size = new IntPtr(GetElementSize() * data.Length); gl.NamedBufferSubData(buffer, gl.GL_ELEMENT_ARRAY_BUFFER, IntPtr.Zero, size, handle.AddrOfPinnedObject().ToPointer()); gl.ThrowIfError(); } } finally { handle.Free(); } }
/// <summary> /// Gets the render target's data. /// </summary> /// <param name="data">An array to populate with the render target's data.</param> /// <param name="region">The region of the render target from which to retrieve data.</param> private unsafe void GetDataInternal(Color[] data, Rectangle region) { using (OpenGLState.ScopedBindFramebuffer(framebuffer, true)) { fixed(Color *pData = data) { if (!gl.IsGLES2) { gl.ReadBuffer(gl.GL_COLOR_ATTACHMENT0); gl.ThrowIfError(); } gl.ReadPixels(region.X, region.Y, region.Width, region.Height, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, pData); gl.ThrowIfError(); } } // OpenGL texture data is stored upside down and in the wrong color format, // so we need to convert that to what our caller expects. var rowsToProcess = (Height % 2 == 0) ? Height / 2 : 1 + Height / 2; for (int y = 0; y < rowsToProcess; y++) { var ySrc = (y); var yDst = (Height - 1) - y; for (int x = 0; x < Width; x++) { var ixSrc = (ySrc * Width) + x; var ixDst = (yDst * Width) + x; var colorSrc = new Color(data[ixSrc].PackedValue); var colorDst = new Color(data[ixDst].PackedValue); data[ixDst] = colorSrc; data[ixSrc] = colorDst; } } }
/// <summary> /// Initializes a new instance of the OpenGLVertexBuffer. /// </summary> /// <param name="uv">The Ultraviolet context.</param> /// <param name="vdecl">The vertex declaration for this buffer.</param> /// <param name="vcount">The number of vertices in the buffer.</param> /// <param name="usage">The buffer's usage type.</param> public OpenGLVertexBuffer(UltravioletContext uv, VertexDeclaration vdecl, Int32 vcount, UInt32 usage) : base(uv, vdecl, vcount) { Contract.Require(vdecl, nameof(vdecl)); this.vdecl = vdecl; this.usage = usage; this.size = new IntPtr(vdecl.VertexStride * vcount); var buffer = 0u; uv.QueueWorkItemAndWait(() => { using (OpenGLState.ScopedCreateArrayBuffer(out buffer)) { gl.NamedBufferData(buffer, gl.GL_ARRAY_BUFFER, size, null, usage); gl.ThrowIfError(); } }); this.buffer = buffer; }
/// <summary> /// Sets the data contained by the vertex buffer. /// </summary> private void SetDataInternal <T>(T[] data, Int32 offsetInBytes, Int32 countInBytes, SetDataOptions options) { var handle = GCHandle.Alloc(data, GCHandleType.Pinned); try { using (OpenGLState.ScopedBindArrayBuffer(buffer)) { var isPartialUpdate = (offsetInBytes > 0 || countInBytes < SizeInBytes); var isDiscarding = (options == SetDataOptions.Discard); if (isDiscarding || !isPartialUpdate) { gl.NamedBufferData(buffer, gl.GL_ARRAY_BUFFER, this.size, isPartialUpdate ? null : handle.AddrOfPinnedObject().ToPointer(), usage); gl.ThrowIfError(); /* FIX: * I have no idea why the following code is necessary, but * it seems to fix flickering sprites on Intel HD 4000 devices. */ if (OpenGLState.SupportsVertexArrayObjects) { var vao = (uint)OpenGLState.GL_VERTEX_ARRAY_BINDING; gl.BindVertexArray(vao); gl.ThrowIfError(); } } if (isPartialUpdate) { gl.NamedBufferSubData(buffer, gl.GL_ARRAY_BUFFER, (IntPtr)offsetInBytes, (IntPtr)countInBytes, handle.AddrOfPinnedObject().ToPointer()); gl.ThrowIfError(); } } } finally { handle.Free(); } }
/// <inheritdoc/> public override void SetDataAligned <T>(T[] data, Int32 dataOffset, Int32 dataCount, Int32 bufferOffset, out Int32 bufferSize, SetDataOptions options) { Contract.Require(data, nameof(data)); Contract.EnsureRange(dataCount > 0, nameof(dataCount)); Contract.EnsureRange(dataOffset >= 0 && dataOffset + dataCount <= data.Length, nameof(dataOffset)); Contract.EnsureRange(bufferOffset >= 0, nameof(bufferOffset)); Contract.Ensure(dataCount <= IndexCount, OpenGLStrings.DataTooLargeForBuffer); var indexStride = GetElementSize(); bufferSize = indexStride * dataCount; var handle = GCHandle.Alloc(data, GCHandleType.Pinned); try { var caps = (OpenGLGraphicsCapabilities)Ultraviolet.GetGraphics().Capabilities; if (caps.SupportsMapBufferRange && caps.MinMapBufferAlignment > 0) { bufferSize = Math.Min(Math.Max(caps.MinMapBufferAlignment, MathUtil.FindNextPowerOfTwo(bufferSize)), SizeInBytes - bufferOffset); } using (OpenGLState.ScopedBindElementArrayBuffer(buffer)) { var isPartialUpdate = (bufferOffset > 0 || bufferSize < SizeInBytes); var isDiscarding = (options == SetDataOptions.Discard); if (isDiscarding || !isPartialUpdate) { gl.NamedBufferData(buffer, gl.GL_ELEMENT_ARRAY_BUFFER, this.size, isPartialUpdate ? null : handle.AddrOfPinnedObject().ToPointer(), usage); gl.ThrowIfError(); } if (isPartialUpdate) { if (caps.SupportsMapBufferRange) { var bufferRangeAccess = gl.GL_MAP_WRITE_BIT | (options == SetDataOptions.NoOverwrite ? gl.GL_MAP_UNSYNCHRONIZED_BIT : 0); var bufferRangePtr = (Byte *)gl.MapNamedBufferRange(buffer, gl.GL_ELEMENT_ARRAY_BUFFER, (IntPtr)bufferOffset, (IntPtr)bufferSize, bufferRangeAccess); gl.ThrowIfError(); var sourceRangePtr = (Byte *)handle.AddrOfPinnedObject() + (dataOffset * indexStride); var sourceSizeInBytes = dataCount * indexStride; for (int i = 0; i < sourceSizeInBytes; i++) { *bufferRangePtr++ = *sourceRangePtr++; } gl.UnmapNamedBuffer(buffer, gl.GL_ELEMENT_ARRAY_BUFFER); gl.ThrowIfError(); } else { gl.NamedBufferSubData(buffer, gl.GL_ELEMENT_ARRAY_BUFFER, (IntPtr)bufferOffset, (IntPtr)bufferSize, (Byte *)handle.AddrOfPinnedObject().ToPointer() + (dataOffset * indexStride)); gl.ThrowIfError(); } } } } finally { handle.Free(); } }
/// <summary> /// Resets the OpenGL context to its values prior to this object's application. /// </summary> public void Dispose() { if (disposed) { return; } switch (stateType) { case OpenGLStateType.None: break; case OpenGLStateType.ActiveTexture: Dispose_ActiveTexture(); break; case OpenGLStateType.BindTexture2D: Dispose_BindTexture2D(); break; case OpenGLStateType.BindVertexArrayObject: Dispose_BindVertexArrayObject(); break; case OpenGLStateType.BindArrayBuffer: Dispose_BindArrayBuffer(); break; case OpenGLStateType.BindElementArrayBuffer: Dispose_BindElementArrayBuffer(); break; case OpenGLStateType.BindFramebuffer: Dispose_BindFramebuffer(); break; case OpenGLStateType.BindRenderbuffer: Dispose_BindRenderbuffer(); break; case OpenGLStateType.UseProgram: Dispose_UseProgram(); break; case OpenGLStateType.CreateTexture2D: Dispose_CreateTexture2D(); break; case OpenGLStateType.CreateArrayBuffer: Dispose_CreateArrayBuffer(); break; case OpenGLStateType.CreateElementArrayBuffer: Dispose_CreateElementArrayBuffer(); break; case OpenGLStateType.CreateFramebuffer: Dispose_CreateFramebuffer(); break; case OpenGLStateType.CreateRenderbuffer: Dispose_CreateRenderbuffer(); break; default: throw new InvalidOperationException(); } OpenGLState.VerifyCache(); disposed = true; pool.Release(this); }
/// <summary> /// Initializes a new instance of the <see cref="OpenGLGraphicsCapabilities"/> class. /// </summary> /// <param name="configuration">The configuration settings for the Ultraviolet context.</param> internal unsafe OpenGLGraphicsCapabilities(OpenGLUltravioletConfiguration configuration) { this.maximumTextureSize = gl.GetInteger(gl.GL_MAX_TEXTURE_SIZE); gl.ThrowIfError(); var viewportDims = stackalloc int[2]; gl.GetIntegerv(gl.GL_MAX_VIEWPORT_DIMS, viewportDims); gl.ThrowIfError(); this.maximumViewportWidth = viewportDims[0]; this.maximumViewportHeight = viewportDims[1]; this.supportsDepthStencilTextures = !gl.IsGLES2 || gl.IsExtensionSupported("GL_OES_packed_depth_stencil"); if (gl.IsGLES2 && !this.supportsDepthStencilTextures) { // HACK: Guess what? The Visual Studio Emulator for Android flat-out lies about this. // So it seems the only reliable way to determine support for depth/stencil is to // actually try to create one and see if it fails. I hate Android emulators. var rb = gl.GenRenderbuffer(); using (var state = OpenGLState.ScopedBindRenderbuffer(rb, true)) { gl.RenderbufferStorage(gl.GL_RENDERBUFFER, gl.GL_DEPTH24_STENCIL8, 32, 32); this.supportsDepthStencilTextures = (gl.GetError() == gl.GL_NO_ERROR); } gl.DeleteRenderBuffers(rb); } this.SupportsNonZeroBaseInstance = SupportsInstancedRendering && !gl.IsGLES && (gl.IsVersionAtLeast(4, 2) || gl.IsExtensionSupported("GL_ARB_base_instance")); this.SupportsIndependentSamplerState = (gl.IsGLES ? gl.IsVersionAtLeast(3, 0) : gl.IsVersionAtLeast(3, 3)) || gl.IsExtensionSupported("GL_ARB_sampler_objects"); this.SupportsIntegerVertexAttributes = !gl.IsGLES2 || gl.IsExtensionSupported("GL_EXT_gpu_shader4"); this.SupportsDoublePrecisionVertexAttributes = !gl.IsGLES && gl.IsVersionAtLeast(4, 1); this.SupportsMapBufferRange = true; if (gl.IsGLES2) { this.SupportsMapBufferRange = gl.IsExtensionSupported("GL_ARB_map_buffer_range") || gl.IsExtensionSupported("GL_EXT_map_buffer_range"); } this.MinMapBufferAlignment = gl.IsExtensionSupported("GL_ARB_map_buffer_alignment") ? gl.GetInteger(gl.GL_MIN_MAP_BUFFER_ALIGNMENT) : 0; // There seems to be a bug in the version of Mesa which is distributed // with Ubuntu 16.04 that causes long stalls when using glMapBufferRange. // Testing indicates that this is fixed in 11.2.2. var version = gl.GetString(gl.GL_VERSION); var versionMatchMesa = Regex.Match(version, "Mesa (?<major>\\d+).(?<minor>\\d+).(?<build>\\d+)"); if (versionMatchMesa != null && versionMatchMesa.Success) { var mesaMajor = Int32.Parse(versionMatchMesa.Groups["major"].Value); var mesaMinor = Int32.Parse(versionMatchMesa.Groups["minor"].Value); var mesaBuild = Int32.Parse(versionMatchMesa.Groups["build"].Value); var mesaVersion = new Version(mesaMajor, mesaMinor, mesaBuild); if (mesaVersion < new Version(11, 2, 2)) { configuration.UseBufferMapping = false; } } // If we've been explicitly told to disable buffer mapping, override the caps from the driver. if (!configuration.UseBufferMapping) { this.SupportsMapBufferRange = false; } }
/// <summary> /// Applies the sampler state to the device. /// </summary> /// <param name="sampler">The sampler index on which to set the state.</param> internal void Apply(Int32 sampler) { Contract.EnsureNotDisposed(this, Disposed); if (Ultraviolet.GetGraphics().Capabilities.SupportsIndependentSamplerState) { throw new InvalidOperationException(UltravioletStrings.GenericError); } OpenGLState.ActiveTexture((uint)(gl.GL_TEXTURE0 + sampler)); gl.TexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_S, GetTextureAddressModeGL(AddressU)); gl.ThrowIfError(); gl.TexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_T, GetTextureAddressModeGL(AddressV)); gl.ThrowIfError(); if (MipMapLevelOfDetailBias != 0) { gl.ThrowIfGLES(OpenGLStrings.UnsupportedLODBiasGLES); gl.TexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_LOD_BIAS, MipMapLevelOfDetailBias); gl.ThrowIfError(); } switch (Filter) { case TextureFilter.Point: gl.TexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAX_ANISOTROPY_EXT, 1f); gl.ThrowIfError(); gl.TexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, (int)gl.GL_NEAREST); gl.ThrowIfError(); gl.TexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, (int)gl.GL_NEAREST); gl.ThrowIfError(); break; case TextureFilter.Linear: if (gl.IsAnisotropicFilteringAvailable) { gl.TexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAX_ANISOTROPY_EXT, 1f); gl.ThrowIfError(); } gl.TexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, (int)gl.GL_LINEAR); gl.ThrowIfError(); gl.TexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, (int)gl.GL_LINEAR); gl.ThrowIfError(); break; case TextureFilter.Anisotropic: if (gl.IsAnisotropicFilteringAvailable) { gl.TexParameterf(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAX_ANISOTROPY_EXT, Math.Min(1f, MaxAnisotropy)); gl.ThrowIfError(); } gl.TexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, (int)gl.GL_LINEAR); gl.ThrowIfError(); gl.TexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, (int)gl.GL_LINEAR); gl.ThrowIfError(); break; default: throw new NotSupportedException(); } }
/// <summary> /// Sets the texture's data. /// </summary> /// <param name="level">The mipmap level for which to set data.</param> /// <param name="rect">A rectangle describing the position and size of the data to set.</param> /// <param name="data">A pointer to the data to set.</param> /// <param name="offset">The index of the first element to set.</param> /// <param name="count">The number of elements to set.</param> /// <param name="stride">The number of elements in one row of data.</param> /// <param name="format">The format of the data being set.</param> /// <param name="origin">A <see cref="SetDataOrigin"/> value specifying the origin of the texture data in <paramref name="data"/>.</param> private unsafe void SetDataInternal(Int32 level, Rectangle?rect, IntPtr data, Int32 offset, Int32 count, Int32 stride, TextureDataFormat format, SetDataOrigin origin) { var region = rect ?? new Rectangle(0, 0, width, height); if (region.Width * region.Height != count) { throw new InvalidOperationException(UltravioletStrings.BufferIsWrongSize); } const Int32 SizeOfTextureElementInBytes = 4; var flipHorizontally = (origin == SetDataOrigin.TopRight || origin == SetDataOrigin.BottomRight); var flipVertically = (origin == SetDataOrigin.TopLeft || origin == SetDataOrigin.TopRight); TextureUtil.ReorientTextureData(data.ToPointer(), region.Width, region.Height, SizeOfTextureElementInBytes, flipHorizontally, flipVertically); if (flipHorizontally) { region = new Rectangle((width - region.Width) - region.X, region.Y, region.Width, region.Height); } if (flipVertically) { region = new Rectangle(region.X, (height - region.Height) - region.Y, region.Width, region.Height); } using (OpenGLState.ScopedBindTexture2D(texture)) { var rowLengthSupported = (!gl.IsGLES2 || gl.IsExtensionSupported("GL_EXT_unpack_subimage")); var rowLengthFallbackRequired = (stride != 0 && !rowLengthSupported); if (rowLengthFallbackRequired) { var ptr = (Byte *)data.ToPointer(); for (int i = 0; i < height; i++) { gl.TextureSubImage2D(texture, gl.GL_TEXTURE_2D, level, region.X, region.Y + i, region.Width, 1, GetOpenGLTextureFormat(format), gl.GL_UNSIGNED_BYTE, ptr); ptr += stride * 4; } } else { if (rowLengthSupported) { gl.PixelStorei(gl.GL_UNPACK_ROW_LENGTH, stride); gl.ThrowIfError(); } gl.PixelStorei(gl.GL_UNPACK_ALIGNMENT, 1); gl.ThrowIfError(); gl.TextureSubImage2D(texture, gl.GL_TEXTURE_2D, level, region.X, region.Y, region.Width, region.Height, GetOpenGLTextureFormat(format), gl.GL_UNSIGNED_BYTE, data.ToPointer()); gl.ThrowIfError(); if (rowLengthSupported) { gl.PixelStorei(gl.GL_UNPACK_ROW_LENGTH, 0); gl.ThrowIfError(); } } } }
/// <summary> /// Creates the underlying native OpenGL texture with the specified format and data. /// </summary> /// <param name="uv">The Ultraviolet context.</param> /// <param name="internalformat">The texture's internal format.</param> /// <param name="width">The texture's width in pixels.</param> /// <param name="height">The texture's height in pixels.</param> /// <param name="format">The texel format.</param> /// <param name="type">The texel data type.</param> /// <param name="pixels">A pointer to the beginning of the texture's pixel data.</param> /// <param name="immutable">A value indicating whether to use immutable texture storage.</param> private void CreateNativeTexture(UltravioletContext uv, UInt32 internalformat, Int32 width, Int32 height, UInt32 format, UInt32 type, void *pixels, Boolean immutable) { if (uv.IsRunningInServiceMode) { throw new NotSupportedException(UltravioletStrings.NotSupportedInServiceMode); } this.width = width; this.height = height; this.internalformat = internalformat; this.format = format; this.type = type; this.immutable = immutable; this.texture = uv.QueueWorkItemAndWait(() => { uint glname; using (OpenGLState.ScopedCreateTexture2D(out glname)) { if (!gl.IsGLES2) { gl.TextureParameteri(glname, gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAX_LEVEL, 0); gl.ThrowIfError(); } gl.TextureParameteri(glname, gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, (int)gl.GL_LINEAR); gl.ThrowIfError(); gl.TextureParameteri(glname, gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, (int)gl.GL_LINEAR); gl.ThrowIfError(); gl.TextureParameteri(glname, gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_S, (int)gl.GL_CLAMP_TO_EDGE); gl.ThrowIfError(); gl.TextureParameteri(glname, gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_T, (int)gl.GL_CLAMP_TO_EDGE); gl.ThrowIfError(); if (immutable) { if (gl.IsTextureStorageAvailable) { gl.TextureStorage2D(glname, gl.GL_TEXTURE_2D, 1, internalformat, width, height); gl.ThrowIfError(); if (pixels != null) { gl.TextureSubImage2D(glname, gl.GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, pixels); gl.ThrowIfError(); } } else { gl.TextureImage2D(glname, gl.GL_TEXTURE_2D, 0, (int)internalformat, width, height, 0, format, type, pixels); gl.ThrowIfError(); } } } if (!immutable) { using (OpenGLState.ScopedBindTexture2D(glname, true)) { gl.TexImage2D(gl.GL_TEXTURE_2D, 0, (int)internalformat, width, height, 0, format, type, pixels); gl.ThrowIfError(); } } return(glname); }); }
/// <summary> /// Binds the specified buffer's vertex attributes to the currently cached program using the new API. /// </summary> private unsafe void BindVertexAttributesForBuffer_NewAPI(OpenGLVertexBuffer vbuffer, UInt32 binding, UInt32 frequency, UInt32?program, UInt32?offset) { var caps = Ultraviolet.GetGraphics().Capabilities; using (OpenGLState.ScopedBindVertexArrayObject(vao, glElementArrayBufferBinding ?? 0)) { if (program.HasValue || offset.HasValue) { gl.VertexArrayVertexBuffer(vao, binding, vbuffer.OpenGLName, (IntPtr)(offset ?? 0), vbuffer.VertexDeclaration.VertexStride); gl.ThrowIfError(); } if (program.HasValue) { gl.VertexArrayBindingDivisor(vao, binding, frequency); gl.ThrowIfError(); var position = 0u; foreach (var element in vbuffer.VertexDeclaration) { var name = GetVertexAttributeNameFromUsage(element.Usage, element.Index); var size = 0; var stride = 0; var normalize = false; var type = GetVertexFormatGL(element.Format, out size, out stride, out normalize); var category = OpenGLAttribCategory.Single; var location = (UInt32)OpenGLState.CurrentProgram.GetAttribLocation(name, out category); if (location >= 0) { gl.VertexArrayAttribBinding(vao, location, binding); gl.ThrowIfError(); gl.EnableVertexArrayAttrib(vao, location); gl.ThrowIfError(); unsafe { switch (category) { case OpenGLAttribCategory.Single: { gl.VertexArrayAttribFormat(vao, location, size, type, normalize, position); gl.ThrowIfError(); } break; case OpenGLAttribCategory.Double: { if (!caps.SupportsDoublePrecisionVertexAttributes) { throw new NotSupportedException(); } gl.VertexArrayAttribLFormat(vao, location, size, type, position); gl.ThrowIfError(); } break; case OpenGLAttribCategory.Integer: { if (!caps.SupportsIntegerVertexAttributes) { throw new NotSupportedException(); } gl.VertexArrayAttribIFormat(vao, location, size, type, position); gl.ThrowIfError(); } break; } } } position += (uint)stride; } } } }
/// <summary> /// Binds the specified buffer's vertex attributes to the currently cached program using the old API. /// </summary> private unsafe void BindVertexAttributesForBuffer_OldAPI(OpenGLVertexBuffer vbuffer, UInt32 binding, UInt32 frequency, UInt32?program, UInt32?offset) { OpenGLState.BindArrayBuffer(vbuffer.OpenGLName); var position = offset ?? this.offset; var caps = Ultraviolet.GetGraphics().Capabilities; foreach (var element in vbuffer.VertexDeclaration) { var name = GetVertexAttributeNameFromUsage(element.Usage, element.Index); var size = 0; var stride = 0; var normalize = false; var type = GetVertexFormatGL(element.Format, out size, out stride, out normalize); var category = OpenGLAttribCategory.Single; var location = (UInt32)OpenGLState.CurrentProgram.GetAttribLocation(name, out category); if (location >= 0) { if (program.HasValue) { if (gl.IsGLES2) { if (frequency != 0) { throw new NotSupportedException(); } } else { gl.VertexAttribDivisor(location, frequency); gl.ThrowIfError(); } gl.EnableVertexAttribArray(location); gl.ThrowIfError(); } switch (category) { case OpenGLAttribCategory.Single: { gl.VertexAttribPointer(location, size, type, normalize, vbuffer.VertexDeclaration.VertexStride, (void *)(position)); gl.ThrowIfError(); } break; case OpenGLAttribCategory.Double: { if (!caps.SupportsDoublePrecisionVertexAttributes) { throw new NotSupportedException(); } gl.VertexAttribLPointer(location, size, type, vbuffer.VertexDeclaration.VertexStride, (void *)(position)); gl.ThrowIfError(); } break; case OpenGLAttribCategory.Integer: { if (!caps.SupportsIntegerVertexAttributes) { throw new NotSupportedException(); } gl.VertexAttribIPointer(location, size, type, vbuffer.VertexDeclaration.VertexStride, (void *)(position)); gl.ThrowIfError(); } break; } } position += (uint)stride; } }