mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-21 19:26:09 +03:00
renderer/metal: CRT effect, ugly hacky code
This commit is contained in:
@ -257,7 +257,9 @@ pub fn init(alloc: Allocator, options: renderer.Options) !Metal {
|
||||
errdefer buf_instance.deinit();
|
||||
|
||||
// Initialize our shaders
|
||||
var shaders = try Shaders.init(alloc, device, &.{});
|
||||
var shaders = try Shaders.init(alloc, device, &.{
|
||||
@embedFile("shaders/temp3.metal"),
|
||||
});
|
||||
errdefer shaders.deinit(alloc);
|
||||
|
||||
// Font atlas textures
|
||||
@ -584,6 +586,36 @@ pub fn drawFrame(self: *Metal, surface: *apprt.Surface) !void {
|
||||
// Get our drawable (CAMetalDrawable)
|
||||
const drawable = self.swapchain.msgSend(objc.Object, objc.sel("nextDrawable"), .{});
|
||||
|
||||
// Make our intermediate texture
|
||||
const target = target: {
|
||||
const desc = init: {
|
||||
const Class = objc.getClass("MTLTextureDescriptor").?;
|
||||
const id_alloc = Class.msgSend(objc.Object, objc.sel("alloc"), .{});
|
||||
const id_init = id_alloc.msgSend(objc.Object, objc.sel("init"), .{});
|
||||
break :init id_init;
|
||||
};
|
||||
|
||||
// Set our properties
|
||||
desc.setProperty("pixelFormat", @intFromEnum(mtl.MTLPixelFormat.bgra8unorm));
|
||||
desc.setProperty("width", @as(c_ulong, @intCast(self.screen_size.?.width)));
|
||||
desc.setProperty("height", @as(c_ulong, @intCast(self.screen_size.?.height)));
|
||||
desc.setProperty(
|
||||
"usage",
|
||||
@intFromEnum(mtl.MTLTextureUsage.render_target) |
|
||||
@intFromEnum(mtl.MTLTextureUsage.shader_read) |
|
||||
@intFromEnum(mtl.MTLTextureUsage.shader_write),
|
||||
);
|
||||
|
||||
const id = self.device.msgSend(
|
||||
?*anyopaque,
|
||||
objc.sel("newTextureWithDescriptor:"),
|
||||
.{desc},
|
||||
) orelse return error.MetalFailed;
|
||||
|
||||
break :target objc.Object.fromId(id);
|
||||
};
|
||||
defer target.msgSend(void, objc.sel("release"), .{});
|
||||
|
||||
// If our font atlas changed, sync the texture data
|
||||
if (self.font_group.atlas_greyscale.modified) {
|
||||
try syncAtlasTexture(self.device, &self.font_group.atlas_greyscale, &self.texture_greyscale);
|
||||
@ -620,10 +652,10 @@ pub fn drawFrame(self: *Metal, surface: *apprt.Surface) !void {
|
||||
// Ghostty in XCode in debug mode it returns a CaptureMTLDrawable
|
||||
// which ironically doesn't implement CAMetalDrawable as a
|
||||
// property so we just send a message.
|
||||
const texture = drawable.msgSend(objc.c.id, objc.sel("texture"), .{});
|
||||
//const texture = drawable.msgSend(objc.c.id, objc.sel("texture"), .{});
|
||||
attachment.setProperty("loadAction", @intFromEnum(mtl.MTLLoadAction.clear));
|
||||
attachment.setProperty("storeAction", @intFromEnum(mtl.MTLStoreAction.store));
|
||||
attachment.setProperty("texture", texture);
|
||||
attachment.setProperty("texture", target.value);
|
||||
attachment.setProperty("clearColor", mtl.MTLClearColor{
|
||||
.red = @as(f32, @floatFromInt(self.current_background_color.r)) / 255,
|
||||
.green = @as(f32, @floatFromInt(self.current_background_color.g)) / 255,
|
||||
@ -659,10 +691,132 @@ pub fn drawFrame(self: *Metal, surface: *apprt.Surface) !void {
|
||||
try self.drawImagePlacements(encoder, self.image_placements.items[self.image_text_end..]);
|
||||
}
|
||||
|
||||
{
|
||||
// MTLRenderPassDescriptor
|
||||
const desc = desc: {
|
||||
const MTLRenderPassDescriptor = objc.getClass("MTLRenderPassDescriptor").?;
|
||||
const desc = MTLRenderPassDescriptor.msgSend(
|
||||
objc.Object,
|
||||
objc.sel("renderPassDescriptor"),
|
||||
.{},
|
||||
);
|
||||
|
||||
// Set our color attachment to be our drawable surface.
|
||||
const attachments = objc.Object.fromId(desc.getProperty(?*anyopaque, "colorAttachments"));
|
||||
{
|
||||
const attachment = attachments.msgSend(
|
||||
objc.Object,
|
||||
objc.sel("objectAtIndexedSubscript:"),
|
||||
.{@as(c_ulong, 0)},
|
||||
);
|
||||
|
||||
// Texture is a property of CAMetalDrawable but if you run
|
||||
// Ghostty in XCode in debug mode it returns a CaptureMTLDrawable
|
||||
// which ironically doesn't implement CAMetalDrawable as a
|
||||
// property so we just send a message.
|
||||
const texture = drawable.msgSend(objc.c.id, objc.sel("texture"), .{});
|
||||
attachment.setProperty("loadAction", @intFromEnum(mtl.MTLLoadAction.clear));
|
||||
attachment.setProperty("storeAction", @intFromEnum(mtl.MTLStoreAction.store));
|
||||
attachment.setProperty("texture", texture);
|
||||
attachment.setProperty("clearColor", mtl.MTLClearColor{
|
||||
.red = 0,
|
||||
.green = 0,
|
||||
.blue = 0,
|
||||
.alpha = 1,
|
||||
});
|
||||
}
|
||||
|
||||
break :desc desc;
|
||||
};
|
||||
|
||||
// MTLRenderCommandEncoder
|
||||
const encoder = buffer.msgSend(
|
||||
objc.Object,
|
||||
objc.sel("renderCommandEncoderWithDescriptor:"),
|
||||
.{desc.value},
|
||||
);
|
||||
defer encoder.msgSend(void, objc.sel("endEncoding"), .{});
|
||||
|
||||
try self.drawPostShader(encoder, target.value);
|
||||
}
|
||||
|
||||
buffer.msgSend(void, objc.sel("presentDrawable:"), .{drawable.value});
|
||||
buffer.msgSend(void, objc.sel("commit"), .{});
|
||||
}
|
||||
|
||||
var post_time: f32 = 1;
|
||||
|
||||
fn drawPostShader(
|
||||
self: *Metal,
|
||||
encoder: objc.Object,
|
||||
texture: objc.c.id,
|
||||
) !void {
|
||||
// Use our image shader pipeline
|
||||
encoder.msgSend(
|
||||
void,
|
||||
objc.sel("setRenderPipelineState:"),
|
||||
.{self.shaders.post_pipelines[0].value},
|
||||
);
|
||||
|
||||
// Set our uniform, which is the only shared buffer
|
||||
encoder.msgSend(
|
||||
void,
|
||||
objc.sel("setVertexBytes:length:atIndex:"),
|
||||
.{
|
||||
@as(*const anyopaque, @ptrCast(&self.uniforms)),
|
||||
@as(c_ulong, @sizeOf(@TypeOf(self.uniforms))),
|
||||
@as(c_ulong, 1),
|
||||
},
|
||||
);
|
||||
|
||||
const Buffer = mtl_buffer.Buffer(mtl_shaders.PostUniforms);
|
||||
var buf = try Buffer.initFill(self.device, &.{.{
|
||||
.resolution = .{
|
||||
@floatFromInt(self.screen_size.?.width),
|
||||
@floatFromInt(self.screen_size.?.height),
|
||||
1,
|
||||
},
|
||||
.time = post_time,
|
||||
.time_delta = 1,
|
||||
.frame_rate = 1,
|
||||
.frame = 1,
|
||||
.channel_time = [1][4]f32{.{ 0, 0, 0, 0 }} ** 4,
|
||||
.channel_resolution = [1][4]f32{.{ 0, 0, 0, 0 }} ** 4,
|
||||
.mouse = .{ 0, 0, 0, 0 },
|
||||
.date = .{ 0, 0, 0, 0 },
|
||||
.sample_rate = 1,
|
||||
}});
|
||||
defer buf.deinit();
|
||||
post_time += 1;
|
||||
|
||||
// Set our buffer
|
||||
encoder.msgSend(
|
||||
void,
|
||||
objc.sel("setFragmentBuffer:offset:atIndex:"),
|
||||
.{ buf.buffer.value, @as(c_ulong, 0), @as(c_ulong, 0) },
|
||||
);
|
||||
|
||||
encoder.msgSend(
|
||||
void,
|
||||
objc.sel("setFragmentTexture:atIndex:"),
|
||||
.{
|
||||
texture,
|
||||
@as(c_ulong, 0),
|
||||
},
|
||||
);
|
||||
|
||||
// Draw!
|
||||
encoder.msgSend(
|
||||
void,
|
||||
objc.sel("drawPrimitives:vertexStart:vertexCount:"),
|
||||
.{
|
||||
@intFromEnum(mtl.MTLPrimitiveType.triangle_strip),
|
||||
@as(c_ulong, 0),
|
||||
@as(c_ulong, 4),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn drawImagePlacements(
|
||||
self: *Metal,
|
||||
encoder: objc.Object,
|
||||
|
@ -57,6 +57,7 @@ pub const MTLVertexStepFunction = enum(c_ulong) {
|
||||
/// https://developer.apple.com/documentation/metal/mtlpixelformat?language=objc
|
||||
pub const MTLPixelFormat = enum(c_ulong) {
|
||||
r8unorm = 10,
|
||||
rgba8unorm = 70,
|
||||
rgba8uint = 73,
|
||||
bgra8unorm = 80,
|
||||
};
|
||||
@ -98,6 +99,15 @@ pub const MTLBlendOperation = enum(c_ulong) {
|
||||
max = 4,
|
||||
};
|
||||
|
||||
/// https://developer.apple.com/documentation/metal/mtltextureusage?language=objc<D-j>
|
||||
pub const MTLTextureUsage = enum(c_ulong) {
|
||||
unknown = 0,
|
||||
shader_read = 1,
|
||||
shader_write = 2,
|
||||
render_target = 4,
|
||||
pixel_format_view = 8,
|
||||
};
|
||||
|
||||
/// https://developer.apple.com/documentation/metal/mtlresourceoptions?language=objc
|
||||
/// (incomplete, we only use this mode so we just hardcode it)
|
||||
pub const MTLResourceStorageModeShared: c_ulong = @intFromEnum(MTLStorageMode.shared) << 4;
|
||||
|
@ -49,6 +49,7 @@ pub const Shaders = struct {
|
||||
const post_pipelines: []const objc.Object = initPostPipelines(
|
||||
alloc,
|
||||
device,
|
||||
library,
|
||||
post_shaders,
|
||||
) catch |err| err: {
|
||||
// If an error happens while building postprocess shaders we
|
||||
@ -126,6 +127,20 @@ pub const Uniforms = extern struct {
|
||||
strikethrough_thickness: f32,
|
||||
};
|
||||
|
||||
/// The uniforms used for custom postprocess shaders.
|
||||
pub const PostUniforms = extern struct {
|
||||
resolution: [3]f32 align(16),
|
||||
time: f32 align(4),
|
||||
time_delta: f32 align(4),
|
||||
frame_rate: f32 align(4),
|
||||
frame: i32 align(4),
|
||||
channel_time: [4][4]f32 align(16),
|
||||
channel_resolution: [4][4]f32 align(16),
|
||||
mouse: [4]f32 align(16),
|
||||
date: [4]f32 align(16),
|
||||
sample_rate: f32 align(4),
|
||||
};
|
||||
|
||||
/// Initialize the MTLLibrary. A MTLLibrary is a collection of shaders.
|
||||
fn initLibrary(device: objc.Object) !objc.Object {
|
||||
// Hardcoded since this file isn't meant to be reusable.
|
||||
@ -157,6 +172,7 @@ fn initLibrary(device: objc.Object) !objc.Object {
|
||||
fn initPostPipelines(
|
||||
alloc: Allocator,
|
||||
device: objc.Object,
|
||||
library: objc.Object,
|
||||
shaders: []const [:0]const u8,
|
||||
) ![]const objc.Object {
|
||||
// If we have no shaders, do nothing.
|
||||
@ -177,7 +193,7 @@ fn initPostPipelines(
|
||||
// Build each shader. Note we don't use "0.." to build our index
|
||||
// because we need to keep track of our length to clean up above.
|
||||
for (shaders) |source| {
|
||||
pipelines[i] = try initPostPipeline(device, source);
|
||||
pipelines[i] = try initPostPipeline(device, library, source);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
@ -185,9 +201,13 @@ fn initPostPipelines(
|
||||
}
|
||||
|
||||
/// Initialize a single custom shader pipeline from shader source.
|
||||
fn initPostPipeline(device: objc.Object, data: [:0]const u8) !objc.Object {
|
||||
fn initPostPipeline(
|
||||
device: objc.Object,
|
||||
library: objc.Object,
|
||||
data: [:0]const u8,
|
||||
) !objc.Object {
|
||||
// Create our library which has the shader source
|
||||
const library = library: {
|
||||
const post_library = library: {
|
||||
const source = try macos.foundation.String.createWithBytes(
|
||||
data,
|
||||
.utf8,
|
||||
@ -196,22 +216,75 @@ fn initPostPipeline(device: objc.Object, data: [:0]const u8) !objc.Object {
|
||||
defer source.release();
|
||||
|
||||
var err: ?*anyopaque = null;
|
||||
const library = device.msgSend(
|
||||
const post_library = device.msgSend(
|
||||
objc.Object,
|
||||
objc.sel("newLibraryWithSource:options:error:"),
|
||||
.{ source, @as(?*anyopaque, null), &err },
|
||||
);
|
||||
try checkError(err);
|
||||
errdefer library.msgSend(void, objc.sel("release"), .{});
|
||||
errdefer post_library.msgSend(void, objc.sel("release"), .{});
|
||||
|
||||
break :library library;
|
||||
break :library post_library;
|
||||
};
|
||||
// TODO: need to do this once we set the pipeline
|
||||
//defer library.msgSend(void, objc.sel("release"), .{});
|
||||
defer post_library.msgSend(void, objc.sel("release"), .{});
|
||||
|
||||
// TODO: need to implement the actual pipeline
|
||||
// Get our vertex and fragment functions
|
||||
const func_vert = func_vert: {
|
||||
const str = try macos.foundation.String.createWithBytes(
|
||||
"post_vertex",
|
||||
.utf8,
|
||||
false,
|
||||
);
|
||||
defer str.release();
|
||||
|
||||
return library;
|
||||
const ptr = library.msgSend(?*anyopaque, objc.sel("newFunctionWithName:"), .{str});
|
||||
break :func_vert objc.Object.fromId(ptr.?);
|
||||
};
|
||||
const func_frag = func_frag: {
|
||||
const str = try macos.foundation.String.createWithBytes(
|
||||
"main0",
|
||||
.utf8,
|
||||
false,
|
||||
);
|
||||
defer str.release();
|
||||
|
||||
const ptr = post_library.msgSend(?*anyopaque, objc.sel("newFunctionWithName:"), .{str});
|
||||
break :func_frag objc.Object.fromId(ptr.?);
|
||||
};
|
||||
|
||||
// Create our descriptor
|
||||
const desc = init: {
|
||||
const Class = objc.getClass("MTLRenderPipelineDescriptor").?;
|
||||
const id_alloc = Class.msgSend(objc.Object, objc.sel("alloc"), .{});
|
||||
const id_init = id_alloc.msgSend(objc.Object, objc.sel("init"), .{});
|
||||
break :init id_init;
|
||||
};
|
||||
desc.setProperty("vertexFunction", func_vert);
|
||||
desc.setProperty("fragmentFunction", func_frag);
|
||||
|
||||
// Set our color attachment
|
||||
const attachments = objc.Object.fromId(desc.getProperty(?*anyopaque, "colorAttachments"));
|
||||
{
|
||||
const attachment = attachments.msgSend(
|
||||
objc.Object,
|
||||
objc.sel("objectAtIndexedSubscript:"),
|
||||
.{@as(c_ulong, 0)},
|
||||
);
|
||||
|
||||
// Value is MTLPixelFormatBGRA8Unorm
|
||||
attachment.setProperty("pixelFormat", @as(c_ulong, 80));
|
||||
}
|
||||
|
||||
// Make our state
|
||||
var err: ?*anyopaque = null;
|
||||
const pipeline_state = device.msgSend(
|
||||
objc.Object,
|
||||
objc.sel("newRenderPipelineStateWithDescriptor:error:"),
|
||||
.{ desc, &err },
|
||||
);
|
||||
try checkError(err);
|
||||
|
||||
return pipeline_state;
|
||||
}
|
||||
|
||||
/// Initialize the cell render pipeline for our shader library.
|
||||
|
@ -253,3 +253,20 @@ fragment float4 image_fragment(
|
||||
uint4 rgba = image.sample(textureSampler, in.tex_coord);
|
||||
return float4(rgba) / 255.0f;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Post Shader
|
||||
//-------------------------------------------------------------------
|
||||
#pragma mark - Post Shader
|
||||
|
||||
struct PostVertexOut {
|
||||
float4 position [[ position ]];
|
||||
};
|
||||
|
||||
constant float2 post_pos[4] = { {-1,-1}, {1,-1}, {-1,1}, {1,1 } };
|
||||
|
||||
vertex PostVertexOut post_vertex(uint id [[ vertex_id ]]) {
|
||||
PostVertexOut out;
|
||||
out.position = float4(post_pos[id], 0, 1);
|
||||
return out;
|
||||
}
|
||||
|
116
src/renderer/shaders/temp3.metal
Normal file
116
src/renderer/shaders/temp3.metal
Normal file
@ -0,0 +1,116 @@
|
||||
#pragma clang diagnostic ignored "-Wmissing-prototypes"
|
||||
|
||||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
// Implementation of the GLSL mod() function, which is slightly different than Metal fmod()
|
||||
template<typename Tx, typename Ty>
|
||||
inline Tx mod(Tx x, Ty y)
|
||||
{
|
||||
return x - y * floor(x / y);
|
||||
}
|
||||
|
||||
struct Globals
|
||||
{
|
||||
float3 iResolution;
|
||||
float iTime;
|
||||
float iTimeDelta;
|
||||
float iFrameRate;
|
||||
int iFrame;
|
||||
float4 iChannelTime[4];
|
||||
float3 iChannelResolution[4];
|
||||
float4 iMouse;
|
||||
float4 iDate;
|
||||
float iSampleRate;
|
||||
};
|
||||
|
||||
struct main0_out
|
||||
{
|
||||
float4 _fragColor [[color(0)]];
|
||||
};
|
||||
|
||||
static inline __attribute__((always_inline))
|
||||
float2 curve(thread float2& uv)
|
||||
{
|
||||
uv = (uv - float2(0.5)) * 2.0;
|
||||
uv *= 1.10000002384185791015625;
|
||||
uv.x *= (1.0 + pow(abs(uv.y) / 5.0, 2.0));
|
||||
uv.y *= (1.0 + pow(abs(uv.x) / 4.0, 2.0));
|
||||
uv = (uv / float2(2.0)) + float2(0.5);
|
||||
uv = (uv * 0.920000016689300537109375) + float2(0.039999999105930328369140625);
|
||||
return uv;
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline))
|
||||
void mainImage(thread float4& fragColor, thread const float2& fragCoord, constant Globals& _89, texture2d<float> iChannel0, sampler iChannel0Smplr)
|
||||
{
|
||||
float2 q = fragCoord / float2(_89.iResolution[0], _89.iResolution[1]);
|
||||
float2 uv = q;
|
||||
float2 param = uv;
|
||||
float2 _100 = curve(param);
|
||||
uv = _100;
|
||||
float3 oricol = iChannel0.sample(iChannel0Smplr, float2(q.x, q.y)).xyz;
|
||||
float x = ((sin((0.300000011920928955078125 * _89.iTime) + (uv.y * 21.0)) * sin((0.699999988079071044921875 * _89.iTime) + (uv.y * 29.0))) * sin((0.300000011920928955078125 + (0.3300000131130218505859375 * _89.iTime)) + (uv.y * 31.0))) * 0.001700000022538006305694580078125;
|
||||
float3 col;
|
||||
col.x = iChannel0.sample(iChannel0Smplr, float2((x + uv.x) + 0.001000000047497451305389404296875, uv.y + 0.001000000047497451305389404296875)).x + 0.0500000007450580596923828125;
|
||||
col.y = iChannel0.sample(iChannel0Smplr, float2((x + uv.x) + 0.0, uv.y - 0.00200000009499490261077880859375)).y + 0.0500000007450580596923828125;
|
||||
col.z = iChannel0.sample(iChannel0Smplr, float2((x + uv.x) - 0.00200000009499490261077880859375, uv.y + 0.0)).z + 0.0500000007450580596923828125;
|
||||
col.x += (0.07999999821186065673828125 * iChannel0.sample(iChannel0Smplr, ((float2(x + 0.02500000037252902984619140625, -0.02700000070035457611083984375) * 0.75) + float2(uv.x + 0.001000000047497451305389404296875, uv.y + 0.001000000047497451305389404296875))).x);
|
||||
col.y += (0.0500000007450580596923828125 * iChannel0.sample(iChannel0Smplr, ((float2(x + (-0.02199999988079071044921875), -0.0199999995529651641845703125) * 0.75) + float2(uv.x + 0.0, uv.y - 0.00200000009499490261077880859375))).y);
|
||||
col.z += (0.07999999821186065673828125 * iChannel0.sample(iChannel0Smplr, ((float2(x + (-0.0199999995529651641845703125), -0.017999999225139617919921875) * 0.75) + float2(uv.x - 0.00200000009499490261077880859375, uv.y + 0.0))).z);
|
||||
col = fast::clamp((col * 0.60000002384185791015625) + (((col * 0.4000000059604644775390625) * col) * 1.0), float3(0.0), float3(1.0));
|
||||
float vig = 0.0 + ((((16.0 * uv.x) * uv.y) * (1.0 - uv.x)) * (1.0 - uv.y));
|
||||
col *= float3(pow(vig, 0.300000011920928955078125));
|
||||
col *= float3(0.949999988079071044921875, 1.0499999523162841796875, 0.949999988079071044921875);
|
||||
col *= 2.7999999523162841796875;
|
||||
float scans = fast::clamp(0.3499999940395355224609375 + (0.3499999940395355224609375 * sin((3.5 * _89.iTime) + ((uv.y * _89.iResolution[1u]) * 1.5))), 0.0, 1.0);
|
||||
float s = pow(scans, 1.7000000476837158203125);
|
||||
col *= float3(0.4000000059604644775390625 + (0.699999988079071044921875 * s));
|
||||
col *= (1.0 + (0.00999999977648258209228515625 * sin(110.0 * _89.iTime)));
|
||||
bool _352 = uv.x < 0.0;
|
||||
bool _359;
|
||||
if (!_352)
|
||||
{
|
||||
_359 = uv.x > 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_359 = _352;
|
||||
}
|
||||
if (_359)
|
||||
{
|
||||
col *= 0.0;
|
||||
}
|
||||
bool _366 = uv.y < 0.0;
|
||||
bool _373;
|
||||
if (!_366)
|
||||
{
|
||||
_373 = uv.y > 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_373 = _366;
|
||||
}
|
||||
if (_373)
|
||||
{
|
||||
col *= 0.0;
|
||||
}
|
||||
col *= (float3(1.0) - (float3(fast::clamp((mod(fragCoord.x, 2.0) - 1.0) * 2.0, 0.0, 1.0)) * 0.64999997615814208984375));
|
||||
float comp = smoothstep(0.100000001490116119384765625, 0.89999997615814208984375, sin(_89.iTime));
|
||||
fragColor = float4(col, 1.0);
|
||||
}
|
||||
|
||||
fragment main0_out main0(constant Globals& _89 [[buffer(0)]], texture2d<float> iChannel0 [[texture(0)]], float4 gl_FragCoord [[position]])
|
||||
{
|
||||
constexpr sampler iChannel0Smplr(address::clamp_to_edge, filter::linear);
|
||||
|
||||
main0_out out = {};
|
||||
float2 param_1 = gl_FragCoord.xy;
|
||||
float4 param;
|
||||
mainImage(param, param_1, _89, iChannel0, iChannel0Smplr);
|
||||
out._fragColor = param;
|
||||
return out;
|
||||
}
|
||||
|
Reference in New Issue
Block a user