private ClydeTexture GenTexture(GLHandle glHandle, Vector2i size, string?name, long memoryPressure = 0) { if (name != null) { ObjectLabelMaybe(ObjectLabelIdentifier.Texture, glHandle, name); } var(width, height) = size; var id = AllocRid(); var instance = new ClydeTexture(id, size, this); var loaded = new LoadedTexture { OpenGLObject = glHandle, Width = width, Height = height, Name = name, MemoryPressure = memoryPressure // TextureInstance = new WeakReference<ClydeTexture>(instance) }; _loadedTextures.Add(id, loaded); //GC.AddMemoryPressure(memoryPressure); return(instance); }
private unsafe void CreateMiscGLObjects() { // Quad drawing. { var quadVertices = new[] { new Vertex2D(1, 0, 1, 1), new Vertex2D(0, 0, 0, 1), new Vertex2D(1, 1, 1, 0), new Vertex2D(0, 1, 0, 0) }; QuadVBO = new GLBuffer <Vertex2D>(this, BufferTarget.ArrayBuffer, BufferUsageHint.StaticDraw, quadVertices, nameof(QuadVBO)); QuadVAO = new GLHandle((uint)GL.GenVertexArray()); GL.BindVertexArray(QuadVAO.Handle); ObjectLabelMaybe(ObjectLabelIdentifier.VertexArray, QuadVAO, nameof(QuadVAO)); // Vertex Coords GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 0); GL.EnableVertexAttribArray(0); // Texture Coords. GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 2 * sizeof(float)); GL.EnableVertexAttribArray(1); } // Batch rendering { BatchVBO = new GLBuffer(this, BufferTarget.ArrayBuffer, BufferUsageHint.DynamicDraw, Vertex2D.SizeOf * BatchVertexData.Length, nameof(BatchVBO)); BatchVAO = new GLHandle(GL.GenVertexArray()); GL.BindVertexArray(BatchVAO.Handle); ObjectLabelMaybe(ObjectLabelIdentifier.VertexArray, BatchVAO, nameof(BatchVAO)); // Vertex Coords GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 0); GL.EnableVertexAttribArray(0); // Texture Coords. GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 2 * sizeof(float)); GL.EnableVertexAttribArray(1); BatchEBO = new GLBuffer(this, BufferTarget.ElementArrayBuffer, BufferUsageHint.DynamicDraw, sizeof(ushort) * BatchIndexData.Length, nameof(BatchEBO)); } ProjViewUBO = new GLBuffer(this, BufferTarget.UniformBuffer, BufferUsageHint.StreamDraw, nameof(ProjViewUBO)); ProjViewUBO.Reallocate(sizeof(ProjViewMatrices)); GL.BindBufferBase(BufferRangeTarget.UniformBuffer, ProjViewBindingIndex, ProjViewUBO.ObjectHandle); UniformConstantsUBO = new GLBuffer(this, BufferTarget.UniformBuffer, BufferUsageHint.StreamDraw, nameof(UniformConstantsUBO)); UniformConstantsUBO.Reallocate(sizeof(UniformConstants)); GL.BindBufferBase(BufferRangeTarget.UniformBuffer, UniformConstantsBindingIndex, UniformConstantsUBO.ObjectHandle); EntityPostRenderTarget = CreateRenderTarget(Vector2i.One * 4 * EyeManager.PIXELSPERMETER, RenderTargetColorFormat.Rgba8Srgb, name: nameof(EntityPostRenderTarget), hasStencilBuffer: true); }
internal int GetShaderHandle() { // If the shader has already been created then return it. if (!_handle.IsNull) { return(_handle); } var shaderType = Stage == ShaderStage.Vertex ? ShaderType.VertexShader : ShaderType.FragmentShader; var shader = GL.CreateShader(shaderType); GL.CheckError(); GL.ShaderSource(shader, GlslCode); GL.CheckError(); GL.CompileShader(shader); GL.CheckError(); GL.GetShader(shader, ShaderParameter.CompileStatus, out int compiled); GL.CheckError(); _handle = GLHandle.Shader(shader); if (compiled == (int)Bool.True) { return(_handle); } var log = GL.GetShaderInfoLog(shader); Debug.WriteLine(log); _handle.Free(); throw new InvalidOperationException("Shader compilation failed."); }
private void PlatformConstruct() { GL.GenQueries(1, out int query); GL.CheckError(); _glQuery = GLHandle.Query(query); }
private unsafe void InitLighting() { LoadLightingShaders(); RegenerateLightingRenderTargets(); // Occlusion VAO. // Only handles positions, no other vertex data necessary. _occlusionVao = new GLHandle(GL.GenVertexArray()); GL.BindVertexArray(_occlusionVao.Handle); ObjectLabelMaybe(ObjectLabelIdentifier.VertexArray, _occlusionVao, nameof(_occlusionVao)); _occlusionVbo = new GLBuffer(this, BufferTarget.ArrayBuffer, BufferUsageHint.DynamicDraw, nameof(_occlusionVbo)); _occlusionEbo = new GLBuffer(this, BufferTarget.ElementArrayBuffer, BufferUsageHint.DynamicDraw, nameof(_occlusionEbo)); GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, sizeof(Vector3), IntPtr.Zero); GL.EnableVertexAttribArray(0); // FOV FBO. _fovRenderTarget = CreateRenderTarget((ShadowMapSize, 1), new RenderTargetFormatParameters(RenderTargetColorFormat.R32F, true), new TextureSampleParameters { WrapMode = TextureWrapMode.Repeat }, "FOV depth render target"); _fovDepthTextureObject = _fovRenderTarget.Texture; }
/// <summary> /// Creates a FrameBuffer that wraps a given Texture2D object. This Texture2D will not be disposed when the /// FrameBuffer itself is disposed, unlike other constructors. /// </summary> public FrameBuffer(RenderContext renderContext, Texture2D wrappedTexture) : base(renderContext) { Width = wrappedTexture._width; Height = wrappedTexture._height; Flags = FrameBufferFlags.Color; Handle = GLHelper.CreateFrameBuffer(); GLHelper.EnsureValid(Handle); GLHandle prevFrameBuffer = (GLHandle)GL.GetInteger(GetPName.FramebufferBinding); GL.BindFramebuffer(FramebufferTarget.Framebuffer, Handle); GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.Texture2D, wrappedTexture.Handle, 0); GL.BindFramebuffer(FramebufferTarget.Framebuffer, prevFrameBuffer); if (!IsComplete()) { throw new InvalidOperationException(); } }
private void PlatformGraphicsDeviceResetting() { if (!_handle.IsNull) { GraphicsDevice.DisposeResource(_handle); _handle = default; } }
/// <summary> /// Creates a FrameBuffer with components depending on the FrameBufferFlags given. /// </summary> public FrameBuffer(RenderContext renderContext, int width, int height, FrameBufferFlags flags) : base(renderContext) { Width = width; Height = height; Flags = flags; Handle = GLHelper.CreateFrameBuffer(); GLHelper.EnsureValid(Handle); // Initialize Color Component (Texture2D) if ((Flags & FrameBufferFlags.Color) != 0) { ColorTexture = Texture2D.CreateEmpty(renderContext, width, height); GLHandle prevFrameBuffer = (GLHandle)GL.GetInteger(GetPName.FramebufferBinding); GL.BindFramebuffer(FramebufferTarget.Framebuffer, Handle); GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.Texture2D, ColorTexture.Handle, 0); GL.BindFramebuffer(FramebufferTarget.Framebuffer, prevFrameBuffer); } // Initialize Depth Component (Renderbuffer) if ((Flags & FrameBufferFlags.Depth) != 0) { DepthHandle = (GLHandle)GL.GenRenderbuffer(); GLHandle prevFrameBuffer = (GLHandle)GL.GetInteger(GetPName.FramebufferBinding); GL.BindFramebuffer(FramebufferTarget.Framebuffer, Handle); GLHandle prevRenderBuffer = (GLHandle)GL.GetInteger(GetPName.RenderbufferBinding); GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, DepthHandle); GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.DepthComponent, width, height); GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthAttachment, RenderbufferTarget.Renderbuffer, DepthHandle); GL.BindFramebuffer(FramebufferTarget.Framebuffer, prevFrameBuffer); GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, prevRenderBuffer); } if (!IsComplete()) { throw new InvalidOperationException(); } }
public RenderTarget(Vector2i size, ClydeTexture texture, GLHandle objectHandle, Clyde clyde, ClydeHandle handle, GLHandle depthStencilBuffer) { Size = size; Texture = texture; ObjectHandle = objectHandle; _clyde = clyde; Handle = handle; DepthStencilBuffer = depthStencilBuffer; }
protected override void Dispose(bool disposing) { if (!IsDisposed && _handle.IsNull) { GraphicsDevice.DisposeResource(_handle); _handle = default; } base.Dispose(disposing); }
internal void DisposeResource(GLHandle handle) { if (IsDisposed || handle.IsNull) { return; } lock (ResourceFreeingLock) _resourceFreeQueue.Add(handle); }
private bool IsComplete() { GLHandle prevFBO = (GLHandle)GL.GetInteger(GetPName.FramebufferBinding); GL.BindFramebuffer(FramebufferTarget.Framebuffer, Handle); var status = GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer); GL.BindFramebuffer(FramebufferTarget.Framebuffer, prevFBO); return(status == FramebufferErrorCode.FramebufferComplete); }
private GLHandle MakeQuadVao() { var vao = new GLHandle(GenVertexArray()); BindVertexArray(vao.Handle); ObjectLabelMaybe(ObjectLabelIdentifier.VertexArray, vao, nameof(QuadVAO)); GL.BindBuffer(BufferTarget.ArrayBuffer, QuadVBO.ObjectHandle); // Vertex Coords GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 0); GL.EnableVertexAttribArray(0); // Texture Coords. GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 2 * sizeof(float)); GL.EnableVertexAttribArray(1); return(vao); }
private ShaderProgram Link(Shader vertexShader, Shader pixelShader) { // NOTE: No need to worry about background threads here // as this is only called at draw time when we're in the // main drawing thread. var program = GL.CreateProgram(); GL.CheckError(); GL.AttachShader(program, vertexShader.GetShaderHandle()); GL.CheckError(); GL.AttachShader(program, pixelShader.GetShaderHandle()); GL.CheckError(); //vertexShader.BindVertexAttributes(program); GL.LinkProgram(program); GL.CheckError(); GL.UseProgram(program); GL.CheckError(); vertexShader.GetVertexAttributeLocations(program); pixelShader.ApplySamplerTextureUnits(program); GL.GetProgram(program, GetProgramParameterName.LinkStatus, out int linked); GL.LogError("VertexShaderCache.Link(), GL.GetProgram"); var programHandle = GLHandle.Program(program); if (linked == (int)Bool.False) { var log = GL.GetProgramInfoLog(program); Debug.WriteLine(log); GL.DetachShader(program, vertexShader.GetShaderHandle()); GL.DetachShader(program, pixelShader.GetShaderHandle()); programHandle.Free(); throw new InvalidOperationException("Unable to link effect program"); } return(new ShaderProgram(programHandle)); }
internal void GenerateIfRequired() { if (!_glBuffer.IsNull) { return; } _glBuffer = GL.GenBuffer(); GL.CheckError(); GL.BindBuffer(_target, _glBuffer); GL.CheckError(); int sizeInBytes = Capacity * _elementSize; GL.BufferData(_target, (IntPtr)sizeInBytes, IntPtr.Zero, _usageHint); GL.CheckError(); }
private ClydeTexture GenTexture(GLHandle glHandle, Vector2i size, string name) { if (name != null) { ObjectLabelMaybe(ObjectLabelIdentifier.Texture, glHandle, name); } var(width, height) = size; var loaded = new LoadedTexture { OpenGLObject = glHandle, Width = width, Height = height, Name = name }; var id = AllocRid(); _loadedTextures.Add(id, loaded); return(new ClydeTexture(id, size, this)); }
private unsafe void InitLighting() { LoadLightingShaders(); { // Occlusion VAO. // Only handles positions, no other vertex data necessary. _occlusionVao = new GLHandle(GL.GenVertexArray()); GL.BindVertexArray(_occlusionVao.Handle); ObjectLabelMaybe(ObjectLabelIdentifier.VertexArray, _occlusionVao, nameof(_occlusionVao)); _occlusionVbo = new GLBuffer(this, BufferTarget.ArrayBuffer, BufferUsageHint.DynamicDraw, nameof(_occlusionVbo)); _occlusionEbo = new GLBuffer(this, BufferTarget.ElementArrayBuffer, BufferUsageHint.DynamicDraw, nameof(_occlusionEbo)); GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, sizeof(Vector3), IntPtr.Zero); GL.EnableVertexAttribArray(0); } { // Occlusion mask VAO. // Only handles positions, no other vertex data necessary. _occlusionMaskVao = new GLHandle(GL.GenVertexArray()); GL.BindVertexArray(_occlusionMaskVao.Handle); ObjectLabelMaybe(ObjectLabelIdentifier.VertexArray, _occlusionMaskVao, nameof(_occlusionMaskVao)); _occlusionMaskVbo = new GLBuffer(this, BufferTarget.ArrayBuffer, BufferUsageHint.DynamicDraw, nameof(_occlusionMaskVbo)); _occlusionMaskEbo = new GLBuffer(this, BufferTarget.ElementArrayBuffer, BufferUsageHint.DynamicDraw, nameof(_occlusionMaskEbo)); GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, sizeof(Vector2), IntPtr.Zero); GL.EnableVertexAttribArray(0); } // FOV FBO. _fovRenderTarget = CreateRenderTarget((FovMapSize, 2), new RenderTargetFormatParameters(RenderTargetColorFormat.RG32F, true), new TextureSampleParameters { WrapMode = TextureWrapMode.Repeat }, nameof(_fovRenderTarget)); _fovFilterSampler = new GLHandle(GL.GenSampler()); GL.SamplerParameter(_fovFilterSampler.Handle, SamplerParameterName.TextureMagFilter, (int)All.Linear); GL.SamplerParameter(_fovFilterSampler.Handle, SamplerParameterName.TextureMinFilter, (int)All.Linear); GL.SamplerParameter(_fovFilterSampler.Handle, SamplerParameterName.TextureWrapS, (int)All.Repeat); GL.SamplerParameter(_fovFilterSampler.Handle, SamplerParameterName.TextureWrapT, (int)All.Repeat); // Shadow FBO. _shadowRenderTarget = CreateRenderTarget((ShadowMapSize, MaxLightsPerScene), new RenderTargetFormatParameters(RenderTargetColorFormat.RG32F, true), new TextureSampleParameters { WrapMode = TextureWrapMode.Repeat, Filter = true }, nameof(_shadowRenderTarget)); }
public ShaderProgram(GLHandle program) { Program = program; }
private void ObjectLabelMaybe(ObjectLabelIdentifier identifier, GLHandle name, string?label) { ObjectLabelMaybe(identifier, name.Handle, label); }
private RenderTarget CreateRenderTarget(Vector2i size, RenderTargetFormatParameters format, TextureSampleParameters?sampleParameters = null, string name = null) { // Cache currently bound framebuffers // so if somebody creates a framebuffer while drawing it won't ruin everything. var boundDrawBuffer = GL.GetInteger(GetPName.DrawFramebufferBinding); var boundReadBuffer = GL.GetInteger(GetPName.ReadFramebufferBinding); // Generate FBO. var fbo = new GLHandle(GL.GenFramebuffer()); // Bind color attachment to FBO. GL.BindFramebuffer(FramebufferTarget.Framebuffer, fbo.Handle); ObjectLabelMaybe(ObjectLabelIdentifier.Framebuffer, fbo, name); var(width, height) = size; ClydeTexture textureObject; GLHandle depthStencilBuffer = default; // Color attachment. { var texture = new GLHandle(GL.GenTexture()); GL.BindTexture(TextureTarget.Texture2D, texture.Handle); ApplySampleParameters(sampleParameters); var internalFormat = format.ColorFormat switch { RenderTargetColorFormat.Rgba8 => PixelInternalFormat.Rgba8, RenderTargetColorFormat.Rgba16F => PixelInternalFormat.Rgba16f, RenderTargetColorFormat.Rgba8Srgb => PixelInternalFormat.Srgb8Alpha8, RenderTargetColorFormat.R11FG11FB10F => PixelInternalFormat.R11fG11fB10f, RenderTargetColorFormat.R32F => PixelInternalFormat.R32f, _ => throw new ArgumentOutOfRangeException(nameof(format.ColorFormat), format.ColorFormat, null) }; GL.TexImage2D(TextureTarget.Texture2D, 0, internalFormat, width, height, 0, PixelFormat.Red, PixelType.Byte, IntPtr.Zero); GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, texture.Handle, 0); textureObject = GenTexture(texture, size, name == null ? null : $"{name}-color"); } // Depth/stencil buffers. if (format.HasDepthStencil) { depthStencilBuffer = new GLHandle(GL.GenRenderbuffer()); GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, depthStencilBuffer.Handle); ObjectLabelMaybe(ObjectLabelIdentifier.Renderbuffer, depthStencilBuffer, name == null ? null : $"{name}-depth-stencil"); GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.Depth24Stencil8, width, height); GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthStencilAttachment, RenderbufferTarget.Renderbuffer, depthStencilBuffer.Handle); } // This should always pass but OpenGL makes it easy to check for once so let's. var status = GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer); DebugTools.Assert(status == FramebufferErrorCode.FramebufferComplete, $"new framebuffer has bad status {status}"); // Re-bind previous framebuffers. GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, boundDrawBuffer); GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, boundReadBuffer); var handle = AllocRid(); var renderTarget = new RenderTarget(size, textureObject, fbo, this, handle, depthStencilBuffer); _renderTargets.Add(handle, renderTarget); return(renderTarget); }
private unsafe void CreateMiscGLObjects() { // Quad drawing. { Span <Vertex2D> quadVertices = stackalloc[] { new Vertex2D(1, 0, 1, 1), new Vertex2D(0, 0, 0, 1), new Vertex2D(1, 1, 1, 0), new Vertex2D(0, 1, 0, 0) }; QuadVBO = new GLBuffer <Vertex2D>(this, BufferTarget.ArrayBuffer, BufferUsageHint.StaticDraw, quadVertices, nameof(QuadVBO)); QuadVAO = MakeQuadVao(); CheckGlError(); } // Window VBO { Span <Vertex2D> winVertices = stackalloc[] { new Vertex2D(-1, 1, 0, 1), new Vertex2D(-1, -1, 0, 0), new Vertex2D(1, 1, 1, 1), new Vertex2D(1, -1, 1, 0), }; WindowVBO = new GLBuffer <Vertex2D>( this, BufferTarget.ArrayBuffer, BufferUsageHint.StaticDraw, winVertices, nameof(WindowVBO)); CheckGlError(); } // Batch rendering { BatchVBO = new GLBuffer(this, BufferTarget.ArrayBuffer, BufferUsageHint.DynamicDraw, Vertex2D.SizeOf * BatchVertexData.Length, nameof(BatchVBO)); BatchVAO = new GLHandle(GenVertexArray()); BindVertexArray(BatchVAO.Handle); ObjectLabelMaybe(ObjectLabelIdentifier.VertexArray, BatchVAO, nameof(BatchVAO)); // Vertex Coords GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 0); GL.EnableVertexAttribArray(0); // Texture Coords. GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, Vertex2D.SizeOf, 2 * sizeof(float)); GL.EnableVertexAttribArray(1); CheckGlError(); BatchEBO = new GLBuffer(this, BufferTarget.ElementArrayBuffer, BufferUsageHint.DynamicDraw, sizeof(ushort) * BatchIndexData.Length, nameof(BatchEBO)); } ProjViewUBO = new GLUniformBuffer <ProjViewMatrices>(this, BindingIndexProjView, nameof(ProjViewUBO)); UniformConstantsUBO = new GLUniformBuffer <UniformConstants>(this, BindingIndexUniformConstants, nameof(UniformConstantsUBO)); screenBufferHandle = new GLHandle(GL.GenTexture()); GL.BindTexture(TextureTarget.Texture2D, screenBufferHandle.Handle); ApplySampleParameters(TextureSampleParameters.Default); ScreenBufferTexture = GenTexture(screenBufferHandle, _windowing !.MainWindow !.FramebufferSize, true, null, TexturePixelType.Rgba32); }
private void DeleteGLTexture() { GraphicsDevice.DisposeResource(_glTexture); _glTexture = default; }
private unsafe void InitLighting() { // Other... LoadLightingShaders(); { // Occlusion VAO. // Only handles positions, no other vertex data necessary. _occlusionVao = new GLHandle(GenVertexArray()); BindVertexArray(_occlusionVao.Handle); CheckGlError(); ObjectLabelMaybe(ObjectLabelIdentifier.VertexArray, _occlusionVao, nameof(_occlusionVao)); // aPos _occlusionVbo = new GLBuffer(this, BufferTarget.ArrayBuffer, BufferUsageHint.DynamicDraw, nameof(_occlusionVbo)); GL.VertexAttribPointer(0, 4, VertexAttribPointerType.Float, false, sizeof(Vector4), IntPtr.Zero); GL.EnableVertexAttribArray(0); CheckGlError(); // subVertex _occlusionVIVbo = new GLBuffer(this, BufferTarget.ArrayBuffer, BufferUsageHint.DynamicDraw, nameof(_occlusionVIVbo)); GL.VertexAttribPointer(1, 2, VertexAttribPointerType.UnsignedByte, true, sizeof(byte) * 2, IntPtr.Zero); GL.EnableVertexAttribArray(1); // index _occlusionEbo = new GLBuffer(this, BufferTarget.ElementArrayBuffer, BufferUsageHint.DynamicDraw, nameof(_occlusionEbo)); CheckGlError(); } { // Occlusion mask VAO. // Only handles positions, no other vertex data necessary. _occlusionMaskVao = new GLHandle(GenVertexArray()); BindVertexArray(_occlusionMaskVao.Handle); CheckGlError(); ObjectLabelMaybe(ObjectLabelIdentifier.VertexArray, _occlusionMaskVao, nameof(_occlusionMaskVao)); _occlusionMaskVbo = new GLBuffer(this, BufferTarget.ArrayBuffer, BufferUsageHint.DynamicDraw, nameof(_occlusionMaskVbo)); _occlusionMaskEbo = new GLBuffer(this, BufferTarget.ElementArrayBuffer, BufferUsageHint.DynamicDraw, nameof(_occlusionMaskEbo)); GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, sizeof(Vector2), IntPtr.Zero); GL.EnableVertexAttribArray(0); CheckGlError(); } // FOV FBO. _fovRenderTarget = CreateRenderTarget((FovMapSize, 2), new RenderTargetFormatParameters(_hasGLFloatFramebuffers ? RenderTargetColorFormat.RG32F : RenderTargetColorFormat.Rgba8, true), new TextureSampleParameters { WrapMode = TextureWrapMode.Repeat }, nameof(_fovRenderTarget)); if (_hasGLSamplerObjects) { _fovFilterSampler = new GLHandle(GL.GenSampler()); GL.SamplerParameter(_fovFilterSampler.Handle, SamplerParameterName.TextureMagFilter, (int)All.Linear); GL.SamplerParameter(_fovFilterSampler.Handle, SamplerParameterName.TextureMinFilter, (int)All.Linear); GL.SamplerParameter(_fovFilterSampler.Handle, SamplerParameterName.TextureWrapS, (int)All.Repeat); GL.SamplerParameter(_fovFilterSampler.Handle, SamplerParameterName.TextureWrapT, (int)All.Repeat); CheckGlError(); } // Shadow FBO. _shadowRenderTargetCanInitializeSafely = true; MaxLightsPerSceneChanged(_maxLightsPerScene); }
public Texture LoadTextureFromImage <T>(Image <T> image, string?name = null, TextureLoadParameters?loadParams = null) where T : unmanaged, IPixel <T> { DebugTools.Assert(_mainThread == Thread.CurrentThread); var actualParams = loadParams ?? TextureLoadParameters.Default; var pixelType = typeof(T); if (!_hasGLTextureSwizzle) { // If texture swizzle isn't available we have to pre-process the images to apply it ourselves // and then upload as RGBA8. // Yes this is inefficient but the alternative is modifying the shaders, // which I CBA to do. // Even 8 year old iGPUs support texture swizzle. if (pixelType == typeof(A8)) { // Disable sRGB so stuff doesn't get interpreter wrong. actualParams.Srgb = false; var img = ApplyA8Swizzle((Image <A8>)(object) image); return(LoadTextureFromImage(img, name, loadParams)); } if (pixelType == typeof(L8) && !actualParams.Srgb) { var img = ApplyL8Swizzle((Image <L8>)(object) image); return(LoadTextureFromImage(img, name, loadParams)); } } // Flip image because OpenGL reads images upside down. var copy = FlipClone(image); var texture = new GLHandle((uint)GL.GenTexture()); CheckGlError(); GL.BindTexture(TextureTarget.Texture2D, texture.Handle); CheckGlError(); ApplySampleParameters(actualParams.SampleParameters); PixelInternalFormat internalFormat; PixelFormat pixelDataFormat; PixelType pixelDataType; if (pixelType == typeof(Rgba32)) { internalFormat = actualParams.Srgb ? PixelInternalFormat.Srgb8Alpha8 : PixelInternalFormat.Rgba8; pixelDataFormat = PixelFormat.Rgba; pixelDataType = PixelType.UnsignedByte; } else if (pixelType == typeof(A8)) { if (image.Width % 4 != 0 || image.Height % 4 != 0) { throw new ArgumentException("Alpha8 images must have multiple of 4 sizes."); } internalFormat = PixelInternalFormat.R8; pixelDataFormat = PixelFormat.Red; pixelDataType = PixelType.UnsignedByte; unsafe { // TODO: Does it make sense to default to 1 for RGB parameters? // It might make more sense to pass some options to change swizzling. var swizzle = stackalloc[] { (int)All.One, (int)All.One, (int)All.One, (int)All.Red }; GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleRgba, swizzle); } } else if (pixelType == typeof(L8) && !actualParams.Srgb) { // Can only use R8 for L8 if sRGB is OFF. // Because OpenGL doesn't provide sRGB single/dual channel image formats. // Vulkan when? if (copy.Width % 4 != 0 || copy.Height % 4 != 0) { throw new ArgumentException("L8 non-sRGB images must have multiple of 4 sizes."); } internalFormat = PixelInternalFormat.R8; pixelDataFormat = PixelFormat.Red; pixelDataType = PixelType.UnsignedByte; unsafe { var swizzle = stackalloc[] { (int)All.Red, (int)All.Red, (int)All.Red, (int)All.One }; GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleRgba, swizzle); } } else { throw new NotImplementedException($"Unable to handle pixel type '{pixelType.Name}'"); } unsafe { var span = copy.GetPixelSpan(); fixed(T *ptr = span) { GL.TexImage2D(TextureTarget.Texture2D, 0, internalFormat, copy.Width, copy.Height, 0, pixelDataFormat, pixelDataType, (IntPtr)ptr); CheckGlError(); } } var pressureEst = EstPixelSize(internalFormat) * copy.Width * copy.Height; return(GenTexture(texture, (copy.Width, copy.Height), name, pressureEst)); }
/// <inheritdoc/> protected override void GraphicsDeviceResetting() { _glBuffer = default; }
private RenderTexture CreateRenderTarget(Vector2i size, RenderTargetFormatParameters format, TextureSampleParameters?sampleParameters = null, string?name = null) { DebugTools.Assert(size.X != 0); DebugTools.Assert(size.Y != 0); // Cache currently bound framebuffers // so if somebody creates a framebuffer while drawing it won't ruin everything. // Note that this means _currentBoundRenderTarget goes temporarily out of sync here var boundDrawBuffer = GL.GetInteger(GetPName.DrawFramebufferBinding); var boundReadBuffer = 0; if (_hasGLReadFramebuffer) { boundReadBuffer = GL.GetInteger(GetPName.ReadFramebufferBinding); } // Generate FBO. var fbo = new GLHandle(GL.GenFramebuffer()); // Bind color attachment to FBO. GL.BindFramebuffer(FramebufferTarget.Framebuffer, fbo.Handle); CheckGlError(); ObjectLabelMaybe(ObjectLabelIdentifier.Framebuffer, fbo, name); var(width, height) = size; ClydeTexture textureObject; GLHandle depthStencilBuffer = default; var estPixSize = 0L; // Color attachment. { var texture = new GLHandle(GL.GenTexture()); CheckGlError(); GL.BindTexture(TextureTarget.Texture2D, texture.Handle); CheckGlError(); ApplySampleParameters(sampleParameters); var colorFormat = format.ColorFormat; if ((!_hasGLSrgb) && (colorFormat == RTCF.Rgba8Srgb)) { // If SRGB is not supported, switch formats. // The shaders will have to compensate. // Note that a check is performed on the *original* format. colorFormat = RTCF.Rgba8; } // This isn't good if (!_hasGLFloatFramebuffers) { switch (colorFormat) { case RTCF.R32F: case RTCF.RG32F: case RTCF.R11FG11FB10F: case RTCF.Rgba16F: Logger.WarningS("clyde.ogl", "The framebuffer {0} [{1}] is trying to be floating-point when that's not supported. Forcing Rgba8.", name == null ? "[unnamed]" : name, size); colorFormat = RTCF.Rgba8; break; } } // Make sure to specify the correct pixel type and formats even if we're not uploading any data. // Not doing this (just sending red/byte) is fine on desktop GL but illegal on ES. var(internalFormat, pixFormat, pixType) = colorFormat switch { // using block comments to force formatters to not f**k this up. RTCF.Rgba8 => /* */ (PIF.Rgba8, /* */ PF.Rgba, /**/ PT.UnsignedByte), RTCF.Rgba16F => /* */ (PIF.Rgba16f, /* */ PF.Rgba, /**/ PT.Float), RTCF.Rgba8Srgb => /* */ (PIF.Srgb8Alpha8, /* */ PF.Rgba, /**/ PT.UnsignedByte), RTCF.R11FG11FB10F => /**/ (PIF.R11fG11fB10f, /**/ PF.Rgb, /* */ PT.Float), RTCF.R32F => /* */ (PIF.R32f, /* */ PF.Red, /* */ PT.Float), RTCF.RG32F => /* */ (PIF.Rg32f, /* */ PF.Rg, /* */ PT.Float), RTCF.R8 => /* */ (PIF.R8, /* */ PF.Red, /* */ PT.UnsignedByte), _ => throw new ArgumentOutOfRangeException(nameof(format.ColorFormat), format.ColorFormat, null) }; estPixSize += EstPixelSize(internalFormat); GL.TexImage2D(TextureTarget.Texture2D, 0, internalFormat, width, height, 0, pixFormat, pixType, IntPtr.Zero); CheckGlError(); if (!_isGLES) { GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, texture.Handle, 0); } else { // OpenGL ES uses a different name, and has an odd added target argument GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.Texture2D, texture.Handle, 0); } CheckGlError(); // Check on original format is NOT a bug, this is so srgb emulation works textureObject = GenTexture(texture, size, format.ColorFormat == RTCF.Rgba8Srgb, name == null ? null : $"{name}-color"); } // Depth/stencil buffers. if (format.HasDepthStencil) { depthStencilBuffer = new GLHandle(GL.GenRenderbuffer()); GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, depthStencilBuffer.Handle); CheckGlError(); ObjectLabelMaybe(ObjectLabelIdentifier.Renderbuffer, depthStencilBuffer, name == null ? null : $"{name}-depth-stencil"); GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.Depth24Stencil8, width, height); CheckGlError(); estPixSize += 4; GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthAttachment, RenderbufferTarget.Renderbuffer, depthStencilBuffer.Handle); GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferAttachment.StencilAttachment, RenderbufferTarget.Renderbuffer, depthStencilBuffer.Handle); CheckGlError(); } // This should always pass but OpenGL makes it easy to check for once so let's. var status = GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer); CheckGlError(); DebugTools.Assert(status == FramebufferErrorCode.FramebufferComplete, $"new framebuffer has bad status {status}"); // Re-bind previous framebuffers (thus _currentBoundRenderTarget is back in sync) GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, boundDrawBuffer); CheckGlError(); if (_hasGLReadFramebuffer) { GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, boundReadBuffer); CheckGlError(); } var pressure = estPixSize * size.X * size.Y; var handle = AllocRid(); var data = new LoadedRenderTarget { IsWindow = false, IsSrgb = textureObject.IsSrgb, DepthStencilHandle = depthStencilBuffer, FramebufferHandle = fbo, Size = size, TextureHandle = textureObject.TextureId, MemoryPressure = pressure }; //GC.AddMemoryPressure(pressure); var renderTarget = new RenderTexture(size, textureObject, this, handle); _renderTargets.Add(handle, data); return(renderTarget); }
public static unsafe Texture2D CreateFromStreamUnsynchronized(RenderContext renderContext, Stream stream) { using var image = Image.Load <Rgba32>(stream); image.Mutate(x => x.Flip(FlipMode.Vertical)); var texture = new Texture2D(renderContext) { Width = image.Width, Height = image.Height, }; var span = image.GetPixelSpan(); int size = span.Length * sizeof(Rgba32); fixed(void *data = &MemoryMarshal.GetReference(span)) { var start = new IntPtr(data); using var pixelBuffer = PixelBuffer.Create <Rgba32>(renderContext, size, true, true); GLHandle prevPixelBuffer = (GLHandle)GL.GetInteger(GetPName.PixelUnpackBufferBinding); GL.BindBuffer(BufferTarget.PixelUnpackBuffer, pixelBuffer.Handle); // When we load assets asynchronously, using SubData to set the data will cause the main thread // to wait for the GL call to complete, so we use a PBO and fill it with a mapped memory range to // prevent OpenGL synchronization from causing frame drops on the main thread. IntPtr pixelPointer = GL.MapBufferRange(BufferTarget.PixelUnpackBuffer, IntPtr.Zero, size, BufferAccessMask.MapWriteBit | BufferAccessMask.MapUnsynchronizedBit | BufferAccessMask.MapInvalidateRangeBit); if (pixelPointer == IntPtr.Zero) { throw new GraphicsContextException("Could not map PixelUnbackBuffer range."); } // We send the data to mapped memory in multiple batches instead of one large one for the same // reason. int remaining = size; const int step = 2048; while (remaining > 0) { int currentStep = Math.Min(remaining, step); int point = size - remaining; void *dest = (void *)IntPtr.Add(pixelPointer, point); void *src = (void *)IntPtr.Add(start, point); Unsafe.CopyBlock(dest, src, (uint)currentStep); remaining -= step; } GL.UnmapBuffer(BufferTarget.PixelUnpackBuffer); if (GLInfo.HasDirectStateAccess) { GL.TextureStorage2D(texture.Handle, 1, SizedInternalFormat.Rgba8, texture.Width, texture.Height); GL.TextureSubImage2D(texture.Handle, 0, 0, 0, texture.Width, texture.Height, PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero); GL.GenerateTextureMipmap(texture.Handle); } else { var previousTexture = renderContext.TextureUnits[0].Texture2D; renderContext.BindTexture(0, texture); GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, texture.Width, texture.Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero); GL.GenerateMipmap(GenerateMipmapTarget.Texture2D); renderContext.BindTexture(0, previousTexture); } GL.BindBuffer(BufferTarget.PixelUnpackBuffer, prevPixelBuffer); } texture.SetDefaultTextureParameters(renderContext); // TODO: Look into if this is necessary (?) GL.Finish(); return(texture); }
public Texture LoadTextureFromImage <T>(Image <T> image, string name = null, TextureLoadParameters?loadParams = null) where T : unmanaged, IPixel <T> { DebugTools.Assert(_mainThread == Thread.CurrentThread); var actualParams = loadParams ?? TextureLoadParameters.Default; var pixelType = typeof(T); // Flip image because OpenGL reads images upside down. var copy = FlipClone(image); var texture = new GLHandle((uint)GL.GenTexture()); GL.BindTexture(TextureTarget.Texture2D, texture.Handle); ApplySampleParameters(actualParams.SampleParameters); PixelInternalFormat internalFormat; PixelFormat pixelDataFormat; PixelType pixelDataType; if (pixelType == typeof(Rgba32)) { internalFormat = actualParams.Srgb ? PixelInternalFormat.Srgb8Alpha8 : PixelInternalFormat.Rgba8; pixelDataFormat = PixelFormat.Rgba; pixelDataType = PixelType.UnsignedByte; } else if (pixelType == typeof(Alpha8)) { if (image.Width % 4 != 0 || image.Height % 4 != 0) { throw new ArgumentException("Alpha8 images must have multiple of 4 sizes."); } internalFormat = PixelInternalFormat.R8; pixelDataFormat = PixelFormat.Red; pixelDataType = PixelType.UnsignedByte; // TODO: Does it make sense to default to 1 for RGB parameters? // It might make more sense to pass some options to change swizzling. var swizzle = new[] { (int)All.One, (int)All.One, (int)All.One, (int)All.Red }; GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleRgba, swizzle); } else if (pixelType == typeof(Gray8) && !actualParams.Srgb) { // Can only use R8 for Gray8 if sRGB is OFF. // Because OpenGL doesn't provide non-sRGB single/dual channel image formats. // Vulkan when? if (copy.Width % 4 != 0 || copy.Height % 4 != 0) { throw new ArgumentException("Gray8 non-sRGB images must have multiple of 4 sizes."); } internalFormat = PixelInternalFormat.R8; pixelDataFormat = PixelFormat.Red; pixelDataType = PixelType.UnsignedByte; var swizzle = new[] { (int)All.Red, (int)All.Red, (int)All.Red, (int)All.One }; GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureSwizzleRgba, swizzle); } else { throw new NotImplementedException($"Unable to handle pixel type '{pixelType.Name}'"); } unsafe { var span = copy.GetPixelSpan(); fixed(T *ptr = span) { GL.TexImage2D(TextureTarget.Texture2D, 0, internalFormat, copy.Width, copy.Height, 0, pixelDataFormat, pixelDataType, (IntPtr)ptr); } } return(GenTexture(texture, (copy.Width, copy.Height), name)); }
private RenderTexture CreateRenderTarget(Vector2i size, RenderTargetFormatParameters format, TextureSampleParameters?sampleParameters = null, string?name = null) { DebugTools.Assert(size.X != 0); DebugTools.Assert(size.Y != 0); // Cache currently bound framebuffers // so if somebody creates a framebuffer while drawing it won't ruin everything. var boundDrawBuffer = GL.GetInteger(GetPName.DrawFramebufferBinding); var boundReadBuffer = GL.GetInteger(GetPName.ReadFramebufferBinding); // Generate FBO. var fbo = new GLHandle(GL.GenFramebuffer()); // Bind color attachment to FBO. GL.BindFramebuffer(FramebufferTarget.Framebuffer, fbo.Handle); ObjectLabelMaybe(ObjectLabelIdentifier.Framebuffer, fbo, name); var(width, height) = size; ClydeTexture textureObject; GLHandle depthStencilBuffer = default; var estPixSize = 0L; // Color attachment. { var texture = new GLHandle(GL.GenTexture()); GL.BindTexture(TextureTarget.Texture2D, texture.Handle); ApplySampleParameters(sampleParameters); // Make sure to specify the correct pixel type and formats even if we're not uploading any data. // Not doing this (just sending red/byte) is fine on desktop GL but illegal on ES. var(internalFormat, pixFormat, pixType) = format.ColorFormat switch { // using block comments to force formatters to not f**k this up. RTCF.Rgba8 => /* */ (PIF.Rgba8, /* */ PF.Rgba, /**/ PT.UnsignedByte), RTCF.Rgba16F => /* */ (PIF.Rgba16f, /* */ PF.Rgba, /**/ PT.Float), RTCF.Rgba8Srgb => /* */ (PIF.Srgb8Alpha8, /* */ PF.Rgba, /**/ PT.UnsignedByte), RTCF.R11FG11FB10F => /**/ (PIF.R11fG11fB10f, /**/ PF.Rgb, /* */ PT.Float), RTCF.R32F => /* */ (PIF.R32f, /* */ PF.Red, /* */ PT.Float), RTCF.RG32F => /* */ (PIF.Rg32f, /* */ PF.Rg, /* */ PT.Float), RTCF.R8 => /* */ (PIF.R8, /* */ PF.Red, /* */ PT.UnsignedByte), _ => throw new ArgumentOutOfRangeException(nameof(format.ColorFormat), format.ColorFormat, null) }; estPixSize += EstPixelSize(internalFormat); GL.TexImage2D(TextureTarget.Texture2D, 0, internalFormat, width, height, 0, pixFormat, pixType, IntPtr.Zero); GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, texture.Handle, 0); textureObject = GenTexture(texture, size, name == null ? null : $"{name}-color"); } // Depth/stencil buffers. if (format.HasDepthStencil) { depthStencilBuffer = new GLHandle(GL.GenRenderbuffer()); GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, depthStencilBuffer.Handle); ObjectLabelMaybe(ObjectLabelIdentifier.Renderbuffer, depthStencilBuffer, name == null ? null : $"{name}-depth-stencil"); GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, RenderbufferStorage.Depth24Stencil8, width, height); estPixSize += 4; GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferAttachment.DepthStencilAttachment, RenderbufferTarget.Renderbuffer, depthStencilBuffer.Handle); } // This should always pass but OpenGL makes it easy to check for once so let's. var status = GL.CheckFramebufferStatus(FramebufferTarget.Framebuffer); DebugTools.Assert(status == FramebufferErrorCode.FramebufferComplete, $"new framebuffer has bad status {status}"); // Re-bind previous framebuffers. GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, boundDrawBuffer); GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, boundReadBuffer); var pressure = estPixSize * size.X * size.Y; var handle = AllocRid(); var data = new LoadedRenderTarget { IsWindow = false, DepthStencilHandle = depthStencilBuffer, FramebufferHandle = fbo, Size = size, TextureHandle = textureObject.TextureId, MemoryPressure = pressure }; //GC.AddMemoryPressure(pressure); var renderTarget = new RenderTexture(size, textureObject, this, handle); _renderTargets.Add(handle, data); return(renderTarget); }
/// <summary> /// IComparable Implementation /// </summary> public int CompareTo(T other) { return(GLHandle.CompareTo(other.GLHandle)); }