renderer-mvp/post-processing #56
							
								
								
									
										14
									
								
								build.zig
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								build.zig
									
									
									
									
									
								
							| @ -194,17 +194,7 @@ pub fn build(b: *std.Build) !void { | |||||||
| 			const vertex_binary_sub_path_utf8 = vertex_binary_sub_path.utf8(); | 			const vertex_binary_sub_path_utf8 = vertex_binary_sub_path.utf8(); | ||||||
| 			const fragment_binary_sub_path_utf8 = fragment_binary_sub_path.utf8(); | 			const fragment_binary_sub_path_utf8 = fragment_binary_sub_path.utf8(); | ||||||
| 
 | 
 | ||||||
| 			const link_command = b.addSystemCommand(&.{ | 			exe.step.dependOn(compile_vertex: { | ||||||
| 				"spirv-link", |  | ||||||
| 				vertex_binary_sub_path_utf8, |  | ||||||
| 				fragment_binary_sub_path_utf8, |  | ||||||
| 				"-o", |  | ||||||
| 				pending_shader.binary_sub_path.utf8(), |  | ||||||
| 			}); |  | ||||||
| 
 |  | ||||||
| 			exe.step.dependOn(&link_command.step); |  | ||||||
| 
 |  | ||||||
| 			link_command.step.dependOn(compile_vertex: { |  | ||||||
| 				const compile_command = b.addSystemCommand(&.{ | 				const compile_command = b.addSystemCommand(&.{ | ||||||
| 					"glslangValidator", | 					"glslangValidator", | ||||||
| 					"-V", | 					"-V", | ||||||
| @ -216,7 +206,7 @@ pub fn build(b: *std.Build) !void { | |||||||
| 				break: compile_vertex &compile_command.step; | 				break: compile_vertex &compile_command.step; | ||||||
| 			}); | 			}); | ||||||
| 
 | 
 | ||||||
| 			link_command.step.dependOn(compile_fragment: { | 			exe.step.dependOn(compile_fragment: { | ||||||
| 				const compile_command = b.addSystemCommand(&.{ | 				const compile_command = b.addSystemCommand(&.{ | ||||||
| 					"glslangValidator", | 					"glslangValidator", | ||||||
| 					"-V", | 					"-V", | ||||||
|  | |||||||
| @ -7,22 +7,19 @@ layout (location = 1) in vec2 uv; | |||||||
| 
 | 
 | ||||||
| layout (location = 0) out vec4 texel; | layout (location = 0) out vec4 texel; | ||||||
| 
 | 
 | ||||||
| layout (binding = 0) readonly buffer Camera { | layout (binding = 0) readonly buffer Effect { | ||||||
| 	mat4 projection_matrix; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| layout (binding = 1) readonly buffer Material { |  | ||||||
| 	float effect_magnitude; | 	float effect_magnitude; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void main() { | void main() { | ||||||
| 	const vec2 red_channel_uv = uv + vec2(effect_magnitude, 0.0); | 	vec4 color1 = texture(sprite, uv) / 3.0; | ||||||
| 	const vec2 blue_channel_uv = uv + vec2(-effect_magnitude, 0.0); |     vec4 color2 = texture(sprite, uv + 0.002 * effect_magnitude) / 3.0; | ||||||
| 	const vec4 original_texel = texture(sprite, uv); |     vec4 color3 = texture(sprite, uv - 0.002 * effect_magnitude) / 3.0; | ||||||
| 
 | 
 | ||||||
| 	texel = vec4(texture(sprite, red_channel_uv).r, original_texel.g, texture(sprite, blue_channel_uv).b, original_texel.a) * color; |     color1 *= 2.0; | ||||||
|  |     color2.g = 0.0; | ||||||
|  |     color2.b = 0.0; | ||||||
|  |     color3.r = 0.0; | ||||||
| 
 | 
 | ||||||
| 	if (texel.a == 0) { |     texel = color * (color1 + color2 + color3); | ||||||
| 		discard; |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										32
									
								
								src/main.zig
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								src/main.zig
									
									
									
									
									
								
							| @ -4,10 +4,15 @@ const std = @import("std"); | |||||||
| 
 | 
 | ||||||
| const ona = @import("ona"); | const ona = @import("ona"); | ||||||
| 
 | 
 | ||||||
|  | const ChromaticAberration = extern struct { | ||||||
|  | 	magnitude: f32, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| const Actors = struct { | const Actors = struct { | ||||||
| 	instances: coral.stack.Sequential(@Vector(2, f32)) = .{.allocator = coral.heap.allocator}, | 	instances: coral.stack.Sequential(@Vector(2, f32)) = .{.allocator = coral.heap.allocator}, | ||||||
| 	body_texture: ona.gfx.Texture = .default, | 	body_texture: ona.gfx.Texture = .default, | ||||||
| 	render_texture: ona.gfx.Texture = .default, | 	render_texture: ona.gfx.Texture = .default, | ||||||
|  | 	ca_effect: ona.gfx.Effect = .default, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const Player = struct { | const Player = struct { | ||||||
| @ -37,6 +42,8 @@ fn load(config: ona.Write(ona.gfx.Config), actors: ona.Write(Actors), assets: on | |||||||
| 		}, | 		}, | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
|  | 	actors.res.ca_effect = try assets.res.load_effect_file(coral.files.bundle, "./ca.frag.spv"); | ||||||
|  | 
 | ||||||
| 	try actors.res.instances.push_grow(.{0, 0}); | 	try actors.res.instances.push_grow(.{0, 0}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -44,12 +51,9 @@ fn exit(actors: ona.Write(Actors)) void { | |||||||
| 	actors.res.instances.deinit(); | 	actors.res.instances.deinit(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn render(commands: ona.gfx.Commands, actors: ona.Write(Actors), config: ona.Read(ona.gfx.Config)) !void { | fn render(commands: ona.gfx.Commands, actors: ona.Write(Actors)) !void { | ||||||
| 	try commands.set_target(.{ | 	try commands.set_effect(actors.res.ca_effect, ChromaticAberration{ | ||||||
| 		.texture = actors.res.render_texture, | 		.magnitude = 15.0, | ||||||
| 		.clear_color = .{0, 0, 0, 0}, |  | ||||||
| 		.clear_depth = 0, |  | ||||||
| 		.clear_stencil = 0, |  | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	for (actors.res.instances.values) |instance| { | 	for (actors.res.instances.values) |instance| { | ||||||
| @ -63,22 +67,6 @@ fn render(commands: ona.gfx.Commands, actors: ona.Write(Actors), config: ona.Rea | |||||||
| 			}, | 			}, | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	try commands.set_target(.{ |  | ||||||
| 		.clear_color = null, |  | ||||||
| 		.clear_depth = null, |  | ||||||
| 		.clear_stencil = null, |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	try commands.draw_texture(.{ |  | ||||||
| 		.texture = actors.res.render_texture, |  | ||||||
| 
 |  | ||||||
| 		.transform = .{ |  | ||||||
| 			.origin = .{@floatFromInt(config.res.width / 2), @floatFromInt(config.res.height / 2)}, |  | ||||||
| 			.xbasis = .{@floatFromInt(config.res.width), 0}, |  | ||||||
| 			.ybasis = .{0, @floatFromInt(config.res.height)}, |  | ||||||
| 		}, |  | ||||||
| 	}); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn update(player: ona.Read(Player), actors: ona.Write(Actors), mapping: ona.Read(ona.act.Mapping)) !void { | fn update(player: ona.Read(Player), actors: ona.Write(Actors), mapping: ona.Read(ona.act.Mapping)) !void { | ||||||
|  | |||||||
| @ -25,12 +25,11 @@ const std = @import("std"); | |||||||
| pub const Assets = struct { | pub const Assets = struct { | ||||||
| 	window: *ext.SDL_Window, | 	window: *ext.SDL_Window, | ||||||
| 	texture_formats: coral.stack.Sequential(TextureFormat), | 	texture_formats: coral.stack.Sequential(TextureFormat), | ||||||
| 	staging_arena: std.heap.ArenaAllocator, |  | ||||||
| 	frame_rendered: std.Thread.ResetEvent = .{}, | 	frame_rendered: std.Thread.ResetEvent = .{}, | ||||||
| 
 | 
 | ||||||
| 	pub const LoadError = std.mem.Allocator.Error; | 	pub const LoadError = std.mem.Allocator.Error; | ||||||
| 
 | 
 | ||||||
| 	pub const LoadFileError = LoadError || coral.files.AccessError || error { | 	pub const LoadFileError = LoadError || coral.files.ReadAllError || error { | ||||||
| 		FormatUnsupported, | 		FormatUnsupported, | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| @ -41,7 +40,6 @@ pub const Assets = struct { | |||||||
| 
 | 
 | ||||||
| 	fn deinit(self: *Assets) void { | 	fn deinit(self: *Assets) void { | ||||||
| 		rendering.enqueue_work(.shutdown); | 		rendering.enqueue_work(.shutdown); | ||||||
| 		self.staging_arena.deinit(); |  | ||||||
| 		self.texture_formats.deinit(); | 		self.texture_formats.deinit(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -64,12 +62,40 @@ pub const Assets = struct { | |||||||
| 		try rendering.startup(window); | 		try rendering.startup(window); | ||||||
| 
 | 
 | ||||||
| 		return .{ | 		return .{ | ||||||
| 			.staging_arena = std.heap.ArenaAllocator.init(coral.heap.allocator), |  | ||||||
| 			.texture_formats = .{.allocator = coral.heap.allocator}, | 			.texture_formats = .{.allocator = coral.heap.allocator}, | ||||||
| 			.window = window, | 			.window = window, | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	pub fn load_effect_file(_: *Assets, storage: coral.files.Storage, path: []const u8) LoadFileError!Effect { | ||||||
|  | 		if (!std.mem.endsWith(u8, path, ".spv")) { | ||||||
|  | 			return error.FormatUnsupported; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		const fragment_file_stat = try storage.stat(path); | ||||||
|  | 		const fragment_spirv = try coral.heap.allocator.alloc(u32, fragment_file_stat.size / @alignOf(u32)); | ||||||
|  | 
 | ||||||
|  | 		defer { | ||||||
|  | 			coral.heap.allocator.free(fragment_spirv); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		_ = try storage.read_all(path, std.mem.sliceAsBytes(fragment_spirv), .{}); | ||||||
|  | 
 | ||||||
|  | 		var loaded = coral.asyncio.Future(std.mem.Allocator.Error!Effect){}; | ||||||
|  | 
 | ||||||
|  | 		rendering.enqueue_work(.{ | ||||||
|  | 			.load_effect = .{ | ||||||
|  | 				.desc = .{ | ||||||
|  | 					.fragment_spirv = fragment_spirv, | ||||||
|  | 				}, | ||||||
|  | 
 | ||||||
|  | 				.loaded = &loaded, | ||||||
|  | 			}, | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
|  | 		return loaded.get().*; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	pub fn load_texture(_: *Assets, desc: Texture.Desc) std.mem.Allocator.Error!Texture { | 	pub fn load_texture(_: *Assets, desc: Texture.Desc) std.mem.Allocator.Error!Texture { | ||||||
| 		var loaded = coral.asyncio.Future(std.mem.Allocator.Error!Texture){}; | 		var loaded = coral.asyncio.Future(std.mem.Allocator.Error!Texture){}; | ||||||
| 
 | 
 | ||||||
| @ -84,12 +110,10 @@ pub const Assets = struct { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	pub fn load_texture_file(self: *Assets, storage: coral.files.Storage, path: []const u8) LoadFileError!Texture { | 	pub fn load_texture_file(self: *Assets, storage: coral.files.Storage, path: []const u8) LoadFileError!Texture { | ||||||
| 		defer { | 		var arena = std.heap.ArenaAllocator.init(coral.heap.allocator); | ||||||
| 			const max_cache_size = 536870912; |  | ||||||
| 
 | 
 | ||||||
| 			if (!self.staging_arena.reset(.{.retain_with_limit = max_cache_size})) { | 		defer { | ||||||
| 				std.log.warn("failed to retain staging arena size of {} bytes", .{max_cache_size}); | 			arena.deinit(); | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		for (self.texture_formats.values) |format| { | 		for (self.texture_formats.values) |format| { | ||||||
| @ -97,12 +121,26 @@ pub const Assets = struct { | |||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			return self.load_texture(try format.load_file(&self.staging_arena, storage, path)); | 			return self.load_texture(try format.load_file(&arena, storage, path)); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		return error.FormatUnsupported; | 		return error.FormatUnsupported; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	pub fn update_effect(_: *Assets, effect: Effect, properties: anytype) bool { | ||||||
|  | 		var setted = coral.asyncio.Future(bool){}; | ||||||
|  | 
 | ||||||
|  | 		rendering.enqueue_work(.{ | ||||||
|  | 			.update_effect = .{ | ||||||
|  | 				.properties = std.mem.asBytes(properties), | ||||||
|  | 				.effect = effect, | ||||||
|  | 				.setted = &setted, | ||||||
|  | 			}, | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
|  | 		return setted.get().*; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	pub const thread_restriction = .main; | 	pub const thread_restriction = .main; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -137,6 +175,15 @@ pub const Commands = struct { | |||||||
| 		try self.list.append(.{.draw_texture = command}); | 		try self.list.append(.{.draw_texture = command}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	pub fn set_effect(self: Commands, effect: handles.Effect, properties: anytype) std.mem.Allocator.Error!void { | ||||||
|  | 		try self.list.append(.{ | ||||||
|  | 			.set_effect = .{ | ||||||
|  | 				.properties = std.mem.asBytes(&properties), | ||||||
|  | 				.effect = effect, | ||||||
|  | 			}, | ||||||
|  | 		}); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	pub fn set_target(self: Commands, command: rendering.Command.SetTarget) std.mem.Allocator.Error!void { | 	pub fn set_target(self: Commands, command: rendering.Command.SetTarget) std.mem.Allocator.Error!void { | ||||||
| 		try self.list.append(.{.set_target = command}); | 		try self.list.append(.{.set_target = command}); | ||||||
| 	} | 	} | ||||||
| @ -164,6 +211,8 @@ pub const Input = union (enum) { | |||||||
| 	}; | 	}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | pub const Effect = handles.Effect; | ||||||
|  | 
 | ||||||
| pub const Rect = struct { | pub const Rect = struct { | ||||||
| 	left: f32, | 	left: f32, | ||||||
| 	top: f32, | 	top: f32, | ||||||
|  | |||||||
| @ -2,6 +2,10 @@ const coral = @import("coral"); | |||||||
| 
 | 
 | ||||||
| const std = @import("std"); | const std = @import("std"); | ||||||
| 
 | 
 | ||||||
|  | pub const Effect = Handle(struct { | ||||||
|  | 	fragment_spirv: []const u32, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
| fn Handle(comptime HandleDesc: type) type { | fn Handle(comptime HandleDesc: type) type { | ||||||
| 	return enum (u32) { | 	return enum (u32) { | ||||||
| 		default, | 		default, | ||||||
|  | |||||||
| @ -16,6 +16,7 @@ const std = @import("std"); | |||||||
| 
 | 
 | ||||||
| pub const Command = union (enum) { | pub const Command = union (enum) { | ||||||
| 	draw_texture: DrawTexture, | 	draw_texture: DrawTexture, | ||||||
|  | 	set_effect: SetEffect, | ||||||
| 	set_target: SetTarget, | 	set_target: SetTarget, | ||||||
| 
 | 
 | ||||||
| 	pub const DrawTexture = struct { | 	pub const DrawTexture = struct { | ||||||
| @ -23,6 +24,11 @@ pub const Command = union (enum) { | |||||||
| 		transform: lina.Transform2D, | 		transform: lina.Transform2D, | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
|  | 	pub const SetEffect = struct { | ||||||
|  | 		effect: handles.Effect, | ||||||
|  | 		properties: []const coral.io.Byte, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
| 	pub const SetTarget = struct { | 	pub const SetTarget = struct { | ||||||
| 		texture: ?handles.Texture = null, | 		texture: ?handles.Texture = null, | ||||||
| 		clear_color: ?lina.Color, | 		clear_color: ?lina.Color, | ||||||
| @ -31,10 +37,16 @@ pub const Command = union (enum) { | |||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	fn clone(self: Command, arena: *std.heap.ArenaAllocator) !Command { | 	fn clone(self: Command, arena: *std.heap.ArenaAllocator) !Command { | ||||||
| 		_ = arena; |  | ||||||
| 
 |  | ||||||
| 		return switch (self) { | 		return switch (self) { | ||||||
| 			.draw_texture => |draw_texture| .{.draw_texture = draw_texture}, | 			.draw_texture => |draw_texture| .{.draw_texture = draw_texture}, | ||||||
|  | 
 | ||||||
|  | 			.set_effect => |set_effect| .{ | ||||||
|  | 				.set_effect = .{ | ||||||
|  | 					.properties = try arena.allocator().dupe(coral.io.Byte, set_effect.properties), | ||||||
|  | 					.effect = set_effect.effect, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 
 | ||||||
| 			.set_target => |set_target| .{.set_target = set_target}, | 			.set_target => |set_target| .{.set_target = set_target}, | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| @ -42,6 +54,7 @@ pub const Command = union (enum) { | |||||||
| 	fn process(self: Command, pools: *Pools, frame: *Frame) !void { | 	fn process(self: Command, pools: *Pools, frame: *Frame) !void { | ||||||
| 		try switch (self) { | 		try switch (self) { | ||||||
| 			.draw_texture => |draw_texture| frame.draw_texture(pools, draw_texture), | 			.draw_texture => |draw_texture| frame.draw_texture(pools, draw_texture), | ||||||
|  | 			.set_effect => |set_effect| frame.set_effect(pools, set_effect), | ||||||
| 			.set_target => |set_target| frame.set_target(pools, set_target), | 			.set_target => |set_target| frame.set_target(pools, set_target), | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| @ -123,6 +136,7 @@ const Frame = struct { | |||||||
| 	flushed_count: usize = 0, | 	flushed_count: usize = 0, | ||||||
| 	current_source_texture: handles.Texture = .default, | 	current_source_texture: handles.Texture = .default, | ||||||
| 	current_target_texture: ?handles.Texture = null, | 	current_target_texture: ?handles.Texture = null, | ||||||
|  | 	current_effect: handles.Effect = .default, | ||||||
| 
 | 
 | ||||||
| 	const DrawTexture = extern struct { | 	const DrawTexture = extern struct { | ||||||
| 		transform: lina.Transform2D, | 		transform: lina.Transform2D, | ||||||
| @ -175,29 +189,29 @@ const Frame = struct { | |||||||
| 			.render => |render| { | 			.render => |render| { | ||||||
| 				bindings.fs.images[0] = render.color_image; | 				bindings.fs.images[0] = render.color_image; | ||||||
| 				bindings.fs.samplers[0] = render.sampler; | 				bindings.fs.samplers[0] = render.sampler; | ||||||
| 				bindings.vs.images[0] = render.color_image; |  | ||||||
| 				bindings.vs.samplers[0] = render.sampler; |  | ||||||
| 			}, | 			}, | ||||||
| 
 | 
 | ||||||
| 			.static => |static| { | 			.static => |static| { | ||||||
| 				bindings.fs.images[0] = static.image; | 				bindings.fs.images[0] = static.image; | ||||||
| 				bindings.fs.samplers[0] = static.sampler; | 				bindings.fs.samplers[0] = static.sampler; | ||||||
| 				bindings.vs.images[0] = static.image; |  | ||||||
| 				bindings.vs.samplers[0] = static.sampler; |  | ||||||
| 			}, | 			}, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (self.current_target_texture) |target_texture| { | 		if (self.current_target_texture) |target_texture| { | ||||||
| 			const target_view_buffer = pools.textures.get(@intFromEnum(target_texture)).?.render.view_buffer; | 			const target_view_buffer = pools.textures.get(@intFromEnum(target_texture)).?.render.view_buffer; | ||||||
| 
 | 
 | ||||||
| 			bindings.fs.storage_buffers[0] = target_view_buffer; |  | ||||||
| 			bindings.vs.storage_buffers[0] = target_view_buffer; | 			bindings.vs.storage_buffers[0] = target_view_buffer; | ||||||
| 		} else { | 		} else { | ||||||
| 			bindings.fs.storage_buffers[0] = view_buffer; |  | ||||||
| 			bindings.vs.storage_buffers[0] = view_buffer; | 			bindings.vs.storage_buffers[0] = view_buffer; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		sokol.gfx.applyPipeline(texture_batching_pipeline); | 		const effect = pools.effects.get(@intFromEnum(self.current_effect)).?; | ||||||
|  | 
 | ||||||
|  | 		sokol.gfx.applyPipeline(effect.pipeline); | ||||||
|  | 
 | ||||||
|  | 		if (effect.has_properties_buffer) |properties_buffer| { | ||||||
|  | 			bindings.fs.storage_buffers[0] = properties_buffer; | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 		while (true) { | 		while (true) { | ||||||
| 			const buffer_index = self.flushed_count / batches_per_buffer; | 			const buffer_index = self.flushed_count / batches_per_buffer; | ||||||
| @ -217,11 +231,29 @@ const Frame = struct { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	pub fn set_effect(self: *Frame, pools: *Pools, command: Command.SetEffect) void { | ||||||
|  | 		if (command.effect != self.current_effect) { | ||||||
|  | 			self.flush(pools); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		self.current_effect = command.effect; | ||||||
|  | 
 | ||||||
|  | 		if (command.properties.len != 0) { | ||||||
|  | 			if (pools.effects.get(@intFromEnum(self.current_effect)).?.has_properties_buffer) |properties_buffer| { | ||||||
|  | 				sokol.gfx.updateBuffer(properties_buffer, sokol.gfx.asRange(command.properties)); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	pub fn set_target(self: *Frame, pools: *Pools, command: Command.SetTarget) void { | 	pub fn set_target(self: *Frame, pools: *Pools, command: Command.SetTarget) void { | ||||||
| 		sokol.gfx.endPass(); | 		sokol.gfx.endPass(); | ||||||
| 
 | 
 | ||||||
| 		var pass = sokol.gfx.Pass{ | 		var pass = sokol.gfx.Pass{ | ||||||
| 			.action = .{.stencil = .{.load_action = .CLEAR}}, | 			.action = .{ | ||||||
|  | 				.stencil = .{ | ||||||
|  | 					.load_action = .CLEAR, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 		if (command.clear_color) |color| { | 		if (command.clear_color) |color| { | ||||||
| @ -265,15 +297,24 @@ const Frame = struct { | |||||||
| 	var texture_batch_buffers = coral.stack.Sequential(sokol.gfx.Buffer){.allocator = coral.heap.allocator}; | 	var texture_batch_buffers = coral.stack.Sequential(sokol.gfx.Buffer){.allocator = coral.heap.allocator}; | ||||||
| 
 | 
 | ||||||
| 	const batches_per_buffer = 512; | 	const batches_per_buffer = 512; | ||||||
| 
 |  | ||||||
| 	var texture_batching_pipeline: sokol.gfx.Pipeline = undefined; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const Pools = struct { | const Pools = struct { | ||||||
|  | 	effects: EffectPool, | ||||||
| 	textures: TexturePool, | 	textures: TexturePool, | ||||||
| 
 | 
 | ||||||
|  | 	const EffectPool = coral.Pool(resources.Effect); | ||||||
|  | 
 | ||||||
| 	const TexturePool = coral.Pool(resources.Texture); | 	const TexturePool = coral.Pool(resources.Texture); | ||||||
| 
 | 
 | ||||||
|  | 	fn create_effect(self: *Pools, desc: handles.Effect.Desc) !handles.Effect { | ||||||
|  | 		var effect = try resources.Effect.init(desc); | ||||||
|  | 
 | ||||||
|  | 		errdefer effect.deinit(); | ||||||
|  | 
 | ||||||
|  | 		return @enumFromInt(try self.effects.insert(effect)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	fn create_texture(self: *Pools, desc: handles.Texture.Desc) !handles.Texture { | 	fn create_texture(self: *Pools, desc: handles.Texture.Desc) !handles.Texture { | ||||||
| 		var texture = try resources.Texture.init(desc); | 		var texture = try resources.Texture.init(desc); | ||||||
| 
 | 
 | ||||||
| @ -292,12 +333,28 @@ const Pools = struct { | |||||||
| 		self.textures.deinit(); | 		self.textures.deinit(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	fn destroy_texture(self: *Pools, texture_key: handles.Texture) bool { | 	fn destroy_effect(self: *Pools, handle: handles.Effect) bool { | ||||||
| 		switch (texture_key) { | 		switch (handle) { | ||||||
| 			.default => {}, | 			.default => {}, | ||||||
| 
 | 
 | ||||||
| 			else => { | 			else => { | ||||||
| 				var texture = self.textures.remove(@intFromEnum(texture_key)) orelse { | 				var effect = self.effects.remove(@intFromEnum(handle)) orelse { | ||||||
|  | 					return false; | ||||||
|  | 				}; | ||||||
|  | 
 | ||||||
|  | 				effect.deinit(); | ||||||
|  | 			}, | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	fn destroy_texture(self: *Pools, handle: handles.Texture) bool { | ||||||
|  | 		switch (handle) { | ||||||
|  | 			.default => {}, | ||||||
|  | 
 | ||||||
|  | 			else => { | ||||||
|  | 				var texture = self.textures.remove(@intFromEnum(handle)) orelse { | ||||||
| 					return false; | 					return false; | ||||||
| 				}; | 				}; | ||||||
| 
 | 
 | ||||||
| @ -310,10 +367,17 @@ const Pools = struct { | |||||||
| 
 | 
 | ||||||
| 	fn init(allocator: std.mem.Allocator) !Pools { | 	fn init(allocator: std.mem.Allocator) !Pools { | ||||||
| 		var pools = Pools{ | 		var pools = Pools{ | ||||||
|  | 			.effects = EffectPool.init(allocator), | ||||||
| 			.textures = TexturePool.init(allocator), | 			.textures = TexturePool.init(allocator), | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 		errdefer pools.deinit(); | 		errdefer { | ||||||
|  | 			pools.deinit(); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		_ = try pools.create_effect(.{ | ||||||
|  | 			.fragment_spirv = &spirv.to_ops(@embedFile("./shaders/2d_default.frag.spv")), | ||||||
|  | 		}); | ||||||
| 
 | 
 | ||||||
| 		const default_texture_data = [_]u32{ | 		const default_texture_data = [_]u32{ | ||||||
| 			0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF, | 			0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF, 0x000000FF, 0x800080FF, | ||||||
| @ -343,16 +407,23 @@ const Pools = struct { | |||||||
| 
 | 
 | ||||||
| pub const Work = union (enum) { | pub const Work = union (enum) { | ||||||
| 	create_commands: CreateCommandsWork, | 	create_commands: CreateCommandsWork, | ||||||
|  | 	load_effect: LoadEffectWork, | ||||||
| 	load_texture: LoadTextureWork, | 	load_texture: LoadTextureWork, | ||||||
| 	render_frame: RenderFrameWork, | 	render_frame: RenderFrameWork, | ||||||
| 	rotate_commands: RotateCommandsWork, | 	rotate_commands: RotateCommandsWork, | ||||||
| 	shutdown, | 	shutdown, | ||||||
|  | 	unload_effect: UnloadEffectWork, | ||||||
| 	unload_texture: UnloadTextureWork, | 	unload_texture: UnloadTextureWork, | ||||||
| 
 | 
 | ||||||
| 	pub const CreateCommandsWork = struct { | 	pub const CreateCommandsWork = struct { | ||||||
| 		created: *coral.asyncio.Future(std.mem.Allocator.Error!*Commands), | 		created: *coral.asyncio.Future(std.mem.Allocator.Error!*Commands), | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
|  | 	pub const LoadEffectWork = struct { | ||||||
|  | 		desc: handles.Effect.Desc, | ||||||
|  | 		loaded: *coral.asyncio.Future(std.mem.Allocator.Error!handles.Effect), | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
| 	pub const LoadTextureWork = struct { | 	pub const LoadTextureWork = struct { | ||||||
| 		desc: handles.Texture.Desc, | 		desc: handles.Texture.Desc, | ||||||
| 		loaded: *coral.asyncio.Future(std.mem.Allocator.Error!handles.Texture), | 		loaded: *coral.asyncio.Future(std.mem.Allocator.Error!handles.Texture), | ||||||
| @ -369,6 +440,10 @@ pub const Work = union (enum) { | |||||||
| 		finished: *std.Thread.ResetEvent, | 		finished: *std.Thread.ResetEvent, | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
|  | 	pub const UnloadEffectWork = struct { | ||||||
|  | 		handle: handles.Effect, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
| 	pub const UnloadTextureWork = struct { | 	pub const UnloadTextureWork = struct { | ||||||
| 		handle: handles.Texture, | 		handle: handles.Texture, | ||||||
| 	}; | 	}; | ||||||
| @ -395,62 +470,6 @@ const vertex_indices = .{ | |||||||
| 	.instance = 1, | 	.instance = 1, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const vertex_layout_state = sokol.gfx.VertexLayoutState{ |  | ||||||
| 	.attrs = get: { |  | ||||||
| 		var attrs = [_]sokol.gfx.VertexAttrState{.{}} ** 16; |  | ||||||
| 
 |  | ||||||
| 		attrs[0] = .{ |  | ||||||
| 			.format = .FLOAT2, |  | ||||||
| 			.buffer_index = vertex_indices.mesh, |  | ||||||
| 		}; |  | ||||||
| 
 |  | ||||||
| 		attrs[1] = .{ |  | ||||||
| 			.format = .FLOAT2, |  | ||||||
| 			.buffer_index = vertex_indices.mesh, |  | ||||||
| 		}; |  | ||||||
| 
 |  | ||||||
| 		attrs[2] = .{ |  | ||||||
| 			.format = .FLOAT2, |  | ||||||
| 			.buffer_index = vertex_indices.instance, |  | ||||||
| 		}; |  | ||||||
| 
 |  | ||||||
| 		attrs[3] = .{ |  | ||||||
| 			.format = .FLOAT2, |  | ||||||
| 			.buffer_index = vertex_indices.instance, |  | ||||||
| 		}; |  | ||||||
| 
 |  | ||||||
| 		attrs[4] = .{ |  | ||||||
| 			.format = .FLOAT2, |  | ||||||
| 			.buffer_index = vertex_indices.instance, |  | ||||||
| 		}; |  | ||||||
| 
 |  | ||||||
| 		attrs[5] = .{ |  | ||||||
| 			.format = .UBYTE4N, |  | ||||||
| 			.buffer_index = vertex_indices.instance, |  | ||||||
| 		}; |  | ||||||
| 
 |  | ||||||
| 		attrs[6] = .{ |  | ||||||
| 			.format = .FLOAT, |  | ||||||
| 			.buffer_index = vertex_indices.instance, |  | ||||||
| 		}; |  | ||||||
| 
 |  | ||||||
| 		attrs[7] = .{ |  | ||||||
| 			.format = .FLOAT4, |  | ||||||
| 			.buffer_index = vertex_indices.instance, |  | ||||||
| 		}; |  | ||||||
| 
 |  | ||||||
| 		break: get attrs; |  | ||||||
| 	}, |  | ||||||
| 
 |  | ||||||
| 	.buffers = get: { |  | ||||||
| 		var vertex_buffer_layouts = [_]sokol.gfx.VertexBufferLayoutState{.{}} ** 8; |  | ||||||
| 
 |  | ||||||
| 		vertex_buffer_layouts[vertex_indices.instance].step_func = .PER_INSTANCE; |  | ||||||
| 
 |  | ||||||
| 		break: get vertex_buffer_layouts; |  | ||||||
| 	}, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| fn run(window: *ext.SDL_Window) !void { | fn run(window: *ext.SDL_Window) !void { | ||||||
| 	const context = configure_and_create: { | 	const context = configure_and_create: { | ||||||
| 		var result = @as(c_int, 0); | 		var result = @as(c_int, 0); | ||||||
| @ -530,21 +549,6 @@ fn run(window: *ext.SDL_Window) !void { | |||||||
| 		.usage = .IMMUTABLE, | 		.usage = .IMMUTABLE, | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 	const shader_spirv = @embedFile("./shaders/2d_default.spv"); |  | ||||||
| 	var spirv_unit = try spirv.Unit.init(coral.heap.allocator); |  | ||||||
| 
 |  | ||||||
| 	defer spirv_unit.deinit(); |  | ||||||
| 
 |  | ||||||
| 	try spirv_unit.compile(shader_spirv, .vertex); |  | ||||||
| 	try spirv_unit.compile(shader_spirv, .fragment); |  | ||||||
| 
 |  | ||||||
| 	Frame.texture_batching_pipeline = sokol.gfx.makePipeline(.{ |  | ||||||
| 		.label = "2D drawing pipeline", |  | ||||||
| 		.layout = vertex_layout_state, |  | ||||||
| 		.shader = sokol.gfx.makeShader(spirv_unit.shader_desc), |  | ||||||
| 		.index_type = .UINT16, |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	var has_commands_head: ?*Commands = null; | 	var has_commands_head: ?*Commands = null; | ||||||
| 	var has_commands_tail: ?*Commands = null; | 	var has_commands_tail: ?*Commands = null; | ||||||
| 
 | 
 | ||||||
| @ -575,6 +579,14 @@ fn run(window: *ext.SDL_Window) !void { | |||||||
| 				} | 				} | ||||||
| 			}, | 			}, | ||||||
| 
 | 
 | ||||||
|  | 			.load_effect => |load| { | ||||||
|  | 				const effect = try pools.create_effect(load.desc); | ||||||
|  | 
 | ||||||
|  | 				if (!load.loaded.resolve(effect)) { | ||||||
|  | 					std.debug.assert(pools.destroy_effect(effect)); | ||||||
|  | 				} | ||||||
|  | 			}, | ||||||
|  | 
 | ||||||
| 			.load_texture => |load| { | 			.load_texture => |load| { | ||||||
| 				const texture = try pools.create_texture(load.desc); | 				const texture = try pools.create_texture(load.desc); | ||||||
| 
 | 
 | ||||||
| @ -650,6 +662,12 @@ fn run(window: *ext.SDL_Window) !void { | |||||||
| 				break; | 				break; | ||||||
| 			}, | 			}, | ||||||
| 
 | 
 | ||||||
|  | 			.unload_effect => |unload| { | ||||||
|  | 				if (!pools.destroy_effect(unload.handle)) { | ||||||
|  | 					@panic("Attempt to unload a non-existent effect"); | ||||||
|  | 				} | ||||||
|  | 			}, | ||||||
|  | 
 | ||||||
| 			.unload_texture => |unload| { | 			.unload_texture => |unload| { | ||||||
| 				if (!pools.destroy_texture(unload.handle)) { | 				if (!pools.destroy_texture(unload.handle)) { | ||||||
| 					@panic("Attempt to unload a non-existent texture"); | 					@panic("Attempt to unload a non-existent texture"); | ||||||
|  | |||||||
| @ -10,6 +10,140 @@ const spirv = @import("./spirv.zig"); | |||||||
| 
 | 
 | ||||||
| const std = @import("std"); | const std = @import("std"); | ||||||
| 
 | 
 | ||||||
|  | pub const Effect = struct { | ||||||
|  | 	shader: sokol.gfx.Shader, | ||||||
|  | 	pipeline: sokol.gfx.Pipeline, | ||||||
|  | 	has_properties_buffer: ?sokol.gfx.Buffer = null, | ||||||
|  | 
 | ||||||
|  | 	pub fn deinit(self: *Effect) void { | ||||||
|  | 		sokol.gfx.destroyPipeline(self.pipeline); | ||||||
|  | 		sokol.gfx.destroyShader(self.shader); | ||||||
|  | 
 | ||||||
|  | 		if (self.has_properties_buffer) |parameters_buffer| { | ||||||
|  | 			sokol.gfx.destroyBuffer(parameters_buffer); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		self.* = undefined; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	pub fn init(desc: handles.Effect.Desc) !Effect { | ||||||
|  | 		var spirv_unit = try spirv.Unit.init(coral.heap.allocator); | ||||||
|  | 
 | ||||||
|  | 		defer spirv_unit.deinit(); | ||||||
|  | 
 | ||||||
|  | 		try spirv_unit.compile(&spirv.to_ops(@embedFile("./shaders/2d_default.vert.spv")), .vertex); | ||||||
|  | 		try spirv_unit.compile(desc.fragment_spirv, .fragment); | ||||||
|  | 
 | ||||||
|  | 		const shader = sokol.gfx.makeShader(spirv_unit.shader_desc()); | ||||||
|  | 
 | ||||||
|  | 		const pipeline = sokol.gfx.makePipeline(pipeline_desc: { | ||||||
|  | 			var pipeline_desc = sokol.gfx.PipelineDesc{ | ||||||
|  | 				.label = "Effect pipeline", | ||||||
|  | 				.layout = vertex_layout_state, | ||||||
|  | 				.shader = shader, | ||||||
|  | 				.index_type = .UINT16, | ||||||
|  | 				.blend_color = .{.r = 1.0, .g = 1.0, .b = 1.0, .a = 1.0}, | ||||||
|  | 			}; | ||||||
|  | 
 | ||||||
|  | 			pipeline_desc.colors[0] = .{ | ||||||
|  | 				.write_mask = .RGBA, | ||||||
|  | 
 | ||||||
|  | 				.blend = .{ | ||||||
|  | 					.enabled = true, | ||||||
|  | 					.src_factor_rgb = .SRC_ALPHA, | ||||||
|  | 					.dst_factor_rgb = .ONE_MINUS_SRC_ALPHA, | ||||||
|  | 				}, | ||||||
|  | 			}; | ||||||
|  | 
 | ||||||
|  | 			break: pipeline_desc pipeline_desc; | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
|  | 		errdefer { | ||||||
|  | 			sokol.gfx.destroyPipeline(pipeline); | ||||||
|  | 			sokol.gfx.destroyShader(shader); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (spirv_unit.shader_stage(.fragment).has_storage_buffer[0]) |storage_buffer| { | ||||||
|  | 			const properties_buffer = sokol.gfx.makeBuffer(.{ | ||||||
|  | 				.size = storage_buffer.minimum_size, | ||||||
|  | 				.type = .STORAGEBUFFER, | ||||||
|  | 				.usage = .STREAM, | ||||||
|  | 			}); | ||||||
|  | 
 | ||||||
|  | 			errdefer { | ||||||
|  | 				sokol.gfx.destroyBuffer(properties_buffer); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return .{ | ||||||
|  | 				.shader = shader, | ||||||
|  | 				.pipeline = pipeline, | ||||||
|  | 				.has_properties_buffer = properties_buffer, | ||||||
|  | 			}; | ||||||
|  | 		} else { | ||||||
|  | 			return .{ | ||||||
|  | 				.shader = shader, | ||||||
|  | 				.pipeline = pipeline, | ||||||
|  | 			}; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	const vertex_layout_state = sokol.gfx.VertexLayoutState{ | ||||||
|  | 		.attrs = get: { | ||||||
|  | 			var attrs = [_]sokol.gfx.VertexAttrState{.{}} ** 16; | ||||||
|  | 
 | ||||||
|  | 			attrs[0] = .{ | ||||||
|  | 				.format = .FLOAT2, | ||||||
|  | 				.buffer_index = 0, | ||||||
|  | 			}; | ||||||
|  | 
 | ||||||
|  | 			attrs[1] = .{ | ||||||
|  | 				.format = .FLOAT2, | ||||||
|  | 				.buffer_index = 0, | ||||||
|  | 			}; | ||||||
|  | 
 | ||||||
|  | 			attrs[2] = .{ | ||||||
|  | 				.format = .FLOAT2, | ||||||
|  | 				.buffer_index = 1, | ||||||
|  | 			}; | ||||||
|  | 
 | ||||||
|  | 			attrs[3] = .{ | ||||||
|  | 				.format = .FLOAT2, | ||||||
|  | 				.buffer_index = 1, | ||||||
|  | 			}; | ||||||
|  | 
 | ||||||
|  | 			attrs[4] = .{ | ||||||
|  | 				.format = .FLOAT2, | ||||||
|  | 				.buffer_index = 1, | ||||||
|  | 			}; | ||||||
|  | 
 | ||||||
|  | 			attrs[5] = .{ | ||||||
|  | 				.format = .UBYTE4N, | ||||||
|  | 				.buffer_index = 1, | ||||||
|  | 			}; | ||||||
|  | 
 | ||||||
|  | 			attrs[6] = .{ | ||||||
|  | 				.format = .FLOAT, | ||||||
|  | 				.buffer_index = 1, | ||||||
|  | 			}; | ||||||
|  | 
 | ||||||
|  | 			attrs[7] = .{ | ||||||
|  | 				.format = .FLOAT4, | ||||||
|  | 				.buffer_index = 1, | ||||||
|  | 			}; | ||||||
|  | 
 | ||||||
|  | 			break: get attrs; | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
|  | 		.buffers = get: { | ||||||
|  | 			var vertex_buffer_layouts = [_]sokol.gfx.VertexBufferLayoutState{.{}} ** 8; | ||||||
|  | 
 | ||||||
|  | 			vertex_buffer_layouts[1].step_func = .PER_INSTANCE; | ||||||
|  | 
 | ||||||
|  | 			break: get vertex_buffer_layouts; | ||||||
|  | 		}, | ||||||
|  | 	}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| pub const Texture = union (enum) { | pub const Texture = union (enum) { | ||||||
| 	render: Render, | 	render: Render, | ||||||
| 	static: Static, | 	static: Static, | ||||||
|  | |||||||
| @ -7,10 +7,6 @@ layout (location = 1) in vec2 uv; | |||||||
| 
 | 
 | ||||||
| layout (location = 0) out vec4 texel; | layout (location = 0) out vec4 texel; | ||||||
| 
 | 
 | ||||||
| layout (binding = 0) readonly buffer View { |  | ||||||
| 	mat4 projection_matrix; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void main() { | void main() { | ||||||
| 	texel = texture(sprite, uv) * color; | 	texel = texture(sprite, uv) * color; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -15,22 +15,225 @@ pub const Error = std.mem.Allocator.Error || error { | |||||||
| 	UnsupportedBackend, | 	UnsupportedBackend, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| pub const Unit = struct { | pub const Shader = struct { | ||||||
| 	arena: std.heap.ArenaAllocator, | 	decompiled_source: [:0]const u8 = "", | ||||||
| 	context: ext.spvc_context, | 	has_storage_buffer: [max_storage_buffers]?StorageBuffer = [_]?StorageBuffer{null} ** max_storage_buffers, | ||||||
| 	shader_desc: sokol.gfx.ShaderDesc, | 	has_image: [max_images]?Image = [_]?Image{null} ** max_images, | ||||||
| 	attrs_used: u32 = 0, |     has_sampler: [max_samplers]?Sampler = [_]?Sampler{null} ** max_samplers, | ||||||
|  |     has_image_sampler_pair: [max_image_sampler_pairs]?ImageSamplerPair = [_]?ImageSamplerPair{null} ** max_image_sampler_pairs, | ||||||
| 
 | 
 | ||||||
| 	pub fn compile(self: *Unit, spirv_data: []const u8, stage: Stage) Error!void { | 	pub const Image = struct { | ||||||
| 		if ((spirv_data.len % @alignOf(u32)) != 0) { | 		is_multisampled: bool, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	pub const ImageSamplerPair = struct { | ||||||
|  | 		image_slot: usize, | ||||||
|  | 		sampler_slot: usize, | ||||||
|  | 		name: [:0]const u8, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	pub const Sampler = enum { | ||||||
|  | 		filtering, | ||||||
|  | 		non_filtering, | ||||||
|  | 		comparison, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	pub const StorageBuffer = struct { | ||||||
|  | 		minimum_size: usize, | ||||||
|  | 		is_readonly: bool, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	pub const max_images = 12; | ||||||
|  | 
 | ||||||
|  | 	pub const max_image_sampler_pairs = 12; | ||||||
|  | 
 | ||||||
|  | 	pub const max_samplers = 8; | ||||||
|  | 
 | ||||||
|  | 	pub const max_storage_buffers = 8; | ||||||
|  | 
 | ||||||
|  | 	fn reflect_image_samplers(self: *Shader, _: Stage, compiler: ext.spvc_compiler, resources: ext.spvc_resources) Error!void { | ||||||
|  | 		var sampled_images: []const ext.spvc_reflected_resource = &.{}; | ||||||
|  | 
 | ||||||
|  | 		try to_error(ext.spvc_resources_get_resource_list_for_type( | ||||||
|  | 			resources, | ||||||
|  | 			ext.SPVC_RESOURCE_TYPE_SAMPLED_IMAGE, | ||||||
|  | 			@ptrCast(&sampled_images.ptr), | ||||||
|  | 			&sampled_images.len, | ||||||
|  | 		)); | ||||||
|  | 
 | ||||||
|  | 		if (sampled_images.len > max_image_sampler_pairs) { | ||||||
|  | 			return error.UnsupportedSPIRV; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		for (0 .. sampled_images.len, sampled_images) |i, sampled_image| { | ||||||
|  | 			const sampled_image_type = ext.spvc_compiler_get_type_handle(compiler, sampled_image.type_id); | ||||||
|  | 
 | ||||||
|  | 			if (ext.spvc_type_get_basetype(sampled_image_type) != ext.SPVC_BASETYPE_SAMPLED_IMAGE) { | ||||||
|  | 				return error.InvalidSPIRV; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			try switch (ext.spvc_type_get_image_dimension(sampled_image_type)) { | ||||||
|  | 				ext.SpvDim2D => {}, | ||||||
|  | 				else => error.InvalidSPIRV, | ||||||
|  | 			}; | ||||||
|  | 
 | ||||||
|  | 			try switch (ext.spvc_type_get_basetype(ext.spvc_compiler_get_type_handle(compiler, ext.spvc_type_get_image_sampled_type(sampled_image_type)))) { | ||||||
|  | 				ext.SPVC_BASETYPE_FP32 => {}, | ||||||
|  | 				else => error.InvalidSPIRV, | ||||||
|  | 			}; | ||||||
|  | 
 | ||||||
|  | 			self.has_image[i] = .{ | ||||||
|  | 				.is_multisampled = ext.spvc_type_get_image_multisampled(sampled_image_type) != 0, | ||||||
|  | 			}; | ||||||
|  | 
 | ||||||
|  | 			self.has_sampler[i] = .filtering; | ||||||
|  | 
 | ||||||
|  | 			self.has_image_sampler_pair[i] = .{ | ||||||
|  | 				.name = std.mem.span(ext.spvc_compiler_get_name(compiler, sampled_image.id)), | ||||||
|  | 				.image_slot = @intCast(i), | ||||||
|  | 				.sampler_slot = @intCast(i), | ||||||
|  | 			}; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	fn reflect_storage_buffers(self: *Shader, stage: Stage, compiler: ext.spvc_compiler, resources: ext.spvc_resources) Error!void { | ||||||
|  | 		var storage_buffers: []const ext.spvc_reflected_resource = &.{}; | ||||||
|  | 
 | ||||||
|  | 		try to_error(ext.spvc_resources_get_resource_list_for_type( | ||||||
|  | 			resources, | ||||||
|  | 			ext.SPVC_RESOURCE_TYPE_STORAGE_BUFFER, | ||||||
|  | 			@ptrCast(&storage_buffers.ptr), | ||||||
|  | 			&storage_buffers.len, | ||||||
|  | 		)); | ||||||
|  | 
 | ||||||
|  | 		const binding_offset: c_uint = if (stage == .fragment) max_storage_buffers else 0; | ||||||
|  | 
 | ||||||
|  | 		for (storage_buffers) |storage_buffer| { | ||||||
|  | 			const binding = ext.spvc_compiler_get_decoration(compiler, storage_buffer.id, ext.SpvDecorationBinding); | ||||||
|  | 
 | ||||||
|  | 			if (binding >= max_storage_buffers) { | ||||||
|  | 				return error.InvalidSPIRV; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			ext.spvc_compiler_set_decoration(compiler, storage_buffer.id, ext.SpvDecorationBinding, binding_offset + binding); | ||||||
|  | 
 | ||||||
|  | 			var block_decorations: []const ext.SpvDecoration = &.{}; | ||||||
|  | 
 | ||||||
|  | 			try to_error(ext.spvc_compiler_get_buffer_block_decorations( | ||||||
|  | 				compiler, | ||||||
|  | 				storage_buffer.id, | ||||||
|  | 				@ptrCast(&block_decorations.ptr), | ||||||
|  | 				&block_decorations.len, | ||||||
|  | 			)); | ||||||
|  | 
 | ||||||
|  | 			var minimum_size: usize = 0; | ||||||
|  | 
 | ||||||
|  | 			try to_error(ext.spvc_compiler_get_declared_struct_size(compiler, ext.spvc_compiler_get_type_handle(compiler, storage_buffer.base_type_id), &minimum_size)); | ||||||
|  | 
 | ||||||
|  | 			self.has_storage_buffer[binding] = .{ | ||||||
|  | 				.is_readonly = std.mem.indexOfScalar(ext.SpvDecoration, block_decorations, ext.SpvDecorationNonWritable) != null, | ||||||
|  | 				.minimum_size = minimum_size, | ||||||
|  | 			}; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	fn reflect_uniform_buffers(self: *Shader, _: Stage, compiler: ext.spvc_compiler, resources: ext.spvc_resources) Error!void { | ||||||
|  | 		var uniform_buffers: []const ext.spvc_reflected_resource = &.{}; | ||||||
|  | 
 | ||||||
|  | 		try to_error(ext.spvc_resources_get_resource_list_for_type( | ||||||
|  | 			resources, | ||||||
|  | 			ext.SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, | ||||||
|  | 			@ptrCast(&uniform_buffers.ptr), | ||||||
|  | 			&uniform_buffers.len, | ||||||
|  | 		)); | ||||||
|  | 
 | ||||||
|  | 		if (uniform_buffers.len != 0) { | ||||||
| 			return error.InvalidSPIRV; | 			return error.InvalidSPIRV; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		const spirv_ops: []const u32 = @alignCast(std.mem.bytesAsSlice(u32, spirv_data)); | 		// TODO: Support for older APIs? | ||||||
|  | 		_ = self; | ||||||
|  | 		_ = compiler; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 		const execution_model, const stage_desc = switch (stage) { | 	pub fn stage_desc(self: Shader) sokol.gfx.ShaderStageDesc { | ||||||
| 			.vertex => .{ext.SpvExecutionModelVertex, &self.shader_desc.vs}, | 		var stage = sokol.gfx.ShaderStageDesc{ | ||||||
| 			.fragment => .{ext.SpvExecutionModelFragment, &self.shader_desc.fs}, | 			.entry = "main", | ||||||
|  | 			.source = self.decompiled_source, | ||||||
|  | 		}; | ||||||
|  | 
 | ||||||
|  | 		for (0 .. max_storage_buffers) |slot| { | ||||||
|  | 			const storage_buffer = &(self.has_storage_buffer[slot] orelse { | ||||||
|  | 				continue; | ||||||
|  | 			}); | ||||||
|  | 
 | ||||||
|  | 			stage.storage_buffers[slot] = .{ | ||||||
|  | 				.readonly = storage_buffer.is_readonly, | ||||||
|  | 				.used = true, | ||||||
|  | 			}; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		for (0 .. max_images) |slot| { | ||||||
|  | 			const image = &(self.has_image[slot] orelse { | ||||||
|  | 				continue; | ||||||
|  | 			}); | ||||||
|  | 
 | ||||||
|  | 			stage.images[slot] = .{ | ||||||
|  | 				.multisampled = image.is_multisampled, | ||||||
|  | 				.image_type = ._2D, | ||||||
|  | 				.sample_type = .FLOAT, | ||||||
|  | 				.used = true, | ||||||
|  | 			}; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		for (0 .. max_samplers) |slot| { | ||||||
|  | 			const sampler = &(self.has_sampler[slot] orelse { | ||||||
|  | 				continue; | ||||||
|  | 			}); | ||||||
|  | 
 | ||||||
|  | 			stage.samplers[slot] = .{ | ||||||
|  | 				.sampler_type = switch (sampler.*) { | ||||||
|  | 					.filtering => .FILTERING, | ||||||
|  | 					.non_filtering => .NONFILTERING, | ||||||
|  | 					.comparison => .COMPARISON, | ||||||
|  | 				}, | ||||||
|  | 
 | ||||||
|  | 				.used = true, | ||||||
|  | 			}; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		for (0 .. max_image_sampler_pairs) |slot| { | ||||||
|  | 			const image_sampler_pair = &(self.has_image_sampler_pair[slot] orelse { | ||||||
|  | 				continue; | ||||||
|  | 			}); | ||||||
|  | 
 | ||||||
|  | 			stage.image_sampler_pairs[slot] = .{ | ||||||
|  | 				.glsl_name = image_sampler_pair.name, | ||||||
|  | 				.image_slot = @intCast(image_sampler_pair.image_slot), | ||||||
|  | 				.sampler_slot = @intCast(image_sampler_pair.sampler_slot), | ||||||
|  | 				.used = true, | ||||||
|  | 			}; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		return stage; | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | pub const Stage = enum { | ||||||
|  | 	vertex, | ||||||
|  | 	fragment, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | pub const Unit = struct { | ||||||
|  | 	arena: std.heap.ArenaAllocator, | ||||||
|  | 	context: ext.spvc_context, | ||||||
|  | 	attrs_used: u32 = 0, | ||||||
|  | 	stages: [std.enums.values(Stage).len]Shader = .{.{}, .{}}, | ||||||
|  | 
 | ||||||
|  | 	pub fn compile(self: *Unit, spirv_ops: []const u32, stage: Stage) Error!void { | ||||||
|  | 		const execution_model, const shader = switch (stage) { | ||||||
|  | 			.vertex => .{ext.SpvExecutionModelVertex, &self.stages[0]}, | ||||||
|  | 			.fragment => .{ext.SpvExecutionModelFragment, &self.stages[1]}, | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 		const Backend = struct { | 		const Backend = struct { | ||||||
| @ -73,8 +276,8 @@ pub const Unit = struct { | |||||||
| 
 | 
 | ||||||
| 			for (combined_image_samplers) |combined_image_sampler| { | 			for (combined_image_samplers) |combined_image_sampler| { | ||||||
| 				const name = try coral.utf8.alloc_formatted(arena_allocator, "{image_name}_{sampler_name}", .{ | 				const name = try coral.utf8.alloc_formatted(arena_allocator, "{image_name}_{sampler_name}", .{ | ||||||
| 					.image_name = std.mem.span(@as([*:0]const u8, ext.spvc_compiler_get_name(compiler, combined_image_sampler.image_id))), | 					.image_name = std.mem.span(ext.spvc_compiler_get_name(compiler, combined_image_sampler.image_id)), | ||||||
| 					.sampler_name = std.mem.span(@as([*:0]const u8, ext.spvc_compiler_get_name(compiler, combined_image_sampler.sampler_id))), | 					.sampler_name = std.mem.span(ext.spvc_compiler_get_name(compiler, combined_image_sampler.sampler_id)), | ||||||
| 				}); | 				}); | ||||||
| 
 | 
 | ||||||
| 				ext.spvc_compiler_set_name(compiler, combined_image_sampler.combined_id, name); | 				ext.spvc_compiler_set_name(compiler, combined_image_sampler.combined_id, name); | ||||||
| @ -86,7 +289,7 @@ pub const Unit = struct { | |||||||
| 			break: parse_and_configure compiler; | 			break: parse_and_configure compiler; | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 		try to_error(ext.spvc_compiler_set_entry_point(compiler, stage_desc.entry, @intCast(execution_model))); | 		try to_error(ext.spvc_compiler_set_entry_point(compiler, "main", @intCast(execution_model))); | ||||||
| 
 | 
 | ||||||
| 		const resources = create: { | 		const resources = create: { | ||||||
| 			var resources: ext.spvc_resources = null; | 			var resources: ext.spvc_resources = null; | ||||||
| @ -96,27 +299,29 @@ pub const Unit = struct { | |||||||
| 			break: create resources; | 			break: create resources; | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 		try reflect_uniform_buffers(compiler, resources, stage_desc); | 		try shader.reflect_uniform_buffers(stage, compiler, resources); | ||||||
| 		try reflect_storage_buffers(compiler, resources, stage_desc); | 		try shader.reflect_storage_buffers(stage, compiler, resources); | ||||||
| 		try reflect_image_samplers(compiler, resources, stage_desc); | 		try shader.reflect_image_samplers(stage, compiler, resources); | ||||||
| 
 | 
 | ||||||
| 		try to_error(ext.spvc_compiler_install_compiler_options(compiler, create: { | 		try to_error(ext.spvc_compiler_install_compiler_options(compiler, create: { | ||||||
| 			var options: ext.spvc_compiler_options = null; | 			var compiler_options: ext.spvc_compiler_options = null; | ||||||
| 
 | 
 | ||||||
| 			try to_error(ext.spvc_compiler_create_compiler_options(compiler, &options)); | 			try to_error(ext.spvc_compiler_create_compiler_options(compiler, &compiler_options)); | ||||||
| 
 | 
 | ||||||
| 			for (backend.option_values) |option_value| { | 			for (backend.option_values) |option_value| { | ||||||
| 				const entry, const value = option_value; | 				const entry, const value = option_value; | ||||||
| 
 | 
 | ||||||
| 				try to_error(ext.spvc_compiler_options_set_uint(options, entry, value)); | 				try to_error(ext.spvc_compiler_options_set_uint(compiler_options, entry, value)); | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			break: create options; | 			break: create compiler_options; | ||||||
| 		})); | 		})); | ||||||
| 
 | 
 | ||||||
| 		try to_error(ext.spvc_compiler_compile(compiler, @ptrCast(&stage_desc.source))); | 		try to_error(ext.spvc_compiler_compile(compiler, @ptrCast(&shader.decompiled_source.ptr))); | ||||||
| 
 | 
 | ||||||
| 		std.log.info("{s}", .{stage_desc.source}); | 		shader.decompiled_source.len = std.mem.span(shader.decompiled_source.ptr).len; | ||||||
|  | 
 | ||||||
|  | 		std.log.info("{s}", .{shader.decompiled_source}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	pub fn deinit(self: *Unit) void { | 	pub fn deinit(self: *Unit) void { | ||||||
| @ -140,18 +345,19 @@ pub const Unit = struct { | |||||||
| 		return .{ | 		return .{ | ||||||
| 			.arena = std.heap.ArenaAllocator.init(allocator), | 			.arena = std.heap.ArenaAllocator.init(allocator), | ||||||
| 			.context = context, | 			.context = context, | ||||||
| 
 |  | ||||||
| 			.shader_desc = .{ |  | ||||||
| 				.vs = .{.entry = "main"}, |  | ||||||
| 				.fs = .{.entry = "main"}, |  | ||||||
| 			}, |  | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| pub const Stage = enum { | 	pub fn shader_desc(self: Unit) sokol.gfx.ShaderDesc { | ||||||
| 	fragment, | 		return .{ | ||||||
| 	vertex, | 			.vs = self.shader_stage(.vertex).stage_desc(), | ||||||
|  | 			.fs = self.shader_stage(.fragment).stage_desc(), | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	pub fn shader_stage(self: *const Unit, stage: Stage) *const Shader { | ||||||
|  | 		return &self.stages[@intFromEnum(stage)]; | ||||||
|  | 	} | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| fn log_context_errors(userdata: ?*anyopaque, error_message: [*c]const u8) callconv(.C) void { | fn log_context_errors(userdata: ?*anyopaque, error_message: [*c]const u8) callconv(.C) void { | ||||||
| @ -159,109 +365,6 @@ fn log_context_errors(userdata: ?*anyopaque, error_message: [*c]const u8) callco | |||||||
| 	std.log.err("{s}", .{error_message}); | 	std.log.err("{s}", .{error_message}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fn reflect_image_samplers(compiler: ext.spvc_compiler, resources: ext.spvc_resources, stage_desc: *sokol.gfx.ShaderStageDesc) Error!void { |  | ||||||
| 	var sampled_images: []const ext.spvc_reflected_resource = &.{}; |  | ||||||
| 
 |  | ||||||
| 	try to_error(ext.spvc_resources_get_resource_list_for_type( |  | ||||||
| 		resources, |  | ||||||
| 		ext.SPVC_RESOURCE_TYPE_SAMPLED_IMAGE, |  | ||||||
| 		@ptrCast(&sampled_images.ptr), |  | ||||||
| 		&sampled_images.len, |  | ||||||
| 	)); |  | ||||||
| 
 |  | ||||||
| 	if (sampled_images.len > stage_desc.image_sampler_pairs.len) { |  | ||||||
| 		return error.UnsupportedSPIRV; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for (0 .. sampled_images.len, sampled_images) |i, sampled_image| { |  | ||||||
| 		const sampled_image_type = ext.spvc_compiler_get_type_handle(compiler, sampled_image.type_id); |  | ||||||
| 
 |  | ||||||
| 		if (ext.spvc_type_get_basetype(sampled_image_type) != ext.SPVC_BASETYPE_SAMPLED_IMAGE) { |  | ||||||
| 			return error.InvalidSPIRV; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		stage_desc.images[i] = .{ |  | ||||||
| 			.multisampled = ext.spvc_type_get_image_multisampled(sampled_image_type) != 0, |  | ||||||
| 
 |  | ||||||
| 			.image_type = try switch (ext.spvc_type_get_image_dimension(sampled_image_type)) { |  | ||||||
| 				ext.SpvDim2D => sokol.gfx.ImageType._2D, |  | ||||||
| 				else => error.InvalidSPIRV, |  | ||||||
| 			}, |  | ||||||
| 
 |  | ||||||
| 			.sample_type = try switch (ext.spvc_type_get_basetype(ext.spvc_compiler_get_type_handle(compiler, ext.spvc_type_get_image_sampled_type(sampled_image_type)))) { |  | ||||||
| 				ext.SPVC_BASETYPE_FP32 => sokol.gfx.ImageSampleType.FLOAT, |  | ||||||
| 				else => error.InvalidSPIRV, |  | ||||||
| 			}, |  | ||||||
| 
 |  | ||||||
| 			.used = true, |  | ||||||
| 		}; |  | ||||||
| 
 |  | ||||||
| 		stage_desc.samplers[i] = .{ |  | ||||||
| 			.sampler_type = .DEFAULT, |  | ||||||
| 			.used = true, |  | ||||||
| 		}; |  | ||||||
| 
 |  | ||||||
| 		stage_desc.image_sampler_pairs[i] = .{ |  | ||||||
| 			.glsl_name = ext.spvc_compiler_get_name(compiler, sampled_image.id), |  | ||||||
| 			.image_slot = @intCast(i), |  | ||||||
| 			.sampler_slot = @intCast(i), |  | ||||||
| 			.used = true, |  | ||||||
| 		}; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn reflect_storage_buffers(compiler: ext.spvc_compiler, resources: ext.spvc_resources, stage_desc: *sokol.gfx.ShaderStageDesc) Error!void { |  | ||||||
| 	var storage_buffers: []const ext.spvc_reflected_resource = &.{}; |  | ||||||
| 
 |  | ||||||
| 	try to_error(ext.spvc_resources_get_resource_list_for_type( |  | ||||||
| 		resources, |  | ||||||
| 		ext.SPVC_RESOURCE_TYPE_STORAGE_BUFFER, |  | ||||||
| 		@ptrCast(&storage_buffers.ptr), |  | ||||||
| 		&storage_buffers.len, |  | ||||||
| 	)); |  | ||||||
| 
 |  | ||||||
| 	for (storage_buffers) |storage_buffer| { |  | ||||||
| 		const binding = ext.spvc_compiler_get_decoration(compiler, storage_buffer.id, ext.SpvDecorationBinding); |  | ||||||
| 
 |  | ||||||
| 		if (binding >= stage_desc.storage_buffers.len) { |  | ||||||
| 			return error.InvalidSPIRV; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		var block_decorations: []const ext.SpvDecoration = &.{}; |  | ||||||
| 
 |  | ||||||
| 		try to_error(ext.spvc_compiler_get_buffer_block_decorations( |  | ||||||
| 			compiler, |  | ||||||
| 			storage_buffer.id, |  | ||||||
| 			@ptrCast(&block_decorations.ptr), |  | ||||||
| 			&block_decorations.len, |  | ||||||
| 		)); |  | ||||||
| 
 |  | ||||||
| 		stage_desc.storage_buffers[binding] = .{ |  | ||||||
| 			.readonly = std.mem.indexOfScalar(ext.SpvDecoration, block_decorations, ext.SpvDecorationNonWritable) != null, |  | ||||||
| 			.used = true, |  | ||||||
| 		}; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn reflect_uniform_buffers(compiler: ext.spvc_compiler, resources: ext.spvc_resources, stage_desc: *sokol.gfx.ShaderStageDesc) Error!void { |  | ||||||
| 	var uniform_buffers: []const ext.spvc_reflected_resource = &.{}; |  | ||||||
| 
 |  | ||||||
| 	try to_error(ext.spvc_resources_get_resource_list_for_type( |  | ||||||
| 		resources, |  | ||||||
| 		ext.SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, |  | ||||||
| 		@ptrCast(&uniform_buffers.ptr), |  | ||||||
| 		&uniform_buffers.len, |  | ||||||
| 	)); |  | ||||||
| 
 |  | ||||||
| 	if (uniform_buffers.len != 0) { |  | ||||||
| 		return error.InvalidSPIRV; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// TODO: Support for older APIs? |  | ||||||
| 	_ = stage_desc; |  | ||||||
| 	_ = compiler; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn to_error(result: ext.spvc_result) Error!void { | fn to_error(result: ext.spvc_result) Error!void { | ||||||
| 	return switch (result) { | 	return switch (result) { | ||||||
| 		ext.SPVC_SUCCESS => {}, | 		ext.SPVC_SUCCESS => {}, | ||||||
| @ -272,3 +375,11 @@ fn to_error(result: ext.spvc_result) Error!void { | |||||||
| 		else => unreachable, | 		else => unreachable, | ||||||
| 	}; | 	}; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | pub fn to_ops(raw: anytype) [raw.len / @alignOf(u32)]u32 { | ||||||
|  | 	var ops: [raw.len / @alignOf(u32)]u32 = undefined; | ||||||
|  | 
 | ||||||
|  | 	@memcpy(std.mem.sliceAsBytes(&ops), raw); | ||||||
|  | 
 | ||||||
|  | 	return ops; | ||||||
|  | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user