mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-09-19 20:18:07 +00:00
pkg/opengl: polish API
This commit is contained in:
@@ -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.?(
|
||||
|
||||
@@ -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,7 +1,9 @@
|
||||
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,
|
||||
};
|
||||
|
||||
@@ -42,5 +42,6 @@ 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;
|
||||
|
||||
@@ -244,7 +244,9 @@ pub fn surfaceSize(self: *const OpenGL) !struct { width: u32, height: u32 } {
|
||||
/// but now we need to do this manually.
|
||||
pub fn setViewport(self: *const OpenGL, width: u32, height: u32) void {
|
||||
_ = self;
|
||||
gl.glad.context.Viewport.?(0, 0, @intCast(width), @intCast(height));
|
||||
gl.viewport(0, 0, @intCast(width), @intCast(height)) catch |err| {
|
||||
log.warn("failed to set OpenGL viewport err={}", .{err});
|
||||
};
|
||||
}
|
||||
|
||||
/// Actions taken before doing anything in `drawFrame`.
|
||||
@@ -348,7 +350,7 @@ pub inline fn textureOptions(self: OpenGL) Texture.Options {
|
||||
return .{
|
||||
.format = .rgba,
|
||||
.internal_format = .srgba,
|
||||
.target = .@"2D",
|
||||
.target = .@"2d",
|
||||
.min_filter = .linear,
|
||||
.mag_filter = .linear,
|
||||
.wrap_s = .clamp_to_edge,
|
||||
@@ -395,7 +397,7 @@ pub inline fn imageTextureOptions(
|
||||
return .{
|
||||
.format = format.toPixelFormat(),
|
||||
.internal_format = if (srgb) .srgba else .rgba,
|
||||
.target = .@"2D",
|
||||
.target = .@"2d",
|
||||
// TODO: Generate mipmaps for image textures and use
|
||||
// linear_mipmap_linear filtering so that they
|
||||
// look good even when scaled way down.
|
||||
@@ -426,7 +428,7 @@ pub fn initAtlasTexture(
|
||||
.{
|
||||
.format = format,
|
||||
.internal_format = internal_format,
|
||||
.target = .Rectangle,
|
||||
.target = .rectangle,
|
||||
.min_filter = .nearest,
|
||||
.mag_filter = .nearest,
|
||||
.wrap_s = .clamp_to_edge,
|
||||
|
||||
@@ -30,10 +30,10 @@ pub fn init(
|
||||
) Error!Self {
|
||||
const sampler = gl.Sampler.create() catch return error.OpenGLFailed;
|
||||
errdefer sampler.destroy();
|
||||
sampler.parameter(.WrapS, @intFromEnum(opts.wrap_s)) catch return error.OpenGLFailed;
|
||||
sampler.parameter(.WrapT, @intFromEnum(opts.wrap_t)) catch return error.OpenGLFailed;
|
||||
sampler.parameter(.MinFilter, @intFromEnum(opts.min_filter)) catch return error.OpenGLFailed;
|
||||
sampler.parameter(.MagFilter, @intFromEnum(opts.mag_filter)) catch return error.OpenGLFailed;
|
||||
sampler.parameter(.wrap_s, opts.wrap_s) catch return error.OpenGLFailed;
|
||||
sampler.parameter(.wrap_t, opts.wrap_t) catch return error.OpenGLFailed;
|
||||
sampler.parameter(.min_filter, opts.min_filter) catch return error.OpenGLFailed;
|
||||
sampler.parameter(.mag_filter, opts.mag_filter) catch return error.OpenGLFailed;
|
||||
|
||||
return .{
|
||||
.sampler = sampler,
|
||||
|
||||
@@ -56,23 +56,23 @@ pub fn init(opts: Options) !Self {
|
||||
const texture = try gl.Texture.create();
|
||||
errdefer texture.destroy();
|
||||
{
|
||||
const bound_tex = try texture.bind(.@"2D");
|
||||
const bound_tex = try texture.bind(.@"2d");
|
||||
defer bound_tex.unbind();
|
||||
try bound_tex.parameter(.MinFilter, @intFromEnum(gl.Texture.MinFilter.nearest));
|
||||
try bound_tex.parameter(.MagFilter, @intFromEnum(gl.Texture.MagFilter.nearest));
|
||||
try bound_tex.parameter(.WrapS, @intFromEnum(gl.Texture.Wrap.clamp_to_edge));
|
||||
try bound_tex.parameter(.WrapT, @intFromEnum(gl.Texture.Wrap.clamp_to_edge));
|
||||
try bound_tex.parameter(.min_filter, .nearest);
|
||||
try bound_tex.parameter(.mag_filter, .nearest);
|
||||
try bound_tex.parameter(.wrap_s, .clamp_to_edge);
|
||||
try bound_tex.parameter(.wrap_t, .clamp_to_edge);
|
||||
try bound_tex.image2D(
|
||||
0,
|
||||
.srgba,
|
||||
@intCast(opts.width),
|
||||
@intCast(opts.height),
|
||||
.rgba,
|
||||
.UnsignedByte,
|
||||
.unsigned_byte,
|
||||
null,
|
||||
);
|
||||
try bound_tex.parameter(.BaseLevel, @as(gl.c.GLint, 0));
|
||||
try bound_tex.parameter(.MaxLevel, @as(gl.c.GLint, 0));
|
||||
try bound_tex.parameter(.base_level, 0);
|
||||
try bound_tex.parameter(.max_level, 0);
|
||||
}
|
||||
|
||||
const fbo = try gl.Framebuffer.create();
|
||||
@@ -80,7 +80,7 @@ pub fn init(opts: Options) !Self {
|
||||
{
|
||||
const bound_fbo = try fbo.bind(.framebuffer);
|
||||
defer bound_fbo.unbind();
|
||||
try bound_fbo.texture2D(.color0, .@"2D", texture, 0);
|
||||
try bound_fbo.texture2D(.color0, .@"2d", texture, 0);
|
||||
switch (bound_fbo.checkStatus()) {
|
||||
.complete => {},
|
||||
else => |status| {
|
||||
@@ -94,23 +94,23 @@ pub fn init(opts: Options) !Self {
|
||||
const export_texture = try gl.Texture.create();
|
||||
errdefer export_texture.destroy();
|
||||
{
|
||||
const bound_tex = try export_texture.bind(.@"2D");
|
||||
const bound_tex = try export_texture.bind(.@"2d");
|
||||
defer bound_tex.unbind();
|
||||
try bound_tex.parameter(.MinFilter, @intFromEnum(gl.Texture.MinFilter.nearest));
|
||||
try bound_tex.parameter(.MagFilter, @intFromEnum(gl.Texture.MagFilter.nearest));
|
||||
try bound_tex.parameter(.WrapS, @intFromEnum(gl.Texture.Wrap.clamp_to_edge));
|
||||
try bound_tex.parameter(.WrapT, @intFromEnum(gl.Texture.Wrap.clamp_to_edge));
|
||||
try bound_tex.parameter(.min_filter, .nearest);
|
||||
try bound_tex.parameter(.mag_filter, .nearest);
|
||||
try bound_tex.parameter(.wrap_s, .clamp_to_edge);
|
||||
try bound_tex.parameter(.wrap_t, .clamp_to_edge);
|
||||
try bound_tex.image2D(
|
||||
0,
|
||||
.rgba,
|
||||
@intCast(opts.width),
|
||||
@intCast(opts.height),
|
||||
.rgba,
|
||||
.UnsignedByte,
|
||||
.unsigned_byte,
|
||||
null,
|
||||
);
|
||||
try bound_tex.parameter(.BaseLevel, @as(gl.c.GLint, 0));
|
||||
try bound_tex.parameter(.MaxLevel, @as(gl.c.GLint, 0));
|
||||
try bound_tex.parameter(.base_level, 0);
|
||||
try bound_tex.parameter(.max_level, 0);
|
||||
}
|
||||
|
||||
const export_fbo = try gl.Framebuffer.create();
|
||||
@@ -118,7 +118,7 @@ pub fn init(opts: Options) !Self {
|
||||
{
|
||||
const bound_fbo = try export_fbo.bind(.framebuffer);
|
||||
defer bound_fbo.unbind();
|
||||
try bound_fbo.texture2D(.color0, .@"2D", export_texture, 0);
|
||||
try bound_fbo.texture2D(.color0, .@"2d", export_texture, 0);
|
||||
switch (bound_fbo.checkStatus()) {
|
||||
.complete => {},
|
||||
else => |status| {
|
||||
|
||||
@@ -50,17 +50,17 @@ pub fn init(
|
||||
{
|
||||
const texbind = tex.bind(opts.target) catch return error.OpenGLFailed;
|
||||
defer texbind.unbind();
|
||||
texbind.parameter(.WrapS, @intFromEnum(opts.wrap_s)) catch return error.OpenGLFailed;
|
||||
texbind.parameter(.WrapT, @intFromEnum(opts.wrap_t)) catch return error.OpenGLFailed;
|
||||
texbind.parameter(.MinFilter, @intFromEnum(opts.min_filter)) catch return error.OpenGLFailed;
|
||||
texbind.parameter(.MagFilter, @intFromEnum(opts.mag_filter)) catch return error.OpenGLFailed;
|
||||
texbind.parameter(.wrap_s, opts.wrap_s) catch return error.OpenGLFailed;
|
||||
texbind.parameter(.wrap_t, opts.wrap_t) catch return error.OpenGLFailed;
|
||||
texbind.parameter(.min_filter, opts.min_filter) catch return error.OpenGLFailed;
|
||||
texbind.parameter(.mag_filter, opts.mag_filter) catch return error.OpenGLFailed;
|
||||
texbind.image2D(
|
||||
0,
|
||||
opts.internal_format,
|
||||
@intCast(width),
|
||||
@intCast(height),
|
||||
opts.format,
|
||||
.UnsignedByte,
|
||||
.unsigned_byte,
|
||||
if (data) |d| @ptrCast(d.ptr) else null,
|
||||
) catch return error.OpenGLFailed;
|
||||
}
|
||||
@@ -98,7 +98,7 @@ pub fn replaceRegion(
|
||||
@intCast(width),
|
||||
@intCast(height),
|
||||
self.format,
|
||||
.UnsignedByte,
|
||||
.unsigned_byte,
|
||||
data.ptr,
|
||||
) catch return error.OpenGLFailed;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user