mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
renderer/opengl: create the screen texture
This commit is contained in:
@ -20,8 +20,13 @@ pub fn destroy(v: Framebuffer) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn bind(v: Framebuffer, target: Target) !Binding {
|
pub fn bind(v: Framebuffer, target: Target) !Binding {
|
||||||
|
// The default framebuffer is documented as being zero but
|
||||||
|
// on multiple OpenGL drivers its not zero, so we grab it
|
||||||
|
// at runtime.
|
||||||
|
var current: c.GLint = undefined;
|
||||||
|
glad.context.GetIntegerv.?(c.GL_FRAMEBUFFER_BINDING, ¤t);
|
||||||
glad.context.BindFramebuffer.?(@intFromEnum(target), v.id);
|
glad.context.BindFramebuffer.?(@intFromEnum(target), v.id);
|
||||||
return .{ .target = target };
|
return .{ .target = target, .previous = @intCast(current) };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enum for possible binding targets.
|
/// Enum for possible binding targets.
|
||||||
@ -55,9 +60,13 @@ pub const Status = enum(c_uint) {
|
|||||||
|
|
||||||
pub const Binding = struct {
|
pub const Binding = struct {
|
||||||
target: Target,
|
target: Target,
|
||||||
|
previous: c.GLuint,
|
||||||
|
|
||||||
pub fn unbind(self: Binding) void {
|
pub fn unbind(self: Binding) void {
|
||||||
glad.context.BindFramebuffer.?(@intFromEnum(self.target), 0);
|
glad.context.BindFramebuffer.?(
|
||||||
|
@intFromEnum(self.target),
|
||||||
|
self.previous,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn texture2D(
|
pub fn texture2D(
|
||||||
@ -78,6 +87,6 @@ pub const Binding = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn checkStatus(self: Binding) Status {
|
pub fn checkStatus(self: Binding) Status {
|
||||||
return @enumFromInt(glad.context.CheckFramebufferStatus.?(self.target));
|
return @enumFromInt(glad.context.CheckFramebufferStatus.?(@intFromEnum(self.target)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -7,11 +7,29 @@ const glad = @import("glad.zig");
|
|||||||
|
|
||||||
id: c.GLuint,
|
id: c.GLuint,
|
||||||
|
|
||||||
pub inline fn active(target: c.GLenum) !void {
|
pub fn active(target: c.GLenum) !void {
|
||||||
glad.context.ActiveTexture.?(target);
|
glad.context.ActiveTexture.?(target);
|
||||||
try errors.getError();
|
try errors.getError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a single texture.
|
||||||
|
pub fn create() !Texture {
|
||||||
|
var id: c.GLuint = undefined;
|
||||||
|
glad.context.GenTextures.?(1, &id);
|
||||||
|
return .{ .id = id };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// glBindTexture
|
||||||
|
pub fn bind(v: Texture, target: Target) !Binding {
|
||||||
|
glad.context.BindTexture.?(@intFromEnum(target), v.id);
|
||||||
|
try errors.getError();
|
||||||
|
return .{ .target = target };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(v: Texture) void {
|
||||||
|
glad.context.DeleteTextures.?(1, &v.id);
|
||||||
|
}
|
||||||
|
|
||||||
/// Enun for possible texture binding targets.
|
/// Enun for possible texture binding targets.
|
||||||
pub const Target = enum(c_uint) {
|
pub const Target = enum(c_uint) {
|
||||||
@"1D" = c.GL_TEXTURE_1D,
|
@"1D" = c.GL_TEXTURE_1D,
|
||||||
@ -48,8 +66,9 @@ pub const Parameter = enum(c_uint) {
|
|||||||
|
|
||||||
/// Internal format enum for texture images.
|
/// Internal format enum for texture images.
|
||||||
pub const InternalFormat = enum(c_int) {
|
pub const InternalFormat = enum(c_int) {
|
||||||
Red = c.GL_RED,
|
red = c.GL_RED,
|
||||||
RGBA = c.GL_RGBA,
|
rgb = c.GL_RGB,
|
||||||
|
rgba = c.GL_RGBA,
|
||||||
|
|
||||||
// There are so many more that I haven't filled in.
|
// There are so many more that I haven't filled in.
|
||||||
_,
|
_,
|
||||||
@ -57,8 +76,9 @@ pub const InternalFormat = enum(c_int) {
|
|||||||
|
|
||||||
/// Format for texture images
|
/// Format for texture images
|
||||||
pub const Format = enum(c_uint) {
|
pub const Format = enum(c_uint) {
|
||||||
Red = c.GL_RED,
|
red = c.GL_RED,
|
||||||
BGRA = c.GL_BGRA,
|
rgb = c.GL_RGB,
|
||||||
|
bgra = c.GL_BGRA,
|
||||||
|
|
||||||
// There are so many more that I haven't filled in.
|
// There are so many more that I haven't filled in.
|
||||||
_,
|
_,
|
||||||
@ -75,9 +95,8 @@ pub const DataType = enum(c_uint) {
|
|||||||
pub const Binding = struct {
|
pub const Binding = struct {
|
||||||
target: Target,
|
target: Target,
|
||||||
|
|
||||||
pub inline fn unbind(b: *Binding) void {
|
pub fn unbind(b: *const Binding) void {
|
||||||
glad.context.BindTexture.?(@intFromEnum(b.target), 0);
|
glad.context.BindTexture.?(@intFromEnum(b.target), 0);
|
||||||
b.* = undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generateMipmap(b: Binding) void {
|
pub fn generateMipmap(b: Binding) void {
|
||||||
@ -143,21 +162,3 @@ pub const Binding = struct {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Create a single texture.
|
|
||||||
pub inline fn create() !Texture {
|
|
||||||
var id: c.GLuint = undefined;
|
|
||||||
glad.context.GenTextures.?(1, &id);
|
|
||||||
return Texture{ .id = id };
|
|
||||||
}
|
|
||||||
|
|
||||||
/// glBindTexture
|
|
||||||
pub inline fn bind(v: Texture, target: Target) !Binding {
|
|
||||||
glad.context.BindTexture.?(@intFromEnum(target), v.id);
|
|
||||||
try errors.getError();
|
|
||||||
return Binding{ .target = target };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub inline fn destroy(v: Texture) void {
|
|
||||||
glad.context.DeleteTextures.?(1, &v.id);
|
|
||||||
}
|
|
||||||
|
@ -1346,11 +1346,11 @@ fn flushAtlas(self: *OpenGL) !void {
|
|||||||
atlas.resized = false;
|
atlas.resized = false;
|
||||||
try texbind.image2D(
|
try texbind.image2D(
|
||||||
0,
|
0,
|
||||||
.Red,
|
.red,
|
||||||
@intCast(atlas.size),
|
@intCast(atlas.size),
|
||||||
@intCast(atlas.size),
|
@intCast(atlas.size),
|
||||||
0,
|
0,
|
||||||
.Red,
|
.red,
|
||||||
.UnsignedByte,
|
.UnsignedByte,
|
||||||
atlas.data.ptr,
|
atlas.data.ptr,
|
||||||
);
|
);
|
||||||
@ -1361,7 +1361,7 @@ fn flushAtlas(self: *OpenGL) !void {
|
|||||||
0,
|
0,
|
||||||
@intCast(atlas.size),
|
@intCast(atlas.size),
|
||||||
@intCast(atlas.size),
|
@intCast(atlas.size),
|
||||||
.Red,
|
.red,
|
||||||
.UnsignedByte,
|
.UnsignedByte,
|
||||||
atlas.data.ptr,
|
atlas.data.ptr,
|
||||||
);
|
);
|
||||||
@ -1380,11 +1380,11 @@ fn flushAtlas(self: *OpenGL) !void {
|
|||||||
atlas.resized = false;
|
atlas.resized = false;
|
||||||
try texbind.image2D(
|
try texbind.image2D(
|
||||||
0,
|
0,
|
||||||
.RGBA,
|
.rgba,
|
||||||
@intCast(atlas.size),
|
@intCast(atlas.size),
|
||||||
@intCast(atlas.size),
|
@intCast(atlas.size),
|
||||||
0,
|
0,
|
||||||
.BGRA,
|
.bgra,
|
||||||
.UnsignedByte,
|
.UnsignedByte,
|
||||||
atlas.data.ptr,
|
atlas.data.ptr,
|
||||||
);
|
);
|
||||||
@ -1395,7 +1395,7 @@ fn flushAtlas(self: *OpenGL) !void {
|
|||||||
0,
|
0,
|
||||||
@intCast(atlas.size),
|
@intCast(atlas.size),
|
||||||
@intCast(atlas.size),
|
@intCast(atlas.size),
|
||||||
.BGRA,
|
.bgra,
|
||||||
.UnsignedByte,
|
.UnsignedByte,
|
||||||
atlas.data.ptr,
|
atlas.data.ptr,
|
||||||
);
|
);
|
||||||
@ -1441,8 +1441,7 @@ fn drawCustomPrograms(
|
|||||||
const custom_bind = try custom_state.bind();
|
const custom_bind = try custom_state.bind();
|
||||||
defer custom_bind.unbind();
|
defer custom_bind.unbind();
|
||||||
|
|
||||||
// Sync the uniform data.
|
// Setup the new frame
|
||||||
// TODO: only do this when the data has changed
|
|
||||||
try custom_state.newFrame();
|
try custom_state.newFrame();
|
||||||
|
|
||||||
// Go through each custom shader and draw it.
|
// Go through each custom shader and draw it.
|
||||||
@ -1469,6 +1468,14 @@ fn drawCellProgram(
|
|||||||
// are changes to the atlas.
|
// are changes to the atlas.
|
||||||
try self.flushAtlas();
|
try self.flushAtlas();
|
||||||
|
|
||||||
|
// If we have custom shaders, then we draw to the custom
|
||||||
|
// shader framebuffer.
|
||||||
|
const fbobind: ?gl.Framebuffer.Binding = fbobind: {
|
||||||
|
const state = gl_state.custom orelse break :fbobind null;
|
||||||
|
break :fbobind try state.fbo.bind(.framebuffer);
|
||||||
|
};
|
||||||
|
defer if (fbobind) |v| v.unbind();
|
||||||
|
|
||||||
// Clear the surface
|
// Clear the surface
|
||||||
gl.clearColor(
|
gl.clearColor(
|
||||||
@as(f32, @floatFromInt(self.draw_background.r)) / 255,
|
@as(f32, @floatFromInt(self.draw_background.r)) / 255,
|
||||||
@ -1615,11 +1622,11 @@ const GLState = struct {
|
|||||||
try texbind.parameter(.MagFilter, gl.c.GL_LINEAR);
|
try texbind.parameter(.MagFilter, gl.c.GL_LINEAR);
|
||||||
try texbind.image2D(
|
try texbind.image2D(
|
||||||
0,
|
0,
|
||||||
.Red,
|
.red,
|
||||||
@intCast(font_group.atlas_greyscale.size),
|
@intCast(font_group.atlas_greyscale.size),
|
||||||
@intCast(font_group.atlas_greyscale.size),
|
@intCast(font_group.atlas_greyscale.size),
|
||||||
0,
|
0,
|
||||||
.Red,
|
.red,
|
||||||
.UnsignedByte,
|
.UnsignedByte,
|
||||||
font_group.atlas_greyscale.data.ptr,
|
font_group.atlas_greyscale.data.ptr,
|
||||||
);
|
);
|
||||||
@ -1636,11 +1643,11 @@ const GLState = struct {
|
|||||||
try texbind.parameter(.MagFilter, gl.c.GL_LINEAR);
|
try texbind.parameter(.MagFilter, gl.c.GL_LINEAR);
|
||||||
try texbind.image2D(
|
try texbind.image2D(
|
||||||
0,
|
0,
|
||||||
.RGBA,
|
.rgba,
|
||||||
@intCast(font_group.atlas_color.size),
|
@intCast(font_group.atlas_color.size),
|
||||||
@intCast(font_group.atlas_color.size),
|
@intCast(font_group.atlas_color.size),
|
||||||
0,
|
0,
|
||||||
.BGRA,
|
.bgra,
|
||||||
.UnsignedByte,
|
.UnsignedByte,
|
||||||
font_group.atlas_color.data.ptr,
|
font_group.atlas_color.data.ptr,
|
||||||
);
|
);
|
||||||
|
@ -3,6 +3,8 @@ const Allocator = std.mem.Allocator;
|
|||||||
const gl = @import("opengl");
|
const gl = @import("opengl");
|
||||||
const ScreenSize = @import("../size.zig").ScreenSize;
|
const ScreenSize = @import("../size.zig").ScreenSize;
|
||||||
|
|
||||||
|
const log = std.log.scoped(.opengl_custom);
|
||||||
|
|
||||||
/// The "INDEX" is the index into the global GL state and the
|
/// The "INDEX" is the index into the global GL state and the
|
||||||
/// "BINDING" is the binding location in the shader.
|
/// "BINDING" is the binding location in the shader.
|
||||||
const UNIFORM_INDEX: gl.c.GLuint = 0;
|
const UNIFORM_INDEX: gl.c.GLuint = 0;
|
||||||
@ -27,9 +29,11 @@ pub const State = struct {
|
|||||||
uniforms: Uniforms,
|
uniforms: Uniforms,
|
||||||
|
|
||||||
/// The OpenGL buffers
|
/// The OpenGL buffers
|
||||||
|
fbo: gl.Framebuffer,
|
||||||
ubo: gl.Buffer,
|
ubo: gl.Buffer,
|
||||||
vao: gl.VertexArray,
|
vao: gl.VertexArray,
|
||||||
ebo: gl.Buffer,
|
ebo: gl.Buffer,
|
||||||
|
fb_texture: gl.Texture,
|
||||||
|
|
||||||
/// The set of programs for the custom shaders.
|
/// The set of programs for the custom shaders.
|
||||||
programs: []const Program,
|
programs: []const Program,
|
||||||
@ -50,6 +54,44 @@ pub const State = struct {
|
|||||||
try programs.append(try Program.init(src));
|
try programs.append(try Program.init(src));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create the texture for the framebuffer
|
||||||
|
const fb_tex = try gl.Texture.create();
|
||||||
|
errdefer fb_tex.destroy();
|
||||||
|
{
|
||||||
|
const texbind = try fb_tex.bind(.@"2D");
|
||||||
|
try texbind.parameter(.WrapS, gl.c.GL_CLAMP_TO_EDGE);
|
||||||
|
try texbind.parameter(.WrapT, gl.c.GL_CLAMP_TO_EDGE);
|
||||||
|
try texbind.parameter(.MinFilter, gl.c.GL_LINEAR);
|
||||||
|
try texbind.parameter(.MagFilter, gl.c.GL_LINEAR);
|
||||||
|
try texbind.image2D(
|
||||||
|
0,
|
||||||
|
.rgb,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
.rgb,
|
||||||
|
.UnsignedByte,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create our framebuffer for rendering off screen.
|
||||||
|
// The shader prior to custom shaders should use this
|
||||||
|
// framebuffer.
|
||||||
|
const fbo = try gl.Framebuffer.create();
|
||||||
|
errdefer fbo.destroy();
|
||||||
|
const fbbind = try fbo.bind(.framebuffer);
|
||||||
|
defer fbbind.unbind();
|
||||||
|
try fbbind.texture2D(.color0, .@"2D", fb_tex, 0);
|
||||||
|
const fbstatus = fbbind.checkStatus();
|
||||||
|
if (fbstatus != .complete) {
|
||||||
|
log.warn(
|
||||||
|
"framebuffer is not complete state={}",
|
||||||
|
.{fbstatus},
|
||||||
|
);
|
||||||
|
return error.InvalidFramebuffer;
|
||||||
|
}
|
||||||
|
|
||||||
// Create our uniform buffer that is shared across all
|
// Create our uniform buffer that is shared across all
|
||||||
// custom shaders
|
// custom shaders
|
||||||
const ubo = try gl.Buffer.create();
|
const ubo = try gl.Buffer.create();
|
||||||
@ -79,9 +121,11 @@ pub const State = struct {
|
|||||||
return .{
|
return .{
|
||||||
.programs = try programs.toOwnedSlice(),
|
.programs = try programs.toOwnedSlice(),
|
||||||
.uniforms = .{},
|
.uniforms = .{},
|
||||||
|
.fbo = fbo,
|
||||||
.ubo = ubo,
|
.ubo = ubo,
|
||||||
.vao = vao,
|
.vao = vao,
|
||||||
.ebo = ebo,
|
.ebo = ebo,
|
||||||
|
.fb_texture = fb_tex,
|
||||||
.last_frame_time = try std.time.Instant.now(),
|
.last_frame_time = try std.time.Instant.now(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -92,6 +136,8 @@ pub const State = struct {
|
|||||||
self.ubo.destroy();
|
self.ubo.destroy();
|
||||||
self.ebo.destroy();
|
self.ebo.destroy();
|
||||||
self.vao.destroy();
|
self.vao.destroy();
|
||||||
|
self.fb_texture.destroy();
|
||||||
|
self.fbo.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setScreenSize(self: *State, size: ScreenSize) !void {
|
pub fn setScreenSize(self: *State, size: ScreenSize) !void {
|
||||||
@ -137,6 +183,11 @@ pub const State = struct {
|
|||||||
// the global state.
|
// the global state.
|
||||||
try self.ubo.bindBase(.uniform, UNIFORM_INDEX);
|
try self.ubo.bindBase(.uniform, UNIFORM_INDEX);
|
||||||
|
|
||||||
|
// Bind our texture that is shared amongst all
|
||||||
|
try gl.Texture.active(gl.c.GL_TEXTURE0);
|
||||||
|
var texbind = try self.fb_texture.bind(.@"2D");
|
||||||
|
errdefer texbind.unbind();
|
||||||
|
|
||||||
const vao = try self.vao.bind();
|
const vao = try self.vao.bind();
|
||||||
errdefer vao.unbind();
|
errdefer vao.unbind();
|
||||||
|
|
||||||
@ -146,16 +197,19 @@ pub const State = struct {
|
|||||||
return .{
|
return .{
|
||||||
.vao = vao,
|
.vao = vao,
|
||||||
.ebo = ebo,
|
.ebo = ebo,
|
||||||
|
.fb_texture = texbind,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const Binding = struct {
|
pub const Binding = struct {
|
||||||
vao: gl.VertexArray.Binding,
|
vao: gl.VertexArray.Binding,
|
||||||
ebo: gl.Buffer.Binding,
|
ebo: gl.Buffer.Binding,
|
||||||
|
fb_texture: gl.Texture.Binding,
|
||||||
|
|
||||||
pub fn unbind(self: Binding) void {
|
pub fn unbind(self: Binding) void {
|
||||||
self.ebo.unbind();
|
self.ebo.unbind();
|
||||||
self.vao.unbind();
|
self.vao.unbind();
|
||||||
|
self.fb_texture.unbind();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user