/// <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 texture's data from managed memory. /// </summary> private unsafe void SetDataInternal <T>(Int32 level, Rectangle?rect, T[] data, Int32 startIndex, Int32 elementCount) where T : struct { var elementSizeInBytes = Marshal.SizeOf(typeof(T)); var region = rect ?? new Rectangle(0, 0, width, height); var regionWidth = region.Width; var regionHeight = region.Height; var pixelSizeInBytes = (format == gl.GL_RGB || format == gl.GL_BGR) ? 3 : 4; if (pixelSizeInBytes * width * height != elementSizeInBytes * elementCount) { throw new ArgumentException(UltravioletStrings.BufferIsWrongSize); } Ultraviolet.QueueWorkItem(state => { var dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned); try { using (OpenGLState.ScopedBindTexture2D(OpenGLName)) { var pData = dataHandle.AddrOfPinnedObject() + (startIndex * elementSizeInBytes); gl.TextureSubImage2D(OpenGLName, gl.GL_TEXTURE_2D, level, region.X, region.Y, region.Width, region.Height, format, type, (void *)pData); gl.ThrowIfError(); } } finally { dataHandle.Free(); } }, null, WorkItemOptions.ReturnNullOnSynchronousExecution)?.Wait(); }
/// <inheritdoc/> protected override void Dispose(Boolean disposing) { if (Disposed) { return; } if (disposing) { if (!Ultraviolet.Disposed) { var glname = vao; if (glname != 0) { Ultraviolet.QueueWorkItem((state) => { gl.DeleteVertexArray(glname); gl.ThrowIfError(); OpenGLState.DeleteVertexArrayObject(glname, glElementArrayBufferBinding ?? 0); }, null, WorkItemOptions.ReturnNullOnSynchronousExecution); } vao = 0; } vbuffers.Clear(); ibuffer = null; } base.Dispose(disposing); }
/// <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 oglBuffer = (OpenGLRenderBuffer2D)buffer; Ultraviolet.QueueWorkItem(state => { using (OpenGLState.ScopedBindFramebuffer(framebuffer)) { AttachRenderBuffer(oglBuffer); framebufferStatus = gl.CheckNamedFramebufferStatus(framebuffer, gl.GL_FRAMEBUFFER); gl.ThrowIfError(); } }).Wait(); oglBuffer.MarkAttached(); buffers.Add(oglBuffer); }
/// <summary> /// Sets the current render target. /// </summary> private void SetRenderTargetInternal(RenderTarget2D renderTarget, Color?clearColor = null, Double?clearDepth = null, Int32?clearStencil = null) { Ultraviolet.ValidateResource(renderTarget); var usage = renderTarget?.RenderTargetUsage ?? backBufferRenderTargetUsage; if (usage == RenderTargetUsage.PlatformContents) { usage = Capabilities.SupportsPreservingRenderTargetContentInHardware ? RenderTargetUsage.PreserveContents : RenderTargetUsage.DiscardContents; } var oglRenderTarget = (OpenGLRenderTarget2D)renderTarget; if (oglRenderTarget != this.renderTarget) { var targetName = gl.DefaultFramebuffer; var targetSize = Size2.Zero; if (oglRenderTarget != null) { oglRenderTarget.ValidateStatus(); targetName = oglRenderTarget.OpenGLName; targetSize = renderTarget.Size; } else { var currentWindow = Ultraviolet.GetPlatform().Windows.GetCurrent(); if (currentWindow != null) { targetSize = currentWindow.DrawableSize; } } OpenGLState.BindFramebuffer(targetName); if (this.renderTarget != null) { this.renderTarget.UnbindWrite(); } this.renderTarget = oglRenderTarget; if (this.renderTarget != null) { this.renderTarget.BindWrite(); } this.viewport = default(Viewport); SetViewport(new Viewport(0, 0, targetSize.Width, targetSize.Height)); if (usage == RenderTargetUsage.DiscardContents) { Clear(clearColor ?? Color.FromArgb(0xFF442288), clearDepth ?? 1.0, clearStencil ?? 0); } } }
/// <summary> /// Sets the texture's data. /// </summary> private void SetDataInternal <T>(Int32 level, Int32 left, Int32 top, Int32 right, Int32 bottom, Int32 front, Int32 back, T[] data, Int32 startIndex, Int32 elementCount) where T : struct { var elementSizeInBytes = Marshal.SizeOf(typeof(T)); var width = right - left; var height = bottom - top; var depth = back - front; var pixelSizeInBytes = (format == gl.GL_RGB || format == gl.GL_BGR) ? 3 : 4; if (pixelSizeInBytes * width * height * depth != elementSizeInBytes * elementCount) { throw new ArgumentException(UltravioletStrings.BufferIsWrongSize); } Ultraviolet.QueueWorkItem(state => { var dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned); try { using (OpenGLState.ScopedBindTexture3D(OpenGLName)) { var pData = dataHandle.AddrOfPinnedObject() + (startIndex * elementSizeInBytes); gl.TextureSubImage3D(OpenGLName, gl.GL_TEXTURE_3D, level, left, top, front, right - left, bottom - top, back - front, format, type, (void *)pData); gl.ThrowIfError(); } } finally { dataHandle.Free(); } }).Wait(); }
/// <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)); var framebuffer = 0u; uv.QueueWorkItem(state => { using (OpenGLState.ScopedCreateFramebuffer(out framebuffer)) { if (buffers != null) { foreach (OpenGLRenderBuffer2D buffer in buffers) { AttachRenderBuffer(buffer); } } } }).Wait(); this.width = width; this.height = height; this.framebuffer = framebuffer; this.renderTargetUsage = usage; }
/// <summary> /// Uploads buffer data to the graphics device. /// </summary> private void Upload(IntPtr data, Int32 offsetInBytes, Int32 countInBytes, SetDataOptions options) { 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 : (void *)data, 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 (gl.IsVertexArrayObjectAvailable) { 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, (void *)data); gl.ThrowIfError(); } } }
/// <summary> /// Processes a resize operation for this texture. /// </summary> private void ProcessResize() { using (OpenGLState.ScopedBindTexture3D(texture, true)) { gl.TexImage3D(gl.GL_TEXTURE_3D, 0, (int)internalformat, width, height, depth, 0, format, type, null); } }
/// <summary> /// Uploads texture data to the graphics device. /// </summary> private void Upload(Int32 level, Int32 left, Int32 top, Int32 right, Int32 bottom, Int32 front, Int32 back, IntPtr data, Int32 offsetInBytes, Int32 sizeInBytes) { using (OpenGLState.ScopedBindTexture3D(OpenGLName)) { var pData = data + offsetInBytes; gl.TextureSubImage3D(OpenGLName, gl.GL_TEXTURE_3D, level, left, top, front, right - left, bottom - top, back - front, format, type, (void *)pData); gl.ThrowIfError(); } }
/// <summary> /// Uploads texture data to the graphics device. /// </summary> private void Upload(Int32 level, Rectangle region, IntPtr ldata, Int32 offsetInBytes) { using (OpenGLState.ScopedBindTexture2D(OpenGLName)) { var pData = ldata + offsetInBytes; gl.TextureSubImage2D(OpenGLName, gl.GL_TEXTURE_2D, level, region.X, region.Y, region.Width, region.Height, format, type, (void *)pData); gl.ThrowIfError(); } }
/// <inheritdoc/> public override void Apply() { var program = programs[programIndex]; OpenGLState.UseProgram(program); foreach (var uniform in program.Uniforms) { uniform.Apply(); } }
/// <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); } } }
/// <inheritdoc/> public void SetTexture(Int32 sampler, Texture2D texture) { Contract.EnsureRange(sampler >= 0 && sampler < maxTextureStages, nameof(sampler)); Contract.EnsureNotDisposed(this, Disposed); Ultraviolet.ValidateResource(texture); if (texture != null && texture.BoundForWriting) { throw new InvalidOperationException(OpenGLStrings.RenderBufferCannotBeUsedAsTexture); } if (texture != null && texture.WillNotBeSampled) { throw new InvalidOperationException(OpenGLStrings.RenderBufferWillNotBeSampled); } if (this.textures[sampler] != texture) { var textureName = (texture == null) ? 0 : ((IOpenGLResource)texture).OpenGLName; OpenGLState.ActiveTexture((uint)(gl.GL_TEXTURE0 + sampler)); OpenGLState.Texture2D(textureName); if (this.textures[sampler] != null) { ((IBindableResource)this.textures[sampler]).UnbindRead(); } this.textures[sampler] = texture; if (this.textures[sampler] != null) { ((IBindableResource)this.textures[sampler]).BindRead(); } if (!capabilities.SupportsIndependentSamplerState) { var samplerState = (OpenGLSamplerState)(GetSamplerState(sampler) ?? SamplerState.LinearClamp); for (int i = 0; i < samplerStates.Length; i++) { if (this.textures[i] == texture) { samplerState.Apply(sampler); } } } } }
/// <summary> /// Applies the geometry stream to the graphics device. /// </summary> public void Apply() { Contract.EnsureNotDisposed(this, Disposed); if (IsUsingVertexArrayObject) { OpenGLState.BindVertexArrayObject(vao, glElementArrayBufferBinding ?? 0); } else { if (ibuffer != null) { OpenGLState.BindElementArrayBuffer(ibuffer.OpenGLName); } } }
/// <inheritdoc/> public override void Apply() { if (effectImplementation == null) { throw new InvalidOperationException(OpenGLStrings.EffectPassNotAssociatedWithEffect); } effectImplementation.OnApply(); var program = Programs[programIndex]; OpenGLState.UseProgram(program); foreach (var uniform in program.Uniforms) { uniform.Apply(); } }
/// <summary> /// Uploads buffer data to the graphics device. /// </summary> private void Upload(IntPtr data, Int32 offsetInBytes, Int32 countInBytes, SetDataOptions options) { 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 : (void *)data, usage); gl.ThrowIfError(); } if (isPartialUpdate) { gl.NamedBufferSubData(buffer, gl.GL_ELEMENT_ARRAY_BUFFER, (IntPtr)offsetInBytes, (IntPtr)countInBytes, (void *)data); gl.ThrowIfError(); } } }
/// <inheritdoc/> public void SetGeometryStream(GeometryStream stream) { Contract.EnsureNotDisposed(this, Disposed); Ultraviolet.ValidateResource(stream); if (stream == null) { this.geometryStream = null; OpenGLState.BindVertexArrayObject(0, 0); } else { if (this.geometryStream != stream) { this.geometryStream = (OpenGLGeometryStream)stream; this.geometryStream.Apply(); } } }
/// <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.QueueWorkItem(state => { using (OpenGLState.ScopedCreateElementArrayBuffer(out buffer)) { gl.NamedBufferData(buffer, gl.GL_ELEMENT_ARRAY_BUFFER, size, null, usage); gl.ThrowIfError(); } }).Wait(); this.buffer = buffer; }
/// <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.IsReadBufferAvailable) { 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.QueueWorkItem(state => { using (OpenGLState.ScopedCreateArrayBuffer(out buffer)) { gl.NamedBufferData(buffer, gl.GL_ARRAY_BUFFER, size, null, usage); gl.ThrowIfError(); } }).Wait(); this.buffer = buffer; }
/// <summary> /// Uploads texture data to the graphics device. /// </summary> private unsafe Task Upload(Int32 level, Rectangle region, IntPtr ldata, Int32 startIndex, Int32 elementSizeInBytes) { if (Ultraviolet.IsExecutingOnCurrentThread) { using (OpenGLState.ScopedBindTexture2D(OpenGLName)) { var pData = ldata + (startIndex * elementSizeInBytes); gl.TextureSubImage2D(OpenGLName, gl.GL_TEXTURE_2D, level, region.X, region.Y, region.Width, region.Height, format, type, (void *)pData); gl.ThrowIfError(); } return(null); } else { return(Ultraviolet.QueueWorkItem(state => { Upload(level, region, ldata, startIndex, elementSizeInBytes); })); } }
/// <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 (gl.IsVertexArrayObjectAvailable) { 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(); } }
/// <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: { var internalformat = OpenGLTextureUtil.GetInternalFormatFromBytesPerPixel(4, SrgbEncoded); gl.RenderbufferStorage(gl.GL_RENDERBUFFER, internalformat, 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"); } } }
/// <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 oglIndexBuffer = (OpenGLIndexBuffer)ibuffer; var oglIndexBufferName = oglIndexBuffer.OpenGLName; this.ibuffer = oglIndexBuffer; if (IsUsingVertexArrayObject) { using (OpenGLState.ScopedBindVertexArrayObject(vao, 0, force: true)) { OpenGLState.BindElementArrayBuffer(oglIndexBufferName); } } this.glElementArrayBufferBinding = oglIndexBufferName; this.indexBufferElementType = ibuffer.IndexElementType; }
/// <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.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.MinMapBufferAlignment >= 0) { 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> /// Initializes a new instance of the OpenGLUltravioletGraphics class. /// </summary> /// <param name="uv">The Ultraviolet context.</param> /// <param name="configuration">The Ultraviolet Framework configuration settings for the current context.</param> /// <param name="versionRequested">The OpenGL context version which is required by the application.</param> public unsafe OpenGLUltravioletGraphics(OpenGLUltravioletContext uv, OpenGLUltravioletConfiguration configuration, Version versionRequested) : base(uv) { var masterptr = ((SDL2UltravioletWindowInfo)uv.GetPlatform().Windows).GetMasterPointer(); if (!TryInitializeGLContext(masterptr, configuration)) { var attemptedVersionMajor = 0; var attemptedVersionMinor = 0; if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &attemptedVersionMajor) < 0) { throw new SDL2Exception(); } if (SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &attemptedVersionMinor) < 0) { throw new SDL2Exception(); } var attemptedVersion = new Version(attemptedVersionMajor, attemptedVersionMinor, 0, 0); var isGLES = (uv.Platform == UltravioletPlatform.Android || uv.Platform == UltravioletPlatform.iOS); if (isGLES && attemptedVersion >= new Version(3, 0) && (configuration.MinimumOpenGLESVersion ?? new Version(2, 0)) <= new Version(2, 0)) { if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2) < 0) { throw new SDL2Exception(); } if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0) < 0) { throw new SDL2Exception(); } if (!TryInitializeGLContext(masterptr, configuration)) { throw new SDL2Exception(); } } else { throw new SDL2Exception(); } } if (SDL_GL_SetSwapInterval(1) < 0 && uv.Platform != UltravioletPlatform.iOS) { throw new SDL2Exception(); } if (gl.Initialized) { gl.Uninitialize(); } gl.Initialize(new OpenGLInitializer()); if (!gl.IsVersionAtLeast(versionRequested)) { throw new InvalidOperationException(OpenGLStrings.DoesNotMeetMinimumVersionRequirement.Format(gl.MajorVersion, gl.MinorVersion, versionRequested.Major, versionRequested.Minor)); } OpenGLState.ResetCache(); if (!VerifyCapabilities()) { throw new NotSupportedException(OpenGLStrings.UnsupportedGraphicsDevice); } if (configuration.Debug && configuration.DebugCallback != null) { InitializeDebugOutput(configuration); } this.capabilities = new OpenGLGraphicsCapabilities(configuration); if (capabilities.SrgbEncodingEnabled && gl.IsFramebufferSrgbAvailable) { gl.Enable(gl.GL_FRAMEBUFFER_SRGB); gl.ThrowIfError(); } this.maxTextureStages = gl.GetInteger(gl.GL_MAX_TEXTURE_IMAGE_UNITS); this.textures = new Texture[maxTextureStages]; this.samplerStates = new SamplerState[maxTextureStages]; this.samplerObjects = capabilities.SupportsIndependentSamplerState ? new OpenGLSamplerObject[maxTextureStages] : null; this.backBufferRenderTargetUsage = configuration.BackBufferRenderTargetUsage; if (samplerObjects != null) { for (int i = 0; i < samplerObjects.Length; i++) { samplerObjects[i] = new OpenGLSamplerObject(Ultraviolet); samplerObjects[i].Bind((uint)i); } } OpenGLState.VerifyCache(); }
/// <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) { 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 (!gl.IsDoublePrecisionVertexAttribAvailable) { throw new NotSupportedException(OpenGLStrings.DoublePrecisionVAttribsNotSupported); } gl.VertexArrayAttribLFormat(vao, location, size, type, position); gl.ThrowIfError(); } break; case OpenGLAttribCategory.Integer: { if (!gl.IsIntegerVertexAttribAvailable) { throw new NotSupportedException(OpenGLStrings.IntegerVAttribsNotSupported); } 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; 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.IsInstancedRenderingAvailable) { gl.VertexAttribDivisor(location, frequency); gl.ThrowIfError(); } else { if (frequency != 0) { throw new NotSupportedException(OpenGLStrings.InstancedRenderingNotSupported); } } 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 (!gl.IsDoublePrecisionVertexAttribAvailable) { throw new NotSupportedException(OpenGLStrings.DoublePrecisionVAttribsNotSupported); } gl.VertexAttribLPointer(location, size, type, vbuffer.VertexDeclaration.VertexStride, (void *)(position)); gl.ThrowIfError(); } break; case OpenGLAttribCategory.Integer: { if (!gl.IsIntegerVertexAttribAvailable) { throw new NotSupportedException(OpenGLStrings.IntegerVAttribsNotSupported); } gl.VertexAttribIPointer(location, size, type, vbuffer.VertexDeclaration.VertexStride, (void *)(position)); gl.ThrowIfError(); } break; } } position += (uint)stride; } }
/// <summary> /// Creates the underlying native OpenGL texture with the specified format and data. /// </summary> private void CreateNativeTexture(UltravioletContext uv, UInt32 internalformat, Int32 width, Int32 height, Int32 depth, UInt32 format, UInt32 type, void *pixels, Boolean immutable) { if (uv.IsRunningInServiceMode) { throw new NotSupportedException(UltravioletStrings.NotSupportedInServiceMode); } this.width = width; this.height = height; this.depth = depth; this.internalformat = internalformat; this.format = format; this.type = type; this.immutable = immutable; this.srgbEncoded = internalformat == gl.GL_SRGB || internalformat == gl.GL_SRGB_ALPHA || internalformat == gl.GL_SRGB8 || internalformat == gl.GL_SRGB8_ALPHA8; this.texture = uv.QueueWorkItem(state => { uint glname; using (OpenGLState.ScopedCreateTexture3D(out glname)) { if (gl.IsTextureMaxLevelSupported) { gl.TextureParameteri(glname, gl.GL_TEXTURE_3D, gl.GL_TEXTURE_MAX_LEVEL, 0); gl.ThrowIfError(); } gl.TextureParameteri(glname, gl.GL_TEXTURE_3D, gl.GL_TEXTURE_MIN_FILTER, (int)gl.GL_LINEAR); gl.ThrowIfError(); gl.TextureParameteri(glname, gl.GL_TEXTURE_3D, gl.GL_TEXTURE_MAG_FILTER, (int)gl.GL_LINEAR); gl.ThrowIfError(); gl.TextureParameteri(glname, gl.GL_TEXTURE_3D, gl.GL_TEXTURE_WRAP_R, (int)gl.GL_CLAMP_TO_EDGE); gl.ThrowIfError(); gl.TextureParameteri(glname, gl.GL_TEXTURE_3D, gl.GL_TEXTURE_WRAP_S, (int)gl.GL_CLAMP_TO_EDGE); gl.ThrowIfError(); gl.TextureParameteri(glname, gl.GL_TEXTURE_3D, gl.GL_TEXTURE_WRAP_T, (int)gl.GL_CLAMP_TO_EDGE); gl.ThrowIfError(); if (immutable) { if (gl.IsTextureStorageAvailable) { gl.TextureStorage3D(glname, gl.GL_TEXTURE_3D, 1, internalformat, width, height, depth); gl.ThrowIfError(); if (pixels != null) { gl.TextureSubImage3D(glname, gl.GL_TEXTURE_3D, 0, 0, 0, 0, width, height, depth, format, type, pixels); gl.ThrowIfError(); } } else { gl.TextureImage3D(glname, gl.GL_TEXTURE_3D, 0, (int)internalformat, width, height, depth, 0, format, type, pixels); gl.ThrowIfError(); } } } if (!immutable) { using (OpenGLState.ScopedBindTexture3D(glname, true)) { gl.TexImage3D(gl.GL_TEXTURE_3D, 0, (int)internalformat, width, height, depth, 0, format, type, pixels); gl.ThrowIfError(); } } return(glname); }).Result; }