CPF/CPF.Skia/GlContexts/Glx/GlxContext.cs
2023-11-21 23:05:03 +08:00

176 lines
5.0 KiB
C#

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using CPF.Drawing;
using SkiaSharp;
namespace CPF.Skia
{
internal class GlxContext : GlContext
{
private IntPtr fDisplay;
private IntPtr fPixmap;
private IntPtr fGlxPixmap;
private IntPtr fContext;
public GlxContext(IRenderTarget renderTarget)
{
fDisplay = Xlib.XOpenDisplay(null);
if (fDisplay == IntPtr.Zero) {
Dispose();
throw new Exception("Failed to open X display.");
}
var visualAttribs = new [] {
Glx.GLX_X_RENDERABLE, Xlib.True,
Glx.GLX_DRAWABLE_TYPE, Glx.GLX_PIXMAP_BIT,
Glx.GLX_RENDER_TYPE, Glx.GLX_RGBA_BIT,
// Glx.GLX_DOUBLEBUFFER, Xlib.True,
Glx.GLX_RED_SIZE, 8,
Glx.GLX_GREEN_SIZE, 8,
Glx.GLX_BLUE_SIZE, 8,
Glx.GLX_ALPHA_SIZE, 8,
Glx.GLX_DEPTH_SIZE, 24,
Glx.GLX_STENCIL_SIZE, 8,
// Glx.GLX_SAMPLE_BUFFERS, 1,
// Glx.GLX_SAMPLES, 4,
Xlib.None
};
int glxMajor, glxMinor;
if (!Glx.glXQueryVersion(fDisplay, out glxMajor, out glxMinor) ||
(glxMajor < 1) ||
(glxMajor == 1 && glxMinor < 3)) {
Dispose();
throw new Exception($"GLX version 1.3 or higher required ({glxMajor}.{glxMinor} provided).");
}
var fbc = Glx.ChooseFBConfig(fDisplay, Xlib.XDefaultScreen(fDisplay), visualAttribs);
if (fbc.Length == 0) {
Dispose();
throw new Exception("Failed to retrieve a framebuffer config.");
}
var bestFBC = IntPtr.Zero;
var bestNumSamp = -1;
for (int i = 0; i < fbc.Length; i++) {
int sampleBuf, samples;
Glx.glXGetFBConfigAttrib(fDisplay, fbc[i], Glx.GLX_SAMPLE_BUFFERS, out sampleBuf);
Glx.glXGetFBConfigAttrib(fDisplay, fbc[i], Glx.GLX_SAMPLES, out samples);
if (bestFBC == IntPtr.Zero || (sampleBuf > 0 && samples > bestNumSamp)) {
bestFBC = fbc[i];
bestNumSamp = samples;
}
}
var vi = Glx.GetVisualFromFBConfig(fDisplay, bestFBC);
fPixmap = Xlib.XCreatePixmap(fDisplay, Xlib.XRootWindow(fDisplay, vi.screen), 10, 10, (uint)vi.depth);
if (fPixmap == IntPtr.Zero) {
Dispose();
throw new Exception("Failed to create pixmap.");
}
fGlxPixmap = Glx.glXCreateGLXPixmap(fDisplay, ref vi, fPixmap);
var glxExts = Glx.QueryExtensions(fDisplay, Xlib.XDefaultScreen(fDisplay));
if (Array.IndexOf(glxExts, "GLX_ARB_create_context") == -1 ||
Glx.glXCreateContextAttribsARB == null) {
Console.WriteLine("OpenGL 3.0 doesn't seem to be available.");
fContext = Glx.glXCreateNewContext(fDisplay, bestFBC, Glx.GLX_RGBA_TYPE, IntPtr.Zero, Xlib.True);
} else {
// Let's just use OpenGL 3.0, but we could try find the highest
int major = 3, minor = 0;
var flags = new List<int> {
Glx.GLX_CONTEXT_MAJOR_VERSION_ARB, major,
Glx.GLX_CONTEXT_MINOR_VERSION_ARB, minor,
};
if (major > 2) {
flags.AddRange(new[] {
Glx.GLX_CONTEXT_PROFILE_MASK_ARB, Glx.GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
});
}
flags.Add(Xlib.None);
fContext = Glx.glXCreateContextAttribsARB(fDisplay, bestFBC, IntPtr.Zero, Xlib.True, flags.ToArray());
}
if (fContext == IntPtr.Zero) {
Dispose();
throw new Exception("Failed to create an OpenGL context.");
}
if (!Glx.glXIsDirect(fDisplay, fContext)) {
Console.WriteLine("Obtained indirect GLX rendering context.");
}
}
public override void MakeCurrent()
{
if (!Glx.glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) {
Dispose();
throw new Exception("Failed to set the context.");
}
}
public override void SwapBuffers()
{
Glx.glXSwapBuffers(fDisplay, fGlxPixmap);
}
public override void Dispose()
{
if (fDisplay != IntPtr.Zero) {
Glx.glXMakeCurrent(fDisplay, IntPtr.Zero, IntPtr.Zero);
if (fContext != IntPtr.Zero) {
Glx.glXDestroyContext(fDisplay, fContext);
fContext = IntPtr.Zero;
}
if (fGlxPixmap != IntPtr.Zero) {
Glx.glXDestroyGLXPixmap(fDisplay, fGlxPixmap);
fGlxPixmap = IntPtr.Zero;
}
if (fPixmap != IntPtr.Zero) {
Xlib.XFreePixmap(fDisplay, fPixmap);
fPixmap = IntPtr.Zero;
}
fDisplay = IntPtr.Zero;
}
}
public override GRGlTextureInfo CreateTexture(SKSizeI textureSize)
{
var textures = new uint[1];
Glx.glGenTextures(textures.Length, textures);
var textureId = textures[0];
Glx.glBindTexture(Glx.GL_TEXTURE_2D, textureId);
Glx.glTexImage2D(Glx.GL_TEXTURE_2D, 0, Glx.GL_RGBA, textureSize.Width, textureSize.Height, 0, Glx.GL_RGBA, Glx.GL_UNSIGNED_BYTE, IntPtr.Zero);
Glx.glBindTexture(Glx.GL_TEXTURE_2D, 0);
return new GRGlTextureInfo {
Id = textureId,
Target = Glx.GL_TEXTURE_2D,
Format = Glx.GL_RGBA8
};
}
public override void DestroyTexture(uint texture)
{
Glx.glDeleteTextures(1, new[] { texture });
}
public const int GL_FRAMEBUFFER_BINDING = 0x8CA6;
public override void GetFramebufferInfo(out int framebuffer, out int samples, out int stencil)
{
Glx.glGetIntegerv(GL_FRAMEBUFFER_BINDING, out framebuffer);
Glx.glGetIntegerv(3415, out stencil);
Glx.glGetIntegerv(32937, out samples);
}
}
}