mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-09-20 04:28:06 +00:00
gtk,opengl: free us from the clutches of GtkGLArea (#14052)
GtkGLArea had numerous downsides that forced us to invent unsightly
hacks in our renderer to work around them, most chiefly the fact that it
holds its own GdkGLContext on the main thread (GL contexts are not at
all thread-safe), forcing us to keep our GL calls on the main thread. It
also does not interact well with triple-buffering and initialization is
forced to be this sort of deferred song-and-dance since we need to wait
for the GLArea to initialize its GL context before we can initialize the
renderer, the core surface, and then most things in the GTK surface.
We instead invent our own custom widget named RenderSurface that takes
simple DMABUFs and displays them. The task of obtaining a GL context
falls to manual EGL bindings, since we also need EGL to export OpenGL
textures into DMABUFs. We keep the EGL context solely on the render
thread meaning that the main thread never concerns itself with rendering
except when being notified that the renderer has pushed a new frame.
What makes this extra significant is that now the entire GTK apprt no
longer depends on OpenGL in any way, shape or form. As long as it is
being fed DMABUFs, it can render from whichever graphics API you want.
This means we can add more backends based on OpenGL ES or more likely
Vulkan rather painlessly in the future.
**AI disclosure**: I came up with the idea and let Pi implement most of
the nitty-gritty details around EGL, as well as replumbing the renderer
and cleaning up all the GTK-specific workarounds there. I then carefully
vetted every line of code and spent roughly as much time reviewing as
coding. Most of the documentation and all commit messages are in my own
words.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
const Buffer = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const c = @import("c.zig").c;
|
||||
const c = @import("c");
|
||||
const errors = @import("errors.zig");
|
||||
const glad = @import("glad.zig");
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const Framebuffer = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const c = @import("c.zig").c;
|
||||
const c = @import("c");
|
||||
const errors = @import("errors.zig");
|
||||
const glad = @import("glad.zig");
|
||||
const Texture = @import("Texture.zig");
|
||||
|
||||
@@ -4,7 +4,7 @@ const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const log = std.log.scoped(.opengl);
|
||||
|
||||
const c = @import("c.zig").c;
|
||||
const c = @import("c");
|
||||
const Shader = @import("Shader.zig");
|
||||
const errors = @import("errors.zig");
|
||||
const glad = @import("glad.zig");
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const Renderbuffer = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const c = @import("c.zig").c;
|
||||
const c = @import("c");
|
||||
const errors = @import("errors.zig");
|
||||
const glad = @import("glad.zig");
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const Sampler = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const c = @import("c.zig").c;
|
||||
const c = @import("c");
|
||||
const errors = @import("errors.zig");
|
||||
const glad = @import("glad.zig");
|
||||
const Texture = @import("Texture.zig");
|
||||
@@ -24,8 +24,8 @@ pub fn bind(v: Sampler, index: c_uint) !void {
|
||||
|
||||
pub fn parameter(
|
||||
self: Sampler,
|
||||
name: Texture.Parameter,
|
||||
value: anytype,
|
||||
comptime name: Texture.Parameter,
|
||||
value: name.Type(),
|
||||
) errors.Error!void {
|
||||
switch (@TypeOf(value)) {
|
||||
c.GLint => glad.context.SamplerParameteri.?(
|
||||
|
||||
@@ -4,7 +4,7 @@ const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const log = std.log.scoped(.opengl);
|
||||
|
||||
const c = @import("c.zig").c;
|
||||
const c = @import("c");
|
||||
const errors = @import("errors.zig");
|
||||
const glad = @import("glad.zig");
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const Texture = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const c = @import("c.zig").c;
|
||||
const c = @import("c");
|
||||
const errors = @import("errors.zig");
|
||||
const glad = @import("glad.zig");
|
||||
|
||||
@@ -33,36 +33,45 @@ pub fn destroy(v: Texture) void {
|
||||
|
||||
/// Enum for possible texture binding targets.
|
||||
pub const Target = enum(c_uint) {
|
||||
@"1D" = c.GL_TEXTURE_1D,
|
||||
@"2D" = c.GL_TEXTURE_2D,
|
||||
@"3D" = c.GL_TEXTURE_3D,
|
||||
@"1DArray" = c.GL_TEXTURE_1D_ARRAY,
|
||||
@"2DArray" = c.GL_TEXTURE_2D_ARRAY,
|
||||
Rectangle = c.GL_TEXTURE_RECTANGLE,
|
||||
CubeMap = c.GL_TEXTURE_CUBE_MAP,
|
||||
Buffer = c.GL_TEXTURE_BUFFER,
|
||||
@"2DMultisample" = c.GL_TEXTURE_2D_MULTISAMPLE,
|
||||
@"2DMultisampleArray" = c.GL_TEXTURE_2D_MULTISAMPLE_ARRAY,
|
||||
@"1d" = c.GL_TEXTURE_1D,
|
||||
@"2d" = c.GL_TEXTURE_2D,
|
||||
@"3d" = c.GL_TEXTURE_3D,
|
||||
@"1d_array" = c.GL_TEXTURE_1D_ARRAY,
|
||||
@"2d_array" = c.GL_TEXTURE_2D_ARRAY,
|
||||
rectangle = c.GL_TEXTURE_RECTANGLE,
|
||||
cube_map = c.GL_TEXTURE_CUBE_MAP,
|
||||
buffer = c.GL_TEXTURE_BUFFER,
|
||||
@"2d_multisample" = c.GL_TEXTURE_2D_MULTISAMPLE,
|
||||
@"2d_multisample_array" = c.GL_TEXTURE_2D_MULTISAMPLE_ARRAY,
|
||||
};
|
||||
|
||||
/// Enum for possible texture parameters.
|
||||
pub const Parameter = enum(c_uint) {
|
||||
BaseLevel = c.GL_TEXTURE_BASE_LEVEL,
|
||||
CompareFunc = c.GL_TEXTURE_COMPARE_FUNC,
|
||||
CompareMode = c.GL_TEXTURE_COMPARE_MODE,
|
||||
LodBias = c.GL_TEXTURE_LOD_BIAS,
|
||||
MinFilter = c.GL_TEXTURE_MIN_FILTER,
|
||||
MagFilter = c.GL_TEXTURE_MAG_FILTER,
|
||||
MinLod = c.GL_TEXTURE_MIN_LOD,
|
||||
MaxLod = c.GL_TEXTURE_MAX_LOD,
|
||||
MaxLevel = c.GL_TEXTURE_MAX_LEVEL,
|
||||
SwizzleR = c.GL_TEXTURE_SWIZZLE_R,
|
||||
SwizzleG = c.GL_TEXTURE_SWIZZLE_G,
|
||||
SwizzleB = c.GL_TEXTURE_SWIZZLE_B,
|
||||
SwizzleA = c.GL_TEXTURE_SWIZZLE_A,
|
||||
WrapS = c.GL_TEXTURE_WRAP_S,
|
||||
WrapT = c.GL_TEXTURE_WRAP_T,
|
||||
WrapR = c.GL_TEXTURE_WRAP_R,
|
||||
base_level = c.GL_TEXTURE_BASE_LEVEL,
|
||||
compare_func = c.GL_TEXTURE_COMPARE_FUNC,
|
||||
compare_mode = c.GL_TEXTURE_COMPARE_MODE,
|
||||
lod_bias = c.GL_TEXTURE_LOD_BIAS,
|
||||
min_filter = c.GL_TEXTURE_MIN_FILTER,
|
||||
mag_filter = c.GL_TEXTURE_MAG_FILTER,
|
||||
min_lod = c.GL_TEXTURE_MIN_LOD,
|
||||
max_lod = c.GL_TEXTURE_MAX_LOD,
|
||||
max_level = c.GL_TEXTURE_MAX_LEVEL,
|
||||
swizzle_r = c.GL_TEXTURE_SWIZZLE_R,
|
||||
swizzle_g = c.GL_TEXTURE_SWIZZLE_G,
|
||||
swizzle_b = c.GL_TEXTURE_SWIZZLE_B,
|
||||
swizzle_a = c.GL_TEXTURE_SWIZZLE_A,
|
||||
wrap_s = c.GL_TEXTURE_WRAP_S,
|
||||
wrap_t = c.GL_TEXTURE_WRAP_T,
|
||||
wrap_r = c.GL_TEXTURE_WRAP_R,
|
||||
|
||||
pub fn Type(comptime self: Parameter) type {
|
||||
return switch (self) {
|
||||
.min_filter => MinFilter,
|
||||
.mag_filter => MagFilter,
|
||||
.wrap_s, .wrap_t, .wrap_r => Wrap,
|
||||
else => c.GLint,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// Internal format enum for texture images.
|
||||
@@ -118,7 +127,7 @@ pub const Wrap = enum(c_int) {
|
||||
|
||||
/// Data type for texture images.
|
||||
pub const DataType = enum(c_uint) {
|
||||
UnsignedByte = c.GL_UNSIGNED_BYTE,
|
||||
unsigned_byte = c.GL_UNSIGNED_BYTE,
|
||||
|
||||
// There are so many more that I haven't filled in.
|
||||
_,
|
||||
@@ -135,14 +144,21 @@ pub const Binding = struct {
|
||||
glad.context.GenerateMipmap.?(@intFromEnum(b.target));
|
||||
}
|
||||
|
||||
pub fn parameter(b: Binding, name: Parameter, value: anytype) errors.Error!void {
|
||||
switch (@TypeOf(value)) {
|
||||
pub fn parameter(b: Binding, comptime name: Parameter, value: name.Type()) errors.Error!void {
|
||||
switch (name.Type()) {
|
||||
c.GLint => glad.context.TexParameteri.?(
|
||||
@intFromEnum(b.target),
|
||||
@intFromEnum(name),
|
||||
value,
|
||||
),
|
||||
else => unreachable,
|
||||
else => switch (@typeInfo(name.Type())) {
|
||||
.@"enum" => glad.context.TexParameteri.?(
|
||||
@intFromEnum(b.target),
|
||||
@intFromEnum(name),
|
||||
@intFromEnum(value),
|
||||
),
|
||||
else => @compileError("unknown parameter type"),
|
||||
},
|
||||
}
|
||||
try errors.getError();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const VertexArray = @This();
|
||||
|
||||
const c = @import("c.zig").c;
|
||||
const c = @import("c");
|
||||
const glad = @import("glad.zig");
|
||||
const errors = @import("errors.zig");
|
||||
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) !void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const c = b.addTranslateC(.{
|
||||
.root_source_file = b.path("gl.c"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
c.addIncludePath(b.path("../../vendor/glad/include"));
|
||||
|
||||
const module = b.addModule("opengl", .{ .root_source_file = b.path("main.zig") });
|
||||
module.addIncludePath(b.path("../../vendor/glad/include"));
|
||||
module.addImport("c", c.createModule());
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
pub const c = @cImport({
|
||||
@cInclude("glad/gl.h");
|
||||
});
|
||||
@@ -1,7 +1,9 @@
|
||||
const c = @import("c.zig").c;
|
||||
const std = @import("std");
|
||||
const c = @import("c");
|
||||
const errors = @import("errors.zig");
|
||||
const glad = @import("glad.zig");
|
||||
const Primitive = @import("primitives.zig").Primitive;
|
||||
const Texture = @import("Texture.zig");
|
||||
|
||||
pub fn clearColor(r: f32, g: f32, b: f32, a: f32) void {
|
||||
glad.context.ClearColor.?(r, g, b, a);
|
||||
@@ -75,11 +77,60 @@ pub fn blendFunc(sfactor: c.GLenum, dfactor: c.GLenum) !void {
|
||||
|
||||
pub fn viewport(x: c.GLint, y: c.GLint, width: c.GLsizei, height: c.GLsizei) !void {
|
||||
glad.context.Viewport.?(x, y, width, height);
|
||||
try errors.getError();
|
||||
}
|
||||
|
||||
pub fn readPixels(
|
||||
x: c.GLint,
|
||||
y: c.GLint,
|
||||
width: c.GLsizei,
|
||||
height: c.GLsizei,
|
||||
format: Texture.Format,
|
||||
typ: Texture.DataType,
|
||||
data: ?*anyopaque,
|
||||
) !void {
|
||||
glad.context.ReadPixels.?(
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
@intFromEnum(format),
|
||||
@intFromEnum(typ),
|
||||
data,
|
||||
);
|
||||
try errors.getError();
|
||||
}
|
||||
|
||||
pub fn blitFramebuffer(
|
||||
src_x0: c.GLint,
|
||||
src_y0: c.GLint,
|
||||
src_x1: c.GLint,
|
||||
src_y1: c.GLint,
|
||||
dst_x0: c.GLint,
|
||||
dst_y0: c.GLint,
|
||||
dst_x1: c.GLint,
|
||||
dst_y1: c.GLint,
|
||||
mask: BlitMask,
|
||||
filter: Texture.MagFilter,
|
||||
) !void {
|
||||
glad.context.BlitFramebuffer.?(
|
||||
src_x0,
|
||||
src_y0,
|
||||
src_x1,
|
||||
src_y1,
|
||||
dst_x0,
|
||||
dst_y0,
|
||||
dst_x1,
|
||||
dst_y1,
|
||||
@bitCast(mask),
|
||||
@intCast(@intFromEnum(filter)),
|
||||
);
|
||||
try errors.getError();
|
||||
}
|
||||
|
||||
pub fn pixelStore(mode: c.GLenum, value: anytype) !void {
|
||||
switch (@typeInfo(@TypeOf(value))) {
|
||||
.ComptimeInt, .Int => glad.context.PixelStorei.?(mode, value),
|
||||
.comptime_int, .int => glad.context.PixelStorei.?(mode, value),
|
||||
else => unreachable,
|
||||
}
|
||||
try errors.getError();
|
||||
@@ -92,3 +143,13 @@ pub fn finish() void {
|
||||
pub fn flush() void {
|
||||
glad.context.Flush.?();
|
||||
}
|
||||
|
||||
pub const BlitMask = packed struct(c.GLbitfield) {
|
||||
_pad1: u8 = 0,
|
||||
depth_buffer_bit: bool = false,
|
||||
_pad2: u1 = 0,
|
||||
stencil_buffer_bit: bool = false,
|
||||
_pad3: u3 = 0,
|
||||
color_buffer_bit: bool = false,
|
||||
_pad4: std.meta.Int(.unsigned, @bitSizeOf(c.GLbitfield) - 15) = 0,
|
||||
};
|
||||
|
||||
267
pkg/opengl/egl.zig
Normal file
267
pkg/opengl/egl.zig
Normal file
@@ -0,0 +1,267 @@
|
||||
//! Thin EGL bindings via GLAD.
|
||||
//!
|
||||
//! Only types and functions used in Ghostty are modelled,
|
||||
//! although the API design is rather flexible and can be extended
|
||||
//! to add whatever is required. Drop down to `gl.egl.c` to access
|
||||
//! raw EGL functions.
|
||||
//!
|
||||
//! Call `load()` once before using any EGL extension functions.
|
||||
|
||||
const std = @import("std");
|
||||
pub const c = @import("c");
|
||||
|
||||
const log = std.log.scoped(.opengl_egl);
|
||||
|
||||
pub fn load() error{EglInitFailed}!void {
|
||||
if (c.gladLoadEGL() == 0) return error.EglInitFailed;
|
||||
}
|
||||
|
||||
/// Wraps `eglGetProcAddress` for GLAD.
|
||||
pub fn getProcAddress(name: [*c]const u8) callconv(.c) ?*const fn () callconv(.c) void {
|
||||
return c.eglGetProcAddress(name);
|
||||
}
|
||||
|
||||
pub const Error = error{
|
||||
NotInitialized,
|
||||
BadAccess,
|
||||
BadAlloc,
|
||||
BadAttribute,
|
||||
BadContext,
|
||||
BadConfig,
|
||||
BadCurrentSurface,
|
||||
BadDisplay,
|
||||
BadSurface,
|
||||
BadMatch,
|
||||
BadParameter,
|
||||
BadNativePixmap,
|
||||
BadNativeWindow,
|
||||
ContextLost,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
pub fn getError() Error!void {
|
||||
return switch (c.eglGetError()) {
|
||||
c.EGL_SUCCESS => {},
|
||||
c.EGL_NOT_INITIALIZED => error.NotInitialized,
|
||||
c.EGL_BAD_ACCESS => error.BadAccess,
|
||||
c.EGL_BAD_ALLOC => error.BadAlloc,
|
||||
c.EGL_BAD_ATTRIBUTE => error.BadAttribute,
|
||||
c.EGL_BAD_CONTEXT => error.BadContext,
|
||||
c.EGL_BAD_CONFIG => error.BadConfig,
|
||||
c.EGL_BAD_CURRENT_SURFACE => error.BadCurrentSurface,
|
||||
c.EGL_BAD_DISPLAY => error.BadDisplay,
|
||||
c.EGL_BAD_SURFACE => error.BadSurface,
|
||||
c.EGL_BAD_MATCH => error.BadMatch,
|
||||
c.EGL_BAD_PARAMETER => error.BadParameter,
|
||||
c.EGL_BAD_NATIVE_PIXMAP => error.BadNativePixmap,
|
||||
c.EGL_BAD_NATIVE_WINDOW => error.BadNativeWindow,
|
||||
c.EGL_CONTEXT_LOST => error.ContextLost,
|
||||
else => Error.Unknown,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn mustError() Error {
|
||||
try getError();
|
||||
return error.Unknown;
|
||||
}
|
||||
|
||||
pub fn bindApi(api: c.EGLenum) Error!void {
|
||||
if (c.eglBindAPI(api) != c.EGL_TRUE) {
|
||||
return mustError();
|
||||
}
|
||||
}
|
||||
|
||||
pub const Display = opaque {
|
||||
pub fn init(id: c.EGLNativeDisplayType) Error!*Display {
|
||||
const display = c.eglGetDisplay(id) orelse return mustError();
|
||||
return initialize(display);
|
||||
}
|
||||
|
||||
fn initialize(display: c.EGLDisplay) Error!*Display {
|
||||
var major: c.EGLint = undefined;
|
||||
var minor: c.EGLint = undefined;
|
||||
if (c.eglInitialize(display, &major, &minor) != c.EGL_TRUE) {
|
||||
return mustError();
|
||||
}
|
||||
log.debug("EGL initialized {}.{}", .{ major, minor });
|
||||
return @ptrCast(display.?);
|
||||
}
|
||||
|
||||
pub fn terminate(self: *Display) void {
|
||||
_ = c.eglTerminate(@ptrCast(self));
|
||||
}
|
||||
|
||||
const StringQuery = enum(c.EGLint) {
|
||||
vendor = c.EGL_VENDOR,
|
||||
extensions = c.EGL_EXTENSIONS,
|
||||
};
|
||||
pub fn queryString(self: *Display, name: StringQuery) ?[:0]const u8 {
|
||||
return std.mem.span(c.eglQueryString(self, @intFromEnum(name)));
|
||||
}
|
||||
|
||||
/// Make the EGL context current on the calling thread and (re)load
|
||||
/// the thread-local GLAD function pointers so subsequent GL work is
|
||||
/// valid. Returns an error if the context can't be made current.
|
||||
pub fn makeCurrent(self: *Display, draw: ?*Surface, read: ?*Surface, context: ?*Context) Error!void {
|
||||
if (c.eglMakeCurrent(self, draw, read, context) != c.EGL_TRUE) {
|
||||
return mustError();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn releaseCurrent(self: *Display) void {
|
||||
_ = c.eglMakeCurrent(self, null, null, null);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Config = opaque {
|
||||
pub fn choose(display: *Display, attrs: [:c.EGL_NONE]const c.EGLint) Error!*Config {
|
||||
var config: c.EGLConfig = undefined;
|
||||
var num_config: c.EGLint = 0;
|
||||
if (c.eglChooseConfig(
|
||||
display,
|
||||
attrs.ptr,
|
||||
&config,
|
||||
1,
|
||||
&num_config,
|
||||
) != c.EGL_TRUE or num_config == 0) {
|
||||
return mustError();
|
||||
}
|
||||
return @ptrCast(config);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Surface = opaque {};
|
||||
|
||||
pub const Context = opaque {
|
||||
pub fn create(
|
||||
display: *Display,
|
||||
config: *Config,
|
||||
surface: ?*Surface,
|
||||
attrs: [:c.EGL_NONE]const c.EGLint,
|
||||
) Error!*Context {
|
||||
const context = c.eglCreateContext(
|
||||
display,
|
||||
config,
|
||||
surface,
|
||||
attrs.ptr,
|
||||
) orelse return mustError();
|
||||
return @ptrCast(context);
|
||||
}
|
||||
|
||||
pub fn destroy(self: *Context, display: *Display) Error!void {
|
||||
if (c.eglDestroyContext(display, self) != c.EGL_TRUE) {
|
||||
return mustError();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn AttribsBuilder(comptime cap: usize) type {
|
||||
return struct {
|
||||
attribs: [capacity:c.EGL_NONE]c.EGLAttrib,
|
||||
len: usize,
|
||||
|
||||
const Attribs = @This();
|
||||
pub const capacity = cap;
|
||||
|
||||
pub const empty: Attribs = .{
|
||||
// Save ourselves the trouble of manually terminating
|
||||
// the array with an `EGL_NONE`
|
||||
.attribs = @splat(c.EGL_NONE),
|
||||
.len = 0,
|
||||
};
|
||||
|
||||
pub fn add(
|
||||
self: *Attribs,
|
||||
k: c.EGLAttrib,
|
||||
v: c.EGLAttrib,
|
||||
) void {
|
||||
std.debug.assert(capacity - self.len >= 2);
|
||||
self.attribs[self.len] = k;
|
||||
self.attribs[self.len + 1] = v;
|
||||
self.len += 2;
|
||||
}
|
||||
|
||||
pub fn items(self: *const Attribs) [:c.EGL_NONE]const c.EGLAttrib {
|
||||
return self.attribs[0..self.len :c.EGL_NONE];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub const Image = opaque {
|
||||
pub fn create(
|
||||
display: *Display,
|
||||
context: ?*Context,
|
||||
target: ImageTarget,
|
||||
attrs: ?[:c.EGL_NONE]const c.EGLAttrib,
|
||||
) Error!*Image {
|
||||
const image = c.eglCreateImage(
|
||||
display,
|
||||
context,
|
||||
@intFromEnum(target),
|
||||
target.toClientBuffer(),
|
||||
if (attrs) |a| a.ptr else null,
|
||||
) orelse return mustError();
|
||||
|
||||
return @ptrCast(image);
|
||||
}
|
||||
|
||||
pub fn destroy(self: *Image, display: *Display) Error!void {
|
||||
if (c.eglDestroyImage(display, self) != c.EGL_TRUE) {
|
||||
return mustError();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exportDmabufQuery(self: *Image, display: *Display) Error!DmabufQuery {
|
||||
var query: DmabufQuery = undefined;
|
||||
if (c.eglExportDMABUFImageQueryMESA(
|
||||
display,
|
||||
self,
|
||||
&query.fourcc,
|
||||
&query.num_planes,
|
||||
&query.modifier,
|
||||
) != c.EGL_TRUE) {
|
||||
return mustError();
|
||||
}
|
||||
return query;
|
||||
}
|
||||
pub fn exportDmabuf(
|
||||
self: *Image,
|
||||
display: *Display,
|
||||
fds: []c_int,
|
||||
strides: []c_int,
|
||||
offsets: []c_int,
|
||||
) Error!void {
|
||||
if (c.eglExportDMABUFImageMESA(
|
||||
display,
|
||||
self,
|
||||
fds.ptr,
|
||||
strides.ptr,
|
||||
offsets.ptr,
|
||||
) != c.EGL_TRUE) {
|
||||
return mustError();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const ImageTarget = union(ImageTarget.Tag) {
|
||||
/// OpenGL 2D Texture with the given name.
|
||||
texture_2d: u32,
|
||||
|
||||
// Many other variants, add when needed
|
||||
pub const Tag = enum(c.EGLenum) {
|
||||
texture_2d = c.EGL_GL_TEXTURE_2D,
|
||||
};
|
||||
|
||||
pub fn toClientBuffer(self: ImageTarget) c.EGLClientBuffer {
|
||||
return switch (self) {
|
||||
// Yes this really is that cursed
|
||||
.texture_2d => |v| @ptrFromInt(@as(usize, v)),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const DmabufQuery = struct {
|
||||
fourcc: c_int,
|
||||
num_planes: c_int,
|
||||
modifier: u64,
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
const std = @import("std");
|
||||
const c = @import("c.zig").c;
|
||||
const c = @import("c");
|
||||
const glad = @import("glad.zig");
|
||||
|
||||
pub const Error = error{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const std = @import("std");
|
||||
const c = @import("c.zig").c;
|
||||
const c = @import("c");
|
||||
const errors = @import("errors.zig");
|
||||
const glad = @import("glad.zig");
|
||||
|
||||
|
||||
2
pkg/opengl/gl.c
Normal file
2
pkg/opengl/gl.c
Normal file
@@ -0,0 +1,2 @@
|
||||
#include <glad/gl.h>
|
||||
#include <glad/glad_egl.h>
|
||||
@@ -1,5 +1,5 @@
|
||||
const std = @import("std");
|
||||
const c = @import("c.zig").c;
|
||||
const c = @import("c");
|
||||
|
||||
pub const Context = c.GladGLContext;
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
//!
|
||||
//! WARNING: Lots of performance improvements that we can make with Zig
|
||||
//! comptime help. I'm deferring this until later but have some fun ideas.
|
||||
|
||||
pub const c = @import("c.zig").c;
|
||||
pub const c = @import("c");
|
||||
pub const glad = @import("glad.zig");
|
||||
pub const egl = @import("egl.zig");
|
||||
pub const ext = @import("extensions.zig");
|
||||
pub const Buffer = @import("Buffer.zig");
|
||||
pub const Framebuffer = @import("Framebuffer.zig");
|
||||
@@ -39,7 +39,9 @@ pub const drawElementsInstanced = draw.drawElementsInstanced;
|
||||
pub const enable = draw.enable;
|
||||
pub const disable = draw.disable;
|
||||
pub const frontFace = draw.frontFace;
|
||||
pub const readPixels = draw.readPixels;
|
||||
pub const pixelStore = draw.pixelStore;
|
||||
pub const viewport = draw.viewport;
|
||||
pub const blitFramebuffer = draw.blitFramebuffer;
|
||||
pub const flush = draw.flush;
|
||||
pub const finish = draw.finish;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pub const c = @import("c.zig").c;
|
||||
const c = @import("c");
|
||||
|
||||
pub const Primitive = enum(c_int) {
|
||||
point = c.GL_POINTS,
|
||||
|
||||
Reference in New Issue
Block a user