renderer-mvp/post-processing #56
|
@ -10,6 +10,8 @@ pub const heap = @import("./heap.zig");
|
||||||
|
|
||||||
pub const io = @import("./io.zig");
|
pub const io = @import("./io.zig");
|
||||||
|
|
||||||
|
pub const lina = @import("./lina.zig");
|
||||||
|
|
||||||
pub const map = @import("./map.zig");
|
pub const map = @import("./map.zig");
|
||||||
|
|
||||||
pub const scalars = @import("./scalars.zig");
|
pub const scalars = @import("./scalars.zig");
|
||||||
|
@ -22,8 +24,6 @@ const std = @import("std");
|
||||||
|
|
||||||
pub const utf8 = @import("./utf8.zig");
|
pub const utf8 = @import("./utf8.zig");
|
||||||
|
|
||||||
pub const vectors = @import("./vectors.zig");
|
|
||||||
|
|
||||||
pub fn Pool(comptime Value: type) type {
|
pub fn Pool(comptime Value: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
entries: stack.Sequential(Entry),
|
entries: stack.Sequential(Entry),
|
||||||
|
|
|
@ -1,5 +1,18 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn Matrix(comptime n: usize, comptime Element: type) type {
|
||||||
|
return [n]@Vector(n, Element);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const ProjectionMatrix = Matrix(4, f32);
|
||||||
|
|
||||||
|
pub const Rect = struct {
|
||||||
|
left: f32,
|
||||||
|
top: f32,
|
||||||
|
right: f32,
|
||||||
|
bottom: f32,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn cross(v1: anytype, v2: anytype) @typeInfo(@TypeOf(v1, v2)).Vector.child {
|
pub fn cross(v1: anytype, v2: anytype) @typeInfo(@TypeOf(v1, v2)).Vector.child {
|
||||||
const multipled = v1 * v2;
|
const multipled = v1 * v2;
|
||||||
const vector_info = @typeInfo(@TypeOf(v1)).Vector;
|
const vector_info = @typeInfo(@TypeOf(v1)).Vector;
|
||||||
|
@ -48,3 +61,16 @@ pub fn normal(v: anytype) @TypeOf(v) {
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn orthographic_projection(near: f32, far: f32, viewport: Rect) Matrix(4, f32) {
|
||||||
|
const width = viewport.right - viewport.left;
|
||||||
|
const height = viewport.bottom - viewport.top;
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.{2 / width, 0, 0, 0},
|
||||||
|
.{0, 2 / height, 0, 0},
|
||||||
|
.{0, 0, 1 / (far - near), 0},
|
||||||
|
.{-((viewport.left + viewport.right) / width), -((viewport.top + viewport.bottom) / height), near / (near - far), 1},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
|
const flow = @import("./flow.zig");
|
||||||
|
|
||||||
const coral = @import("coral");
|
const coral = @import("coral");
|
||||||
|
|
||||||
const states = @import("./states.zig");
|
const states = @import("./states.zig");
|
||||||
|
@ -19,7 +21,7 @@ const Self = @This();
|
||||||
pub fn create_event(self: *Self, label: []const u8) std.mem.Allocator.Error!Event {
|
pub fn create_event(self: *Self, label: []const u8) std.mem.Allocator.Error!Event {
|
||||||
var systems = try system.Schedule.init(label);
|
var systems = try system.Schedule.init(label);
|
||||||
|
|
||||||
errdefer systems.deinit();
|
errdefer systems.deinit(self);
|
||||||
|
|
||||||
const index = self.event_systems.len();
|
const index = self.event_systems.len();
|
||||||
|
|
||||||
|
@ -34,7 +36,7 @@ pub fn deinit(self: *Self) void {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (self.event_systems.values) |*schedule| {
|
for (self.event_systems.values) |*schedule| {
|
||||||
schedule.deinit();
|
schedule.deinit(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.thread_pool) |thread_pool| {
|
if (self.thread_pool) |thread_pool| {
|
||||||
|
@ -47,11 +49,19 @@ pub fn deinit(self: *Self) void {
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_resource(self: Self, comptime State: type) ?*State {
|
pub fn get_params(self: Self, comptime Value: type) flow.Params(Value) {
|
||||||
return @ptrCast(@alignCast(self.thread_restricted_resources[@intFromEnum(states.thread_restriction(State))].get(State)));
|
const params = self.get_state(flow.Params(Value)) orelse {
|
||||||
|
return .{};
|
||||||
|
};
|
||||||
|
|
||||||
|
return params.*;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_get_resource(self: *Self, value: anytype) std.mem.Allocator.Error!*@TypeOf(value) {
|
pub fn get_state(self: Self, comptime Value: type) ?*Value {
|
||||||
|
return @ptrCast(@alignCast(self.thread_restricted_resources[@intFromEnum(states.thread_restriction(Value))].get(Value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_get_state(self: *Self, value: anytype) std.mem.Allocator.Error!*@TypeOf(value) {
|
||||||
return self.thread_restricted_resources[@intFromEnum(states.thread_restriction(@TypeOf(value)))].set_get(value);
|
return self.thread_restricted_resources[@intFromEnum(states.thread_restriction(@TypeOf(value)))].set_get(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,6 +93,6 @@ pub fn run_event(self: *Self, event: Event) anyerror!void {
|
||||||
try self.event_systems.values[@intFromEnum(event)].run(self);
|
try self.event_systems.values[@intFromEnum(event)].run(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_resource(self: *Self, value: anytype) std.mem.Allocator.Error!void {
|
pub fn set_state(self: *Self, value: anytype) std.mem.Allocator.Error!void {
|
||||||
try self.thread_restricted_resources[@intFromEnum(states.thread_restriction(@TypeOf(value)))].set(value);
|
try self.thread_restricted_resources[@intFromEnum(states.thread_restriction(@TypeOf(value)))].set(value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,45 @@ pub const system = @import("./system.zig");
|
||||||
|
|
||||||
pub const World = @import("./World.zig");
|
pub const World = @import("./World.zig");
|
||||||
|
|
||||||
|
pub const Exclusive = struct {
|
||||||
|
world: *World,
|
||||||
|
|
||||||
|
pub const Param = struct {
|
||||||
|
world: *World,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn bind(context: system.BindContext) std.mem.Allocator.Error!Param {
|
||||||
|
return .{
|
||||||
|
.world = context.world,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(param: *Param) Exclusive {
|
||||||
|
return .{
|
||||||
|
.world = param.world,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const thread_restriction = .main;
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn Params(comptime Value: type) type {
|
||||||
|
if (!@hasDecl(Value, "Param")) {
|
||||||
|
@compileError("System parameters must have a Params type declaration");
|
||||||
|
}
|
||||||
|
|
||||||
|
return struct {
|
||||||
|
has_head: ?*Node = null,
|
||||||
|
has_tail: ?*Node = null,
|
||||||
|
|
||||||
|
pub const Node = struct {
|
||||||
|
param: Value.Param,
|
||||||
|
has_prev: ?*Node = null,
|
||||||
|
has_next: ?*Node = null,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn Read(comptime Value: type) type {
|
pub fn Read(comptime Value: type) type {
|
||||||
return Shared(Value, .{
|
return Shared(Value, .{
|
||||||
.thread_restriction = states.thread_restriction(Value),
|
.thread_restriction = states.thread_restriction(Value),
|
||||||
|
@ -58,19 +97,19 @@ pub fn Shared(comptime Value: type, comptime info: ShareInfo) type {
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
pub const State = struct {
|
pub const Param = struct {
|
||||||
res: Qualified,
|
res: Qualified,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn bind(context: system.BindContext) std.mem.Allocator.Error!State {
|
pub fn bind(context: system.BindContext) std.mem.Allocator.Error!Param {
|
||||||
const thread_restriction_name = switch (info.thread_restriction) {
|
const thread_restriction_name = switch (info.thread_restriction) {
|
||||||
.main => "main thread-restricted ",
|
.main => "main thread-restricted ",
|
||||||
.none => ""
|
.none => ""
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = switch (info.read_only) {
|
const res = switch (info.read_only) {
|
||||||
true => (try context.register_readable_resource_access(Value)),
|
true => (try context.register_readable_state_access(Value)),
|
||||||
false => (try context.register_writable_resource_access(Value)),
|
false => (try context.register_writable_state_access(Value)),
|
||||||
};
|
};
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
|
@ -88,9 +127,9 @@ pub fn Shared(comptime Value: type, comptime info: ShareInfo) type {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(state: *State) Self {
|
pub fn init(param: *Param) Self {
|
||||||
return .{
|
return .{
|
||||||
.res = state.res,
|
.res = param.res,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -104,60 +143,71 @@ pub fn Write(comptime Value: type) type {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parameter_type(comptime Value: type) *const system.Info.Parameter {
|
fn parameter_type(comptime Value: type) *const system.Info.Parameter {
|
||||||
const has_state = @hasDecl(Value, "State");
|
const ValueParams = Params(Value);
|
||||||
|
|
||||||
if (@sizeOf(Value) == 0) {
|
if (@sizeOf(Value) == 0) {
|
||||||
@compileError("System parameters must have a non-zero size");
|
@compileError("System parameters must have a non-zero size");
|
||||||
}
|
}
|
||||||
|
|
||||||
const parameters = struct {
|
const parameters = struct {
|
||||||
fn bind(allocator: std.mem.Allocator, context: system.BindContext) std.mem.Allocator.Error!?*anyopaque {
|
fn bind(allocator: std.mem.Allocator, context: system.BindContext) std.mem.Allocator.Error!*anyopaque {
|
||||||
if (has_state) {
|
const value_name = @typeName(Value);
|
||||||
const value_name = @typeName(Value);
|
|
||||||
|
|
||||||
if (!@hasDecl(Value, "bind")) {
|
if (!@hasDecl(Value, "bind")) {
|
||||||
@compileError(
|
@compileError(
|
||||||
"a `bind` declaration on " ++
|
"a `bind` declaration on " ++
|
||||||
value_name ++
|
value_name ++
|
||||||
" is requied for parameter types with a `State` declaration");
|
" is requied for parameter types with a `Param` declaration");
|
||||||
}
|
|
||||||
|
|
||||||
const bind_type = @typeInfo(@TypeOf(Value.bind));
|
|
||||||
|
|
||||||
if (bind_type != .Fn) {
|
|
||||||
@compileError("`bind` declaration on " ++ value_name ++ " must be a fn");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bind_type.Fn.params.len != 1 or bind_type.Fn.params[0].type.? != system.BindContext) {
|
|
||||||
@compileError(
|
|
||||||
"`bind` fn on " ++
|
|
||||||
value_name ++
|
|
||||||
" must accept " ++
|
|
||||||
@typeName(system.BindContext) ++
|
|
||||||
" as it's one and only argument");
|
|
||||||
}
|
|
||||||
|
|
||||||
const state = try allocator.create(Value.State);
|
|
||||||
|
|
||||||
state.* = switch (bind_type.Fn.return_type.?) {
|
|
||||||
Value.State => Value.bind(context),
|
|
||||||
std.mem.Allocator.Error!Value.State => try Value.bind(context),
|
|
||||||
else => @compileError(
|
|
||||||
"`bind` fn on " ++
|
|
||||||
@typeName(Value) ++
|
|
||||||
" must return " ++
|
|
||||||
@typeName(Value.State) ++
|
|
||||||
" or " ++
|
|
||||||
@typeName(std.mem.Allocator.Error!Value.State)),
|
|
||||||
};
|
|
||||||
|
|
||||||
return @ptrCast(state);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bind_type = @typeInfo(@TypeOf(Value.bind));
|
||||||
|
|
||||||
|
if (bind_type != .Fn) {
|
||||||
|
@compileError("`bind` declaration on " ++ value_name ++ " must be a fn");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bind_type.Fn.params.len != 1 or bind_type.Fn.params[0].type.? != system.BindContext) {
|
||||||
|
@compileError(
|
||||||
|
"`bind` fn on " ++
|
||||||
|
value_name ++
|
||||||
|
" must accept " ++
|
||||||
|
@typeName(system.BindContext) ++
|
||||||
|
" as it's one and only argument");
|
||||||
|
}
|
||||||
|
|
||||||
|
const params_node = try allocator.create(ValueParams.Node);
|
||||||
|
|
||||||
|
params_node.* = .{
|
||||||
|
.param = switch (bind_type.Fn.return_type.?) {
|
||||||
|
Value.Param => Value.bind(context),
|
||||||
|
std.mem.Allocator.Error!Value.Param => try Value.bind(context),
|
||||||
|
|
||||||
|
else => @compileError(std.fmt.comptimePrint("`bind` fn on {s} must return {s} or {s}", .{
|
||||||
|
@typeName(Value),
|
||||||
|
@typeName(Value.Param),
|
||||||
|
@typeName(std.mem.Allocator.Error!Value.Param)
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (context.world.get_state(ValueParams)) |value_params| {
|
||||||
|
if (value_params.has_tail) |tail| {
|
||||||
|
tail.has_next = params_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
params_node.has_prev = value_params.has_tail;
|
||||||
|
value_params.has_tail = params_node;
|
||||||
|
} else {
|
||||||
|
try context.world.set_state(ValueParams{
|
||||||
|
.has_head = params_node,
|
||||||
|
.has_tail = params_node,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return @ptrCast(params_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(argument: *anyopaque, state: ?*anyopaque) void {
|
fn init(argument: *anyopaque, erased_node: *anyopaque) void {
|
||||||
const value_name = @typeName(Value);
|
const value_name = @typeName(Value);
|
||||||
|
|
||||||
if (!@hasDecl(Value, "init")) {
|
if (!@hasDecl(Value, "init")) {
|
||||||
|
@ -176,31 +226,37 @@ fn parameter_type(comptime Value: type) *const system.Info.Parameter {
|
||||||
|
|
||||||
const concrete_argument = @as(*Value, @ptrCast(@alignCast(argument)));
|
const concrete_argument = @as(*Value, @ptrCast(@alignCast(argument)));
|
||||||
|
|
||||||
if (has_state) {
|
if (init_type.Fn.params.len != 1 or init_type.Fn.params[0].type.? != *Value.Param) {
|
||||||
if (init_type.Fn.params.len != 1 or init_type.Fn.params[0].type.? != *Value.State) {
|
@compileError("`init` fn on " ++ value_name ++ " must accept a " ++ @typeName(*Value.Param));
|
||||||
@compileError("`init` fn on stateful " ++ value_name ++ " must accept a " ++ @typeName(*Value.State));
|
|
||||||
}
|
|
||||||
|
|
||||||
concrete_argument.* = Value.init(@ptrCast(@alignCast(state.?)));
|
|
||||||
} else {
|
|
||||||
if (init_type.Fn.params.len != 0) {
|
|
||||||
@compileError("`init` fn on statelss " ++ value_name ++ " cannot use parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
concrete_argument.* = Value.init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
concrete_argument.* = Value.init(&@as(*ValueParams.Node, @ptrCast(@alignCast(erased_node))).param);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unbind(allocator: std.mem.Allocator, state: ?*anyopaque) void {
|
fn unbind(allocator: std.mem.Allocator, erased_node: *anyopaque, context: system.UnbindContext) void {
|
||||||
if (@hasDecl(Value, "unbind")) {
|
if (@hasDecl(Value, "unbind")) {
|
||||||
if (has_state) {
|
const node = @as(*ValueParams.Node, @ptrCast(@alignCast(erased_node)));
|
||||||
const typed_state = @as(*Value.State, @ptrCast(@alignCast(state.?)));
|
|
||||||
|
|
||||||
Value.unbind(typed_state);
|
if (node.has_prev) |prev| {
|
||||||
allocator.destroy(typed_state);
|
prev.has_next = node.has_next;
|
||||||
} else {
|
|
||||||
Value.unbind();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (node.has_next) |next| {
|
||||||
|
next.has_prev = node.has_prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.world.get_state(ValueParams)) |params| {
|
||||||
|
if (node.has_prev == null) {
|
||||||
|
params.has_head = node.has_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.has_next == null) {
|
||||||
|
params.has_tail = node.has_prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Value.unbind(node);
|
||||||
|
allocator.destroy(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -224,7 +280,7 @@ pub fn system_fn(comptime call: anytype) *const system.Info {
|
||||||
}
|
}
|
||||||
|
|
||||||
const systems = struct {
|
const systems = struct {
|
||||||
fn run(parameters: []const *const system.Info.Parameter, data: *const [system.max_parameters]?*anyopaque) anyerror!void {
|
fn run(parameters: []const *const system.Info.Parameter, data: *const [system.max_parameters]*anyopaque) anyerror!void {
|
||||||
var call_args = @as(std.meta.ArgsTuple(Call), undefined);
|
var call_args = @as(std.meta.ArgsTuple(Call), undefined);
|
||||||
|
|
||||||
inline for (parameters, &call_args, data[0 .. parameters.len]) |parameter, *call_arg, state| {
|
inline for (parameters, &call_args, data[0 .. parameters.len]) |parameter, *call_arg, state| {
|
||||||
|
|
|
@ -37,8 +37,8 @@ pub const BindContext = struct {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_writable_resource_access(self: BindContext, comptime Resource: type) std.mem.Allocator.Error!?*Resource {
|
pub fn register_writable_state_access(self: BindContext, comptime Resource: type) std.mem.Allocator.Error!?*Resource {
|
||||||
const value = self.world.get_resource(Resource) orelse {
|
const value = self.world.get_state(Resource) orelse {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -65,8 +65,8 @@ pub const BindContext = struct {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_readable_resource_access(self: BindContext, comptime Resource: type) std.mem.Allocator.Error!?*const Resource {
|
pub fn register_readable_state_access(self: BindContext, comptime Resource: type) std.mem.Allocator.Error!?*const Resource {
|
||||||
const value = self.world.get_resource(Resource) orelse {
|
const value = self.world.get_state(Resource) orelse {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -95,16 +95,16 @@ pub const BindContext = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Info = struct {
|
pub const Info = struct {
|
||||||
execute: *const fn ([]const *const Parameter, *const [max_parameters]?*anyopaque) anyerror!void,
|
execute: *const fn ([]const *const Parameter, *const [max_parameters]*anyopaque) anyerror!void,
|
||||||
parameters: [max_parameters]*const Parameter = undefined,
|
parameters: [max_parameters]*const Parameter = undefined,
|
||||||
parameter_count: u4 = 0,
|
parameter_count: u4 = 0,
|
||||||
thread_restriction: states.ThreadRestriction = .none,
|
thread_restriction: states.ThreadRestriction = .none,
|
||||||
|
|
||||||
pub const Parameter = struct {
|
pub const Parameter = struct {
|
||||||
thread_restriction: states.ThreadRestriction,
|
thread_restriction: states.ThreadRestriction,
|
||||||
init: *const fn (*anyopaque, ?*anyopaque) void,
|
init: *const fn (*anyopaque, *anyopaque) void,
|
||||||
bind: *const fn (std.mem.Allocator, BindContext) std.mem.Allocator.Error!?*anyopaque,
|
bind: *const fn (std.mem.Allocator, BindContext) std.mem.Allocator.Error!*anyopaque,
|
||||||
unbind: *const fn (std.mem.Allocator, ?*anyopaque) void,
|
unbind: *const fn (std.mem.Allocator, *anyopaque, UnbindContext) void,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn used_parameters(self: *const Info) []const *const Parameter {
|
pub fn used_parameters(self: *const Info) []const *const Parameter {
|
||||||
|
@ -142,7 +142,7 @@ pub const Schedule = struct {
|
||||||
info: *const Info,
|
info: *const Info,
|
||||||
label: [:0]u8,
|
label: [:0]u8,
|
||||||
dependencies: []Dependency,
|
dependencies: []Dependency,
|
||||||
parameter_states: [max_parameters]?*anyopaque = [_]?*anyopaque{null} ** max_parameters,
|
parameter_states: [max_parameters]*anyopaque = [_]*anyopaque{undefined} ** max_parameters,
|
||||||
resource_accesses: coral.stack.Sequential(ResourceAccess),
|
resource_accesses: coral.stack.Sequential(ResourceAccess),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -157,7 +157,7 @@ pub const Schedule = struct {
|
||||||
|
|
||||||
const ResourceNodeBundle = coral.map.Hashed(states.TypeID, NodeBundle, coral.map.enum_traits(states.TypeID));
|
const ResourceNodeBundle = coral.map.Hashed(states.TypeID, NodeBundle, coral.map.enum_traits(states.TypeID));
|
||||||
|
|
||||||
pub fn deinit(self: *Schedule) void {
|
pub fn deinit(self: *Schedule, world: *World) void {
|
||||||
{
|
{
|
||||||
var nodes = self.system_id_nodes.entries();
|
var nodes = self.system_id_nodes.entries();
|
||||||
|
|
||||||
|
@ -188,7 +188,9 @@ pub const Schedule = struct {
|
||||||
const system = self.graph.get_ptr(node).?;
|
const system = self.graph.get_ptr(node).?;
|
||||||
|
|
||||||
for (system.info.used_parameters(), system.parameter_states[0 .. system.info.parameter_count]) |parameter, state| {
|
for (system.info.used_parameters(), system.parameter_states[0 .. system.info.parameter_count]) |parameter, state| {
|
||||||
parameter.unbind(self.arena.allocator(), state);
|
parameter.unbind(self.arena.allocator(), state, .{
|
||||||
|
.world = world,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
system.resource_accesses.deinit();
|
system.resource_accesses.deinit();
|
||||||
|
@ -467,9 +469,9 @@ pub const Schedule = struct {
|
||||||
|
|
||||||
errdefer {
|
errdefer {
|
||||||
for (info.used_parameters(), system.parameter_states[0 .. info.parameter_count]) |parameter, state| {
|
for (info.used_parameters(), system.parameter_states[0 .. info.parameter_count]) |parameter, state| {
|
||||||
if (state) |initialized_state| {
|
parameter.unbind(self.arena.allocator(), state, .{
|
||||||
parameter.unbind(self.arena.allocator(), initialized_state);
|
.world = world,
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std.debug.assert(self.graph.remove_node(node) != null);
|
std.debug.assert(self.graph.remove_node(node) != null);
|
||||||
|
@ -489,4 +491,8 @@ pub const Schedule = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const UnbindContext = struct {
|
||||||
|
world: *World,
|
||||||
|
};
|
||||||
|
|
||||||
pub const max_parameters = 16;
|
pub const max_parameters = 16;
|
||||||
|
|
16
src/main.zig
16
src/main.zig
|
@ -80,10 +80,14 @@ fn render(commands: ona.gfx.Commands, actors: ona.Write(Actors), app: ona.Read(o
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
try commands.set_effect(actors.res.crt_effect, CRT{
|
try commands.set_effect(.{
|
||||||
.width = 1280,
|
.effect = actors.res.crt_effect,
|
||||||
.height = 720,
|
|
||||||
.time = @floatCast(app.res.elapsed_time),
|
.properties = std.mem.asBytes(&CRT{
|
||||||
|
.width = 1280,
|
||||||
|
.height = 720,
|
||||||
|
.time = @floatCast(app.res.elapsed_time),
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
try commands.set_target(.{
|
try commands.set_target(.{
|
||||||
|
@ -112,8 +116,8 @@ fn update(player: ona.Read(Player), actors: ona.Write(Actors), mapping: ona.Read
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup(world: *ona.World, events: ona.App.Events) !void {
|
fn setup(world: *ona.World, events: ona.App.Events) !void {
|
||||||
try world.set_resource(Actors{});
|
try world.set_state(Actors{});
|
||||||
try world.set_resource(Player{});
|
try world.set_state(Player{});
|
||||||
|
|
||||||
try world.on_event(events.load, ona.system_fn(load), .{.label = "load"});
|
try world.on_event(events.load, ona.system_fn(load), .{.label = "load"});
|
||||||
try world.on_event(events.update, ona.system_fn(update), .{.label = "update"});
|
try world.on_event(events.update, ona.system_fn(update), .{.label = "update"});
|
||||||
|
|
|
@ -38,7 +38,7 @@ pub const Mapping = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn setup(world: *flow.World, events: App.Events) std.mem.Allocator.Error!void {
|
pub fn setup(world: *flow.World, events: App.Events) std.mem.Allocator.Error!void {
|
||||||
try world.set_resource(Mapping{});
|
try world.set_state(Mapping{});
|
||||||
|
|
||||||
try world.on_event(events.pre_update, flow.system_fn(update), .{
|
try world.on_event(events.pre_update, flow.system_fn(update), .{
|
||||||
.label = "update act",
|
.label = "update act",
|
||||||
|
|
294
src/ona/gfx.zig
294
src/ona/gfx.zig
|
@ -10,15 +10,9 @@ const ext = @import("./ext.zig");
|
||||||
|
|
||||||
const flow = @import("flow");
|
const flow = @import("flow");
|
||||||
|
|
||||||
const handles = @import("./gfx/handles.zig");
|
|
||||||
|
|
||||||
pub const lina = @import("./gfx/lina.zig");
|
|
||||||
|
|
||||||
const msg = @import("./msg.zig");
|
const msg = @import("./msg.zig");
|
||||||
|
|
||||||
const spirv = @import("./gfx/spirv.zig");
|
const sokol = @import("./gfx/sokol.zig");
|
||||||
|
|
||||||
const rendering = @import("./gfx/rendering.zig");
|
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
@ -26,6 +20,8 @@ pub const Assets = struct {
|
||||||
window: *ext.SDL_Window,
|
window: *ext.SDL_Window,
|
||||||
texture_formats: coral.stack.Sequential(TextureFormat),
|
texture_formats: coral.stack.Sequential(TextureFormat),
|
||||||
frame_rendered: std.Thread.ResetEvent = .{},
|
frame_rendered: std.Thread.ResetEvent = .{},
|
||||||
|
pending_work: WorkQueue = .{},
|
||||||
|
has_worker_thread: ?std.Thread = null,
|
||||||
|
|
||||||
pub const LoadError = std.mem.Allocator.Error;
|
pub const LoadError = std.mem.Allocator.Error;
|
||||||
|
|
||||||
|
@ -38,9 +34,51 @@ pub const Assets = struct {
|
||||||
load_file: *const fn (*std.heap.ArenaAllocator, coral.files.Storage, []const u8) LoadFileError!Texture.Desc,
|
load_file: *const fn (*std.heap.ArenaAllocator, coral.files.Storage, []const u8) LoadFileError!Texture.Desc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const WorkQueue = coral.asyncio.BlockingQueue(1024, union (enum) {
|
||||||
|
load_effect: LoadEffectWork,
|
||||||
|
load_texture: LoadTextureWork,
|
||||||
|
render_frame: RenderFrameWork,
|
||||||
|
shutdown,
|
||||||
|
unload_effect: UnloadEffectWork,
|
||||||
|
unload_texture: UnloadTextureWork,
|
||||||
|
|
||||||
|
const LoadEffectWork = struct {
|
||||||
|
desc: Effect.Desc,
|
||||||
|
loaded: *coral.asyncio.Future(std.mem.Allocator.Error!Effect),
|
||||||
|
};
|
||||||
|
|
||||||
|
const LoadTextureWork = struct {
|
||||||
|
desc: Texture.Desc,
|
||||||
|
loaded: *coral.asyncio.Future(std.mem.Allocator.Error!Texture),
|
||||||
|
};
|
||||||
|
|
||||||
|
const RenderFrameWork = struct {
|
||||||
|
clear_color: Color,
|
||||||
|
width: u16,
|
||||||
|
height: u16,
|
||||||
|
finished: *std.Thread.ResetEvent,
|
||||||
|
has_command_params: ?*flow.Params(Commands).Node,
|
||||||
|
};
|
||||||
|
|
||||||
|
const UnloadEffectWork = struct {
|
||||||
|
handle: Effect,
|
||||||
|
};
|
||||||
|
|
||||||
|
const UnloadTextureWork = struct {
|
||||||
|
handle: Texture,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
fn deinit(self: *Assets) void {
|
fn deinit(self: *Assets) void {
|
||||||
rendering.enqueue_work(.shutdown);
|
self.pending_work.enqueue(.shutdown);
|
||||||
|
|
||||||
|
if (self.has_worker_thread) |worker_thread| {
|
||||||
|
worker_thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
self.texture_formats.deinit();
|
self.texture_formats.deinit();
|
||||||
|
|
||||||
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init() !Assets {
|
fn init() !Assets {
|
||||||
|
@ -59,15 +97,13 @@ pub const Assets = struct {
|
||||||
ext.SDL_DestroyWindow(window);
|
ext.SDL_DestroyWindow(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
try rendering.startup(window);
|
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.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 {
|
pub fn load_effect_file(self: *Assets, storage: coral.files.Storage, path: []const u8) LoadFileError!Effect {
|
||||||
if (!std.mem.endsWith(u8, path, ".spv")) {
|
if (!std.mem.endsWith(u8, path, ".spv")) {
|
||||||
return error.FormatUnsupported;
|
return error.FormatUnsupported;
|
||||||
}
|
}
|
||||||
|
@ -85,7 +121,7 @@ pub const Assets = struct {
|
||||||
|
|
||||||
var loaded = coral.asyncio.Future(std.mem.Allocator.Error!Effect){};
|
var loaded = coral.asyncio.Future(std.mem.Allocator.Error!Effect){};
|
||||||
|
|
||||||
rendering.enqueue_work(.{
|
self.pending_work.enqueue(.{
|
||||||
.load_effect = .{
|
.load_effect = .{
|
||||||
.desc = .{
|
.desc = .{
|
||||||
.fragment_spirv_ops = fragment_spirv_ops,
|
.fragment_spirv_ops = fragment_spirv_ops,
|
||||||
|
@ -98,10 +134,10 @@ pub const Assets = struct {
|
||||||
return loaded.get().*;
|
return loaded.get().*;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_texture(_: *Assets, desc: Texture.Desc) std.mem.Allocator.Error!Texture {
|
pub fn load_texture(self: *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){};
|
||||||
|
|
||||||
rendering.enqueue_work(.{
|
self.pending_work.enqueue(.{
|
||||||
.load_texture = .{
|
.load_texture = .{
|
||||||
.desc = desc,
|
.desc = desc,
|
||||||
.loaded = &loaded,
|
.loaded = &loaded,
|
||||||
|
@ -132,55 +168,127 @@ pub const Assets = struct {
|
||||||
pub const thread_restriction = .main;
|
pub const thread_restriction = .main;
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Commands = struct {
|
pub const Color = @Vector(4, f32);
|
||||||
list: *rendering.Commands.List,
|
|
||||||
|
|
||||||
pub const State = struct {
|
pub const Commands = struct {
|
||||||
commands: *rendering.Commands,
|
pending: *List,
|
||||||
|
|
||||||
|
const Command = union (enum) {
|
||||||
|
draw_texture: DrawTextureCommand,
|
||||||
|
set_effect: SetEffectCommand,
|
||||||
|
set_target: SetTargetCommand,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn bind(_: flow.system.BindContext) std.mem.Allocator.Error!State {
|
pub const DrawTextureCommand = struct {
|
||||||
var created_commands = coral.asyncio.Future(std.mem.Allocator.Error!*rendering.Commands){};
|
texture: Texture,
|
||||||
|
transform: Transform2D,
|
||||||
|
};
|
||||||
|
|
||||||
rendering.enqueue_work(.{
|
pub const SetEffectCommand = struct {
|
||||||
.create_commands = .{
|
effect: Effect,
|
||||||
.created = &created_commands,
|
properties: []const coral.io.Byte,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const SetTargetCommand = struct {
|
||||||
|
texture: Texture,
|
||||||
|
clear_color: ?Color,
|
||||||
|
clear_depth: ?f32,
|
||||||
|
clear_stencil: ?u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const List = struct {
|
||||||
|
arena: std.heap.ArenaAllocator,
|
||||||
|
stack: coral.stack.Sequential(Command),
|
||||||
|
|
||||||
|
fn clear(self: *List) void {
|
||||||
|
self.stack.clear();
|
||||||
|
|
||||||
|
if (!self.arena.reset(.retain_capacity)) {
|
||||||
|
std.log.warn("failed to reset the buffer of a gfx queue with retained capacity", .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deinit(self: *List) void {
|
||||||
|
self.arena.deinit();
|
||||||
|
self.stack.deinit();
|
||||||
|
|
||||||
|
self.* = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(allocator: std.mem.Allocator) List {
|
||||||
|
return .{
|
||||||
|
.arena = std.heap.ArenaAllocator.init(allocator),
|
||||||
|
.stack = .{.allocator = allocator},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Param = struct {
|
||||||
|
swap_lists: [2]List,
|
||||||
|
swap_state: u1 = 0,
|
||||||
|
|
||||||
|
fn deinit(self: *Param) void {
|
||||||
|
for (&self.swap_lists) |*list| {
|
||||||
|
list.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.* = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pending_list(self: *Param) *List {
|
||||||
|
return &self.swap_lists[self.swap_state];
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rotate(self: *Param) void {
|
||||||
|
const swapped_state = self.swap_state ^ 1;
|
||||||
|
|
||||||
|
self.swap_lists[swapped_state].clear();
|
||||||
|
|
||||||
|
self.swap_state = swapped_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn submitted_commands(self: Param) []const Command {
|
||||||
|
return self.swap_lists[self.swap_state ^ 1].stack.values;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn bind(_: flow.system.BindContext) std.mem.Allocator.Error!Param {
|
||||||
|
return .{
|
||||||
|
.swap_lists = .{
|
||||||
|
List.init(coral.heap.allocator),
|
||||||
|
List.init(coral.heap.allocator),
|
||||||
},
|
},
|
||||||
});
|
|
||||||
|
|
||||||
return .{
|
|
||||||
.commands = try created_commands.get().*,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(state: *State) Commands {
|
pub fn init(state: *Param) Commands {
|
||||||
return .{
|
return .{
|
||||||
.list = state.commands.pending_list(),
|
.pending = state.pending_list(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_texture(self: Commands, command: rendering.Command.DrawTexture) std.mem.Allocator.Error!void {
|
pub fn draw_texture(self: Commands, command: DrawTextureCommand) std.mem.Allocator.Error!void {
|
||||||
try self.list.append(.{.draw_texture = command});
|
try self.pending.stack.push_grow(.{.draw_texture = command});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_effect(self: Commands, effect: handles.Effect, properties: anytype) std.mem.Allocator.Error!void {
|
pub fn set_effect(self: Commands, command: SetEffectCommand) std.mem.Allocator.Error!void {
|
||||||
try self.list.append(.{
|
try self.pending.stack.push_grow(.{
|
||||||
.set_effect = .{
|
.set_effect = .{
|
||||||
.properties = std.mem.asBytes(&properties),
|
.properties = try self.pending.arena.allocator().dupe(coral.io.Byte, command.properties),
|
||||||
.effect = effect,
|
.effect = command.effect,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_target(self: Commands, command: rendering.Command.SetTarget) std.mem.Allocator.Error!void {
|
pub fn set_target(self: Commands, command: SetTargetCommand) std.mem.Allocator.Error!void {
|
||||||
try self.list.append(.{.set_target = command});
|
try self.pending.stack.push_grow(.{.set_target = command});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Config = struct {
|
pub const Config = struct {
|
||||||
width: u16 = 1280,
|
width: u16 = 1280,
|
||||||
height: u16 = 720,
|
height: u16 = 720,
|
||||||
clear_color: lina.Color = colors.black,
|
clear_color: Color = colors.black,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Input = union (enum) {
|
pub const Input = union (enum) {
|
||||||
|
@ -199,9 +307,59 @@ pub const Input = union (enum) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Effect = handles.Effect;
|
pub const Effect = enum (u32) {
|
||||||
|
default,
|
||||||
|
_,
|
||||||
|
|
||||||
pub const Texture = handles.Texture;
|
pub const Desc = struct {
|
||||||
|
fragment_spirv_ops: []const u32,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Texture = enum (u32) {
|
||||||
|
default,
|
||||||
|
backbuffer,
|
||||||
|
_,
|
||||||
|
|
||||||
|
pub const Desc = struct {
|
||||||
|
format: Format,
|
||||||
|
access: Access,
|
||||||
|
|
||||||
|
pub const Access = union (enum) {
|
||||||
|
static: StaticAccess,
|
||||||
|
render: RenderAccess,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const StaticAccess = struct {
|
||||||
|
width: u16,
|
||||||
|
data: []const coral.io.Byte,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const RenderAccess = struct {
|
||||||
|
width: u16,
|
||||||
|
height: u16,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Format = enum {
|
||||||
|
rgba8,
|
||||||
|
bgra8,
|
||||||
|
|
||||||
|
pub fn byte_size(self: Format) usize {
|
||||||
|
return switch (self) {
|
||||||
|
.rgba8, .bgra8 => 4,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Transform2D = extern struct {
|
||||||
|
xbasis: Vector = .{1, 0},
|
||||||
|
ybasis: Vector = .{0, 1},
|
||||||
|
origin: Vector = @splat(0),
|
||||||
|
|
||||||
|
const Vector = @Vector(2, f32);
|
||||||
|
};
|
||||||
|
|
||||||
pub fn poll(app: flow.Write(App), inputs: msg.Send(Input)) !void {
|
pub fn poll(app: flow.Write(App), inputs: msg.Send(Input)) !void {
|
||||||
var event = @as(ext.SDL_Event, undefined);
|
var event = @as(ext.SDL_Event, undefined);
|
||||||
|
@ -228,7 +386,7 @@ pub fn setup(world: *flow.World, events: App.Events) (error {Unsupported} || std
|
||||||
assets.deinit();
|
assets.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
break: create try world.set_get_resource(assets);
|
break: create try world.set_get_state(assets);
|
||||||
};
|
};
|
||||||
|
|
||||||
assets.frame_rendered.set();
|
assets.frame_rendered.set();
|
||||||
|
@ -237,6 +395,8 @@ pub fn setup(world: *flow.World, events: App.Events) (error {Unsupported} || std
|
||||||
assets.deinit();
|
assets.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assets.has_worker_thread = try std.Thread.spawn(.{}, sokol.run, .{&assets.pending_work, assets.window});
|
||||||
|
|
||||||
const builtin_texture_formats = [_]Assets.TextureFormat{
|
const builtin_texture_formats = [_]Assets.TextureFormat{
|
||||||
.{
|
.{
|
||||||
.extension = "bmp",
|
.extension = "bmp",
|
||||||
|
@ -248,7 +408,7 @@ pub fn setup(world: *flow.World, events: App.Events) (error {Unsupported} || std
|
||||||
try assets.texture_formats.push_grow(format);
|
try assets.texture_formats.push_grow(format);
|
||||||
}
|
}
|
||||||
|
|
||||||
try world.set_resource(Config{});
|
try world.set_state(Config{});
|
||||||
try world.on_event(events.pre_update, flow.system_fn(poll), .{.label = "poll gfx"});
|
try world.on_event(events.pre_update, flow.system_fn(poll), .{.label = "poll gfx"});
|
||||||
try world.on_event(events.exit, flow.system_fn(stop), .{.label = "stop gfx"});
|
try world.on_event(events.exit, flow.system_fn(stop), .{.label = "stop gfx"});
|
||||||
try world.on_event(events.finish, flow.system_fn(synchronize), .{.label = "synchronize gfx"});
|
try world.on_event(events.finish, flow.system_fn(synchronize), .{.label = "synchronize gfx"});
|
||||||
|
@ -258,34 +418,40 @@ pub fn stop(assets: flow.Write(Assets)) void {
|
||||||
assets.res.deinit();
|
assets.res.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn synchronize(assets: flow.Write(Assets), config: flow.Read(Config)) !void {
|
pub fn synchronize(exclusive: flow.Exclusive) !void {
|
||||||
assets.res.frame_rendered.wait();
|
const assets = exclusive.world.get_state(Assets).?;
|
||||||
assets.res.frame_rendered.reset();
|
const config = exclusive.world.get_state(Config).?;
|
||||||
|
|
||||||
var commands_swapped = std.Thread.ResetEvent{};
|
assets.frame_rendered.wait();
|
||||||
|
assets.frame_rendered.reset();
|
||||||
|
|
||||||
rendering.enqueue_work(.{
|
{
|
||||||
.rotate_commands = .{
|
var has_command_param = exclusive.world.get_params(Commands).has_head;
|
||||||
.finished = &commands_swapped,
|
|
||||||
},
|
while (has_command_param) |command_param| : (has_command_param = command_param.has_next) {
|
||||||
});
|
command_param.param.rotate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var display_width, var display_height = [_]c_int{0, 0};
|
var display_width, var display_height = [_]c_int{0, 0};
|
||||||
|
|
||||||
ext.SDL_GL_GetDrawableSize(assets.res.window, &display_width, &display_height);
|
ext.SDL_GL_GetDrawableSize(assets.window, &display_width, &display_height);
|
||||||
|
|
||||||
if (config.res.width != display_width or config.res.height != display_height) {
|
if (config.width != display_width or config.height != display_height) {
|
||||||
ext.SDL_SetWindowSize(assets.res.window, config.res.width, config.res.height);
|
ext.SDL_SetWindowSize(assets.window, config.width, config.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
commands_swapped.wait();
|
if (exclusive.world.get_params(Commands).has_head) |command_param| {
|
||||||
|
assets.pending_work.enqueue(.{
|
||||||
rendering.enqueue_work(.{
|
.render_frame = .{
|
||||||
.render_frame = .{
|
.has_command_params = command_param,
|
||||||
.width = config.res.width,
|
.width = config.width,
|
||||||
.height = config.res.height,
|
.height = config.height,
|
||||||
.clear_color = config.res.clear_color,
|
.clear_color = config.clear_color,
|
||||||
.finished = &assets.res.frame_rendered,
|
.finished = &assets.frame_rendered,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
assets.frame_rendered.set();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
const coral = @import("coral");
|
const coral = @import("coral");
|
||||||
|
|
||||||
const handles = @import("./handles.zig");
|
const gfx = @import("../gfx.zig");
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
pub fn load_file(arena: *std.heap.ArenaAllocator, storage: coral.files.Storage, path: []const u8) !handles.Texture.Desc {
|
pub fn load_file(arena: *std.heap.ArenaAllocator, storage: coral.files.Storage, path: []const u8) !gfx.Texture.Desc {
|
||||||
const header = try storage.read_little(path, 0, extern struct {
|
const header = try storage.read_little(path, 0, extern struct {
|
||||||
type: [2]u8 align (1),
|
type: [2]u8 align (1),
|
||||||
file_size: u32 align (1),
|
file_size: u32 align (1),
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
const lina = @import("./lina.zig");
|
const gfx = @import("../gfx.zig");
|
||||||
|
|
||||||
pub const black = greyscale(0);
|
pub const black = greyscale(0);
|
||||||
|
|
||||||
pub const white = greyscale(1);
|
pub const white = greyscale(1);
|
||||||
|
|
||||||
pub fn greyscale(v: f32) lina.Color {
|
pub fn greyscale(v: f32) gfx.Color {
|
||||||
return .{v, v, v, 1};
|
return .{v, v, v, 1};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rgb(r: f32, g: f32, b: f32) lina.Color {
|
pub fn rgb(r: f32, g: f32, b: f32) gfx.Color {
|
||||||
return .{r, g, b, 1};
|
return .{r, g, b, 1};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
const coral = @import("coral");
|
|
||||||
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub const Effect = Handle(struct {
|
|
||||||
fragment_spirv_ops: []const u32,
|
|
||||||
});
|
|
||||||
|
|
||||||
fn Handle(comptime HandleDesc: type) type {
|
|
||||||
return enum (u32) {
|
|
||||||
default,
|
|
||||||
_,
|
|
||||||
|
|
||||||
pub const Desc = HandleDesc;
|
|
||||||
|
|
||||||
const Self = @This();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const Texture = enum (u32) {
|
|
||||||
default,
|
|
||||||
backbuffer,
|
|
||||||
_,
|
|
||||||
|
|
||||||
pub const Desc = struct {
|
|
||||||
format: Format,
|
|
||||||
access: Access,
|
|
||||||
|
|
||||||
pub const Access = union (enum) {
|
|
||||||
static: Static,
|
|
||||||
render: Render,
|
|
||||||
|
|
||||||
pub const Static = struct {
|
|
||||||
width: u16,
|
|
||||||
data: []const coral.io.Byte,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Render = struct {
|
|
||||||
width: u16,
|
|
||||||
height: u16,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Format = enum {
|
|
||||||
rgba8,
|
|
||||||
bgra8,
|
|
||||||
|
|
||||||
pub fn byte_size(self: Format) usize {
|
|
||||||
return switch (self) {
|
|
||||||
.rgba8, .bgra8 => 4,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
pub const Color = @Vector(4, f32);
|
|
||||||
|
|
||||||
pub fn EvenOrderMatrix(comptime n: usize, comptime Element: type) type {
|
|
||||||
return [n]@Vector(n, Element);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const Transform2D = extern struct {
|
|
||||||
xbasis: Vector = .{1, 0},
|
|
||||||
ybasis: Vector = .{0, 1},
|
|
||||||
origin: Vector = @splat(0),
|
|
||||||
|
|
||||||
const Vector = @Vector(2, f32);
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const ProjectionMatrix = EvenOrderMatrix(4, f32);
|
|
||||||
|
|
||||||
pub const Rect = struct {
|
|
||||||
left: f32,
|
|
||||||
top: f32,
|
|
||||||
right: f32,
|
|
||||||
bottom: f32,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn orthographic_projection(near: f32, far: f32, viewport: Rect) EvenOrderMatrix(4, f32) {
|
|
||||||
const width = viewport.right - viewport.left;
|
|
||||||
const height = viewport.bottom - viewport.top;
|
|
||||||
|
|
||||||
return .{
|
|
||||||
.{2 / width, 0, 0, 0},
|
|
||||||
.{0, 2 / height, 0, 0},
|
|
||||||
.{0, 0, 1 / (far - near), 0},
|
|
||||||
.{-((viewport.left + viewport.right) / width), -((viewport.top + viewport.bottom) / height), near / (near - far), 1},
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,380 +0,0 @@
|
||||||
const coral = @import("coral");
|
|
||||||
|
|
||||||
const handles = @import("./handles.zig");
|
|
||||||
|
|
||||||
const lina = @import("./lina.zig");
|
|
||||||
|
|
||||||
const sokol = @import("sokol");
|
|
||||||
|
|
||||||
const spirv = @import("./spirv.zig");
|
|
||||||
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub const Effect = struct {
|
|
||||||
shader: sokol.gfx.Shader,
|
|
||||||
pipeline: sokol.gfx.Pipeline,
|
|
||||||
properties: []coral.io.Byte,
|
|
||||||
|
|
||||||
pub fn deinit(self: *Effect) void {
|
|
||||||
coral.heap.allocator.free(self.properties);
|
|
||||||
sokol.gfx.destroyPipeline(self.pipeline);
|
|
||||||
sokol.gfx.destroyShader(self.shader);
|
|
||||||
|
|
||||||
self.* = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(desc: handles.Effect.Desc) spirv.Error!Effect {
|
|
||||||
var spirv_arena = std.heap.ArenaAllocator.init(coral.heap.allocator);
|
|
||||||
|
|
||||||
defer {
|
|
||||||
spirv_arena.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
const spirv_program = try spirv.analyze(&spirv_arena, .{
|
|
||||||
.target = try switch (sokol.gfx.queryBackend()) {
|
|
||||||
.GLCORE => spirv.Target.glsl,
|
|
||||||
else => error.InvalidSPIRV,
|
|
||||||
},
|
|
||||||
|
|
||||||
.vertex_source = .{
|
|
||||||
.ops = &spirv.to_ops(@embedFile("./shaders/2d_default.vert.spv")),
|
|
||||||
},
|
|
||||||
|
|
||||||
.fragment_source = .{
|
|
||||||
.ops = desc.fragment_spirv_ops,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const shader = sokol.gfx.makeShader(shader_desc: {
|
|
||||||
const shader_desc = sokol.gfx.ShaderDesc{
|
|
||||||
.vs = stage_desc(spirv_program.vertex_stage),
|
|
||||||
.fs = stage_desc(spirv_program.fragment_stage),
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Vertex attributes, for some reason they aren't needed?
|
|
||||||
|
|
||||||
break: shader_desc shader_desc;
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: Review blending rules.
|
|
||||||
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;
|
|
||||||
});
|
|
||||||
|
|
||||||
const properties = try coral.heap.allocator.alloc(
|
|
||||||
coral.io.Byte,
|
|
||||||
if (spirv_program.fragment_stage.has_uniform_blocks[0]) |uniform_block| uniform_block.size() else 0,
|
|
||||||
);
|
|
||||||
|
|
||||||
errdefer {
|
|
||||||
coral.heap.allocator.free(properties);
|
|
||||||
sokol.gfx.destroyPipeline(pipeline);
|
|
||||||
sokol.gfx.destroyShader(shader);
|
|
||||||
}
|
|
||||||
|
|
||||||
return .{
|
|
||||||
.shader = shader,
|
|
||||||
.pipeline = pipeline,
|
|
||||||
.properties = properties,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn stage_desc(spirv_stage: spirv.Stage) sokol.gfx.ShaderStageDesc {
|
|
||||||
var stage = sokol.gfx.ShaderStageDesc{
|
|
||||||
.entry = spirv_stage.entry_point,
|
|
||||||
.source = spirv_stage.source,
|
|
||||||
};
|
|
||||||
|
|
||||||
for (0 .. spirv.Stage.max_uniform_blocks) |slot| {
|
|
||||||
const uniform_block = &(spirv_stage.has_uniform_blocks[slot] orelse {
|
|
||||||
continue;
|
|
||||||
});
|
|
||||||
|
|
||||||
const stage_uniform_block = &stage.uniform_blocks[slot];
|
|
||||||
|
|
||||||
stage_uniform_block.layout = switch (uniform_block.layout) {
|
|
||||||
.std140 => .STD140,
|
|
||||||
};
|
|
||||||
|
|
||||||
stage_uniform_block.size = uniform_block.size();
|
|
||||||
|
|
||||||
for (stage_uniform_block.uniforms[0 .. uniform_block.uniforms.len], uniform_block.uniforms) |*stage_uniform, uniform| {
|
|
||||||
stage_uniform.* = .{
|
|
||||||
.type = switch (uniform.type) {
|
|
||||||
.float => .FLOAT,
|
|
||||||
.float2 => .FLOAT2,
|
|
||||||
.float3 => .FLOAT3,
|
|
||||||
.float4 => .FLOAT4,
|
|
||||||
.integer => .INT,
|
|
||||||
.integer2 => .INT2,
|
|
||||||
.integer3 => .INT3,
|
|
||||||
.integer4 => .INT4,
|
|
||||||
.matrix4 => .MAT4,
|
|
||||||
},
|
|
||||||
|
|
||||||
.name = uniform.name,
|
|
||||||
.array_count = uniform.len,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (0 .. spirv.Stage.max_images) |slot| {
|
|
||||||
const image = &(spirv_stage.has_images[slot] orelse {
|
|
||||||
continue;
|
|
||||||
});
|
|
||||||
|
|
||||||
stage.images[slot] = .{
|
|
||||||
.multisampled = image.is_multisampled,
|
|
||||||
.image_type = ._2D,
|
|
||||||
.sample_type = .FLOAT,
|
|
||||||
.used = true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
for (0 .. spirv.Stage.max_samplers) |slot| {
|
|
||||||
const sampler = &(spirv_stage.has_samplers[slot] orelse {
|
|
||||||
continue;
|
|
||||||
});
|
|
||||||
|
|
||||||
stage.samplers[slot] = .{
|
|
||||||
.sampler_type = switch (sampler.*) {
|
|
||||||
.filtering => .FILTERING,
|
|
||||||
.non_filtering => .NONFILTERING,
|
|
||||||
.comparison => .COMPARISON,
|
|
||||||
},
|
|
||||||
|
|
||||||
.used = true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
for (0 .. spirv.Stage.max_image_sampler_pairs) |slot| {
|
|
||||||
const image_sampler_pair = &(spirv_stage.has_image_sampler_pairs[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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = struct {
|
|
||||||
width: u16,
|
|
||||||
height: u16,
|
|
||||||
access: Access,
|
|
||||||
|
|
||||||
pub const Access = union (enum) {
|
|
||||||
empty,
|
|
||||||
render: RenderAccess,
|
|
||||||
static: StaticAccess,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const RenderAccess = struct {
|
|
||||||
color_image: sokol.gfx.Image,
|
|
||||||
depth_image: sokol.gfx.Image,
|
|
||||||
attachments: sokol.gfx.Attachments,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const StaticAccess = struct {
|
|
||||||
image: sokol.gfx.Image,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn deinit(self: *Texture) void {
|
|
||||||
switch (self.access) {
|
|
||||||
.render => |render| {
|
|
||||||
sokol.gfx.destroyImage(render.color_image);
|
|
||||||
sokol.gfx.destroyImage(render.depth_image);
|
|
||||||
sokol.gfx.destroyAttachments(render.attachments);
|
|
||||||
},
|
|
||||||
|
|
||||||
.static => |static| {
|
|
||||||
sokol.gfx.destroyImage(static.image);
|
|
||||||
},
|
|
||||||
|
|
||||||
.empty => {},
|
|
||||||
}
|
|
||||||
|
|
||||||
self.* = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(desc: handles.Texture.Desc) std.mem.Allocator.Error!Texture {
|
|
||||||
const pixel_format = switch (desc.format) {
|
|
||||||
.rgba8 => sokol.gfx.PixelFormat.RGBA8,
|
|
||||||
.bgra8 => sokol.gfx.PixelFormat.BGRA8,
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (desc.access) {
|
|
||||||
.render => |render| {
|
|
||||||
if (render.width == 0 or render.height == 0) {
|
|
||||||
return .{
|
|
||||||
.width = render.width,
|
|
||||||
.height = render.height,
|
|
||||||
.access = .empty,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const color_image = sokol.gfx.makeImage(.{
|
|
||||||
.pixel_format = pixel_format,
|
|
||||||
.width = render.width,
|
|
||||||
.height = render.height,
|
|
||||||
.render_target = true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const depth_image = sokol.gfx.makeImage(.{
|
|
||||||
.width = render.width,
|
|
||||||
.height = render.height,
|
|
||||||
.render_target = true,
|
|
||||||
.pixel_format = .DEPTH_STENCIL,
|
|
||||||
});
|
|
||||||
|
|
||||||
const attachments = sokol.gfx.makeAttachments(attachments_desc: {
|
|
||||||
var attachments_desc = sokol.gfx.AttachmentsDesc{
|
|
||||||
.depth_stencil = .{
|
|
||||||
.image = depth_image,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
attachments_desc.colors[0] = .{
|
|
||||||
.image = color_image,
|
|
||||||
};
|
|
||||||
|
|
||||||
break: attachments_desc attachments_desc;
|
|
||||||
});
|
|
||||||
|
|
||||||
return .{
|
|
||||||
.width = render.width,
|
|
||||||
.height = render.height,
|
|
||||||
|
|
||||||
.access = .{
|
|
||||||
.render = .{
|
|
||||||
.attachments = attachments,
|
|
||||||
.color_image = color_image,
|
|
||||||
.depth_image = depth_image,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
.static => |static| {
|
|
||||||
const height = std.math.cast(u16, static.data.len / (static.width * desc.format.byte_size())) orelse {
|
|
||||||
return error.OutOfMemory;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (static.width == 0 or height == 0) {
|
|
||||||
return .{
|
|
||||||
.width = static.width,
|
|
||||||
.height = height,
|
|
||||||
.access = .empty,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const image = sokol.gfx.makeImage(image_desc: {
|
|
||||||
var image_desc = sokol.gfx.ImageDesc{
|
|
||||||
.height = height,
|
|
||||||
.pixel_format = pixel_format,
|
|
||||||
.width = static.width,
|
|
||||||
};
|
|
||||||
|
|
||||||
image_desc.data.subimage[0][0] = sokol.gfx.asRange(static.data);
|
|
||||||
|
|
||||||
break: image_desc image_desc;
|
|
||||||
});
|
|
||||||
|
|
||||||
errdefer {
|
|
||||||
sokol.gfx.destroyImage(image);
|
|
||||||
}
|
|
||||||
|
|
||||||
return .{
|
|
||||||
.width = static.width,
|
|
||||||
.height = height,
|
|
||||||
|
|
||||||
.access = .{
|
|
||||||
.static = .{
|
|
||||||
.image = image,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -2,11 +2,7 @@ const coral = @import("coral");
|
||||||
|
|
||||||
const ext = @import("../ext.zig");
|
const ext = @import("../ext.zig");
|
||||||
|
|
||||||
const handles = @import("./handles.zig");
|
const gfx = @import("../gfx.zig");
|
||||||
|
|
||||||
const lina = @import("./lina.zig");
|
|
||||||
|
|
||||||
const resources = @import("./resources.zig");
|
|
||||||
|
|
||||||
const spirv = @import("./spirv.zig");
|
const spirv = @import("./spirv.zig");
|
||||||
|
|
||||||
|
@ -14,139 +10,23 @@ const sokol = @import("sokol");
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
pub const Command = union (enum) {
|
|
||||||
draw_texture: DrawTexture,
|
|
||||||
set_effect: SetEffect,
|
|
||||||
set_target: SetTarget,
|
|
||||||
|
|
||||||
pub const DrawTexture = struct {
|
|
||||||
texture: handles.Texture,
|
|
||||||
transform: lina.Transform2D,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const SetEffect = struct {
|
|
||||||
effect: handles.Effect,
|
|
||||||
properties: []const coral.io.Byte,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const SetTarget = struct {
|
|
||||||
texture: handles.Texture,
|
|
||||||
clear_color: ?lina.Color,
|
|
||||||
clear_depth: ?f32,
|
|
||||||
clear_stencil: ?u8,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn clone(self: Command, arena: *std.heap.ArenaAllocator) !Command {
|
|
||||||
return switch (self) {
|
|
||||||
.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},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process(self: Command, pools: *Pools, frame: *Frame) !void {
|
|
||||||
try switch (self) {
|
|
||||||
.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),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Commands = struct {
|
|
||||||
swap_lists: [2]List,
|
|
||||||
swap_state: u1 = 0,
|
|
||||||
has_next: ?*Commands = null,
|
|
||||||
has_prev: ?*Commands = null,
|
|
||||||
|
|
||||||
pub const List = struct {
|
|
||||||
arena: std.heap.ArenaAllocator,
|
|
||||||
stack: coral.stack.Sequential(Command),
|
|
||||||
|
|
||||||
pub fn append(self: *List, command: Command) std.mem.Allocator.Error!void {
|
|
||||||
return self.stack.push_grow(try command.clone(&self.arena));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear(self: *List) void {
|
|
||||||
self.stack.clear();
|
|
||||||
|
|
||||||
if (!self.arena.reset(.retain_capacity)) {
|
|
||||||
std.log.warn("failed to reset the buffer of a gfx queue with retained capacity", .{});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deinit(self: *List) void {
|
|
||||||
self.arena.deinit();
|
|
||||||
self.stack.deinit();
|
|
||||||
|
|
||||||
self.* = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init(allocator: std.mem.Allocator) List {
|
|
||||||
return .{
|
|
||||||
.arena = std.heap.ArenaAllocator.init(allocator),
|
|
||||||
.stack = .{.allocator = allocator},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
pub fn deinit(self: *Self) void {
|
|
||||||
for (&self.swap_lists) |*list| {
|
|
||||||
list.deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.* = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator) Self {
|
|
||||||
return .{
|
|
||||||
.swap_lists = .{List.init(allocator), List.init(allocator)},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pending_list(self: *Self) *List {
|
|
||||||
return &self.swap_lists[self.swap_state];
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rotate(self: *Self) void {
|
|
||||||
const swapped_state = self.swap_state ^ 1;
|
|
||||||
|
|
||||||
self.swap_lists[swapped_state].clear();
|
|
||||||
|
|
||||||
self.swap_state = swapped_state;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn submitted_commands(self: *const Self) []const Command {
|
|
||||||
return self.swap_lists[self.swap_state ^ 1].stack.values;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const Frame = struct {
|
const Frame = struct {
|
||||||
swapchain: sokol.gfx.Swapchain,
|
swapchain: sokol.gfx.Swapchain,
|
||||||
drawn_count: usize = 0,
|
drawn_count: usize = 0,
|
||||||
flushed_count: usize = 0,
|
flushed_count: usize = 0,
|
||||||
current_source_texture: handles.Texture = .default,
|
current_source_texture: gfx.Texture = .default,
|
||||||
current_target_texture: handles.Texture = .backbuffer,
|
current_target_texture: gfx.Texture = .backbuffer,
|
||||||
current_effect: handles.Effect = .default,
|
current_effect: gfx.Effect = .default,
|
||||||
|
|
||||||
const DrawTexture = extern struct {
|
const DrawTexture = extern struct {
|
||||||
transform: lina.Transform2D,
|
transform: gfx.Transform2D,
|
||||||
tint: @Vector(4, u8) = @splat(std.math.maxInt(u8)),
|
tint: @Vector(4, u8) = @splat(std.math.maxInt(u8)),
|
||||||
depth: f32 = 0,
|
depth: f32 = 0,
|
||||||
texture_offset: @Vector(2, f32) = @splat(0),
|
texture_offset: @Vector(2, f32) = @splat(0),
|
||||||
texture_size: @Vector(2, f32) = @splat(1),
|
texture_size: @Vector(2, f32) = @splat(1),
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn draw_texture(self: *Frame, pools: *Pools, command: Command.DrawTexture) !void {
|
pub fn draw_texture(self: *Frame, pools: *Resources, command: gfx.Commands.DrawTextureCommand) !void {
|
||||||
if (command.texture != self.current_source_texture) {
|
if (command.texture != self.current_source_texture) {
|
||||||
self.flush(pools);
|
self.flush(pools);
|
||||||
}
|
}
|
||||||
|
@ -174,7 +54,7 @@ const Frame = struct {
|
||||||
self.drawn_count += 1;
|
self.drawn_count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flush(self: *Frame, pools: *Pools) void {
|
pub fn flush(self: *Frame, pools: *Resources) void {
|
||||||
if (self.flushed_count == self.drawn_count) {
|
if (self.flushed_count == self.drawn_count) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -207,7 +87,7 @@ const Frame = struct {
|
||||||
|
|
||||||
const texture = pools.get_texture(self.current_target_texture).?;
|
const texture = pools.get_texture(self.current_target_texture).?;
|
||||||
|
|
||||||
sokol.gfx.applyUniforms(.VS, 0, sokol.gfx.asRange(&lina.orthographic_projection(-1.0, 1.0, .{
|
sokol.gfx.applyUniforms(.VS, 0, sokol.gfx.asRange(&coral.lina.orthographic_projection(-1.0, 1.0, .{
|
||||||
.left = 0,
|
.left = 0,
|
||||||
.top = 0,
|
.top = 0,
|
||||||
.right = @floatFromInt(texture.width),
|
.right = @floatFromInt(texture.width),
|
||||||
|
@ -236,7 +116,7 @@ const Frame = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_effect(self: *Frame, pools: *Pools, command: Command.SetEffect) void {
|
pub fn set_effect(self: *Frame, pools: *Resources, command: gfx.Commands.SetEffectCommand) void {
|
||||||
if (command.effect != self.current_effect) {
|
if (command.effect != self.current_effect) {
|
||||||
self.flush(pools);
|
self.flush(pools);
|
||||||
}
|
}
|
||||||
|
@ -248,7 +128,7 @@ const Frame = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_target(self: *Frame, pools: *Pools, command: Command.SetTarget) void {
|
pub fn set_target(self: *Frame, pools: *Resources, command: gfx.Commands.SetTargetCommand) void {
|
||||||
sokol.gfx.endPass();
|
sokol.gfx.endPass();
|
||||||
|
|
||||||
var pass = sokol.gfx.Pass{
|
var pass = sokol.gfx.Pass{
|
||||||
|
@ -298,31 +178,401 @@ const Frame = struct {
|
||||||
const batches_per_buffer = 512;
|
const batches_per_buffer = 512;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Pools = struct {
|
const Resources = struct {
|
||||||
effects: EffectPool,
|
effects: EffectPool,
|
||||||
textures: TexturePool,
|
textures: TexturePool,
|
||||||
|
|
||||||
const EffectPool = coral.Pool(resources.Effect);
|
pub const Effect = struct {
|
||||||
|
shader: sokol.gfx.Shader,
|
||||||
|
pipeline: sokol.gfx.Pipeline,
|
||||||
|
properties: []coral.io.Byte,
|
||||||
|
|
||||||
const TexturePool = coral.Pool(resources.Texture);
|
pub fn deinit(self: *Effect) void {
|
||||||
|
coral.heap.allocator.free(self.properties);
|
||||||
|
sokol.gfx.destroyPipeline(self.pipeline);
|
||||||
|
sokol.gfx.destroyShader(self.shader);
|
||||||
|
|
||||||
pub fn create_effect(self: *Pools, desc: handles.Effect.Desc) !handles.Effect {
|
self.* = undefined;
|
||||||
var effect = try resources.Effect.init(desc);
|
}
|
||||||
|
|
||||||
|
pub fn init(desc: gfx.Effect.Desc) spirv.Error!Effect {
|
||||||
|
var spirv_arena = std.heap.ArenaAllocator.init(coral.heap.allocator);
|
||||||
|
|
||||||
|
defer {
|
||||||
|
spirv_arena.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
const spirv_program = try spirv.analyze(&spirv_arena, .{
|
||||||
|
.target = try switch (sokol.gfx.queryBackend()) {
|
||||||
|
.GLCORE => spirv.Target.glsl,
|
||||||
|
else => error.InvalidSPIRV,
|
||||||
|
},
|
||||||
|
|
||||||
|
.vertex_source = .{
|
||||||
|
.ops = &spirv.to_ops(@embedFile("./shaders/2d_default.vert.spv")),
|
||||||
|
},
|
||||||
|
|
||||||
|
.fragment_source = .{
|
||||||
|
.ops = desc.fragment_spirv_ops,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const shader = sokol.gfx.makeShader(shader_desc: {
|
||||||
|
const shader_desc = sokol.gfx.ShaderDesc{
|
||||||
|
.vs = stage_desc(spirv_program.vertex_stage),
|
||||||
|
.fs = stage_desc(spirv_program.fragment_stage),
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Vertex attributes, for some reason they aren't needed?
|
||||||
|
|
||||||
|
break: shader_desc shader_desc;
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: Review blending rules.
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
|
||||||
|
const properties = try coral.heap.allocator.alloc(
|
||||||
|
coral.io.Byte,
|
||||||
|
if (spirv_program.fragment_stage.has_uniform_blocks[0]) |uniform_block| uniform_block.size() else 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
errdefer {
|
||||||
|
coral.heap.allocator.free(properties);
|
||||||
|
sokol.gfx.destroyPipeline(pipeline);
|
||||||
|
sokol.gfx.destroyShader(shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.shader = shader,
|
||||||
|
.pipeline = pipeline,
|
||||||
|
.properties = properties,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stage_desc(spirv_stage: spirv.Stage) sokol.gfx.ShaderStageDesc {
|
||||||
|
var stage = sokol.gfx.ShaderStageDesc{
|
||||||
|
.entry = spirv_stage.entry_point,
|
||||||
|
.source = spirv_stage.source,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (0 .. spirv.Stage.max_uniform_blocks) |slot| {
|
||||||
|
const uniform_block = &(spirv_stage.has_uniform_blocks[slot] orelse {
|
||||||
|
continue;
|
||||||
|
});
|
||||||
|
|
||||||
|
const stage_uniform_block = &stage.uniform_blocks[slot];
|
||||||
|
|
||||||
|
stage_uniform_block.layout = switch (uniform_block.layout) {
|
||||||
|
.std140 => .STD140,
|
||||||
|
};
|
||||||
|
|
||||||
|
stage_uniform_block.size = uniform_block.size();
|
||||||
|
|
||||||
|
for (stage_uniform_block.uniforms[0 .. uniform_block.uniforms.len], uniform_block.uniforms) |*stage_uniform, uniform| {
|
||||||
|
stage_uniform.* = .{
|
||||||
|
.type = switch (uniform.type) {
|
||||||
|
.float => .FLOAT,
|
||||||
|
.float2 => .FLOAT2,
|
||||||
|
.float3 => .FLOAT3,
|
||||||
|
.float4 => .FLOAT4,
|
||||||
|
.integer => .INT,
|
||||||
|
.integer2 => .INT2,
|
||||||
|
.integer3 => .INT3,
|
||||||
|
.integer4 => .INT4,
|
||||||
|
.matrix4 => .MAT4,
|
||||||
|
},
|
||||||
|
|
||||||
|
.name = uniform.name,
|
||||||
|
.array_count = uniform.len,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (0 .. spirv.Stage.max_images) |slot| {
|
||||||
|
const image = &(spirv_stage.has_images[slot] orelse {
|
||||||
|
continue;
|
||||||
|
});
|
||||||
|
|
||||||
|
stage.images[slot] = .{
|
||||||
|
.multisampled = image.is_multisampled,
|
||||||
|
.image_type = ._2D,
|
||||||
|
.sample_type = .FLOAT,
|
||||||
|
.used = true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (0 .. spirv.Stage.max_samplers) |slot| {
|
||||||
|
const sampler = &(spirv_stage.has_samplers[slot] orelse {
|
||||||
|
continue;
|
||||||
|
});
|
||||||
|
|
||||||
|
stage.samplers[slot] = .{
|
||||||
|
.sampler_type = switch (sampler.*) {
|
||||||
|
.filtering => .FILTERING,
|
||||||
|
.non_filtering => .NONFILTERING,
|
||||||
|
.comparison => .COMPARISON,
|
||||||
|
},
|
||||||
|
|
||||||
|
.used = true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (0 .. spirv.Stage.max_image_sampler_pairs) |slot| {
|
||||||
|
const image_sampler_pair = &(spirv_stage.has_image_sampler_pairs[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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = struct {
|
||||||
|
width: u16,
|
||||||
|
height: u16,
|
||||||
|
access: Access,
|
||||||
|
|
||||||
|
pub const Access = union (enum) {
|
||||||
|
empty,
|
||||||
|
render: RenderAccess,
|
||||||
|
static: StaticAccess,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const RenderAccess = struct {
|
||||||
|
color_image: sokol.gfx.Image,
|
||||||
|
depth_image: sokol.gfx.Image,
|
||||||
|
attachments: sokol.gfx.Attachments,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const StaticAccess = struct {
|
||||||
|
image: sokol.gfx.Image,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn deinit(self: *Texture) void {
|
||||||
|
switch (self.access) {
|
||||||
|
.render => |render| {
|
||||||
|
sokol.gfx.destroyImage(render.color_image);
|
||||||
|
sokol.gfx.destroyImage(render.depth_image);
|
||||||
|
sokol.gfx.destroyAttachments(render.attachments);
|
||||||
|
},
|
||||||
|
|
||||||
|
.static => |static| {
|
||||||
|
sokol.gfx.destroyImage(static.image);
|
||||||
|
},
|
||||||
|
|
||||||
|
.empty => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
self.* = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(desc: gfx.Texture.Desc) std.mem.Allocator.Error!Texture {
|
||||||
|
const pixel_format = switch (desc.format) {
|
||||||
|
.rgba8 => sokol.gfx.PixelFormat.RGBA8,
|
||||||
|
.bgra8 => sokol.gfx.PixelFormat.BGRA8,
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (desc.access) {
|
||||||
|
.render => |render| {
|
||||||
|
if (render.width == 0 or render.height == 0) {
|
||||||
|
return .{
|
||||||
|
.width = render.width,
|
||||||
|
.height = render.height,
|
||||||
|
.access = .empty,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const color_image = sokol.gfx.makeImage(.{
|
||||||
|
.pixel_format = pixel_format,
|
||||||
|
.width = render.width,
|
||||||
|
.height = render.height,
|
||||||
|
.render_target = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const depth_image = sokol.gfx.makeImage(.{
|
||||||
|
.width = render.width,
|
||||||
|
.height = render.height,
|
||||||
|
.render_target = true,
|
||||||
|
.pixel_format = .DEPTH_STENCIL,
|
||||||
|
});
|
||||||
|
|
||||||
|
const attachments = sokol.gfx.makeAttachments(attachments_desc: {
|
||||||
|
var attachments_desc = sokol.gfx.AttachmentsDesc{
|
||||||
|
.depth_stencil = .{
|
||||||
|
.image = depth_image,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
attachments_desc.colors[0] = .{
|
||||||
|
.image = color_image,
|
||||||
|
};
|
||||||
|
|
||||||
|
break: attachments_desc attachments_desc;
|
||||||
|
});
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.width = render.width,
|
||||||
|
.height = render.height,
|
||||||
|
|
||||||
|
.access = .{
|
||||||
|
.render = .{
|
||||||
|
.attachments = attachments,
|
||||||
|
.color_image = color_image,
|
||||||
|
.depth_image = depth_image,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
.static => |static| {
|
||||||
|
const height = std.math.cast(u16, static.data.len / (static.width * desc.format.byte_size())) orelse {
|
||||||
|
return error.OutOfMemory;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (static.width == 0 or height == 0) {
|
||||||
|
return .{
|
||||||
|
.width = static.width,
|
||||||
|
.height = height,
|
||||||
|
.access = .empty,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const image = sokol.gfx.makeImage(image_desc: {
|
||||||
|
var image_desc = sokol.gfx.ImageDesc{
|
||||||
|
.height = height,
|
||||||
|
.pixel_format = pixel_format,
|
||||||
|
.width = static.width,
|
||||||
|
};
|
||||||
|
|
||||||
|
image_desc.data.subimage[0][0] = sokol.gfx.asRange(static.data);
|
||||||
|
|
||||||
|
break: image_desc image_desc;
|
||||||
|
});
|
||||||
|
|
||||||
|
errdefer {
|
||||||
|
sokol.gfx.destroyImage(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.width = static.width,
|
||||||
|
.height = height,
|
||||||
|
|
||||||
|
.access = .{
|
||||||
|
.static = .{
|
||||||
|
.image = image,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const EffectPool = coral.Pool(Effect);
|
||||||
|
|
||||||
|
const TexturePool = coral.Pool(Texture);
|
||||||
|
|
||||||
|
pub fn create_effect(self: *Resources, desc: gfx.Effect.Desc) !gfx.Effect {
|
||||||
|
var effect = try Effect.init(desc);
|
||||||
|
|
||||||
errdefer effect.deinit();
|
errdefer effect.deinit();
|
||||||
|
|
||||||
return @enumFromInt(try self.effects.insert(effect));
|
return @enumFromInt(try self.effects.insert(effect));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_texture(self: *Pools, desc: handles.Texture.Desc) !handles.Texture {
|
pub fn create_texture(self: *Resources, desc: gfx.Texture.Desc) !gfx.Texture {
|
||||||
var texture = try resources.Texture.init(desc);
|
var texture = try Texture.init(desc);
|
||||||
|
|
||||||
errdefer texture.deinit();
|
errdefer texture.deinit();
|
||||||
|
|
||||||
return @enumFromInt(try self.textures.insert(texture));
|
return @enumFromInt(try self.textures.insert(texture));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Pools) void {
|
pub fn deinit(self: *Resources) void {
|
||||||
var textures = self.textures.values();
|
var textures = self.textures.values();
|
||||||
|
|
||||||
while (textures.next()) |texture| {
|
while (textures.next()) |texture| {
|
||||||
|
@ -332,7 +582,7 @@ const Pools = struct {
|
||||||
self.textures.deinit();
|
self.textures.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroy_effect(self: *Pools, handle: handles.Effect) bool {
|
pub fn destroy_effect(self: *Resources, handle: gfx.Effect) bool {
|
||||||
switch (handle) {
|
switch (handle) {
|
||||||
.default => {},
|
.default => {},
|
||||||
|
|
||||||
|
@ -348,7 +598,7 @@ const Pools = struct {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroy_texture(self: *Pools, handle: handles.Texture) bool {
|
pub fn destroy_texture(self: *Resources, handle: gfx.Texture) bool {
|
||||||
switch (handle) {
|
switch (handle) {
|
||||||
.default => {},
|
.default => {},
|
||||||
|
|
||||||
|
@ -364,16 +614,16 @@ const Pools = struct {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_effect(self: *Pools, handle: handles.Effect) ?*resources.Effect {
|
pub fn get_effect(self: *Resources, handle: gfx.Effect) ?*Effect {
|
||||||
return self.effects.get(@intFromEnum(handle));
|
return self.effects.get(@intFromEnum(handle));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_texture(self: *Pools, handle: handles.Texture) ?*resources.Texture {
|
pub fn get_texture(self: *Resources, handle: gfx.Texture) ?*Texture {
|
||||||
return self.textures.get(@intFromEnum(handle));
|
return self.textures.get(@intFromEnum(handle));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator) !Pools {
|
pub fn init(allocator: std.mem.Allocator) !Resources {
|
||||||
var pools = Pools{
|
var pools = Resources{
|
||||||
.effects = EffectPool.init(allocator),
|
.effects = EffectPool.init(allocator),
|
||||||
.textures = TexturePool.init(allocator),
|
.textures = TexturePool.init(allocator),
|
||||||
};
|
};
|
||||||
|
@ -388,11 +638,11 @@ const Pools = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
assert.is_handle(handles.Effect.default, try pools.create_effect(.{
|
assert.is_handle(gfx.Effect.default, try pools.create_effect(.{
|
||||||
.fragment_spirv_ops = &spirv.to_ops(@embedFile("./shaders/2d_default.frag.spv")),
|
.fragment_spirv_ops = &spirv.to_ops(@embedFile("./shaders/2d_default.frag.spv")),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
assert.is_handle(handles.Texture.default, try pools.create_texture(.{
|
assert.is_handle(gfx.Texture.default, try pools.create_texture(.{
|
||||||
.format = .rgba8,
|
.format = .rgba8,
|
||||||
|
|
||||||
.access = .{
|
.access = .{
|
||||||
|
@ -413,7 +663,7 @@ const Pools = struct {
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
assert.is_handle(handles.Texture.backbuffer, try pools.create_texture(.{
|
assert.is_handle(gfx.Texture.backbuffer, try pools.create_texture(.{
|
||||||
.format = .rgba8,
|
.format = .rgba8,
|
||||||
|
|
||||||
.access = .{
|
.access = .{
|
||||||
|
@ -428,62 +678,8 @@ const Pools = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Work = union (enum) {
|
|
||||||
create_commands: CreateCommandsWork,
|
|
||||||
load_effect: LoadEffectWork,
|
|
||||||
load_texture: LoadTextureWork,
|
|
||||||
render_frame: RenderFrameWork,
|
|
||||||
rotate_commands: RotateCommandsWork,
|
|
||||||
shutdown,
|
|
||||||
unload_effect: UnloadEffectWork,
|
|
||||||
unload_texture: UnloadTextureWork,
|
|
||||||
|
|
||||||
pub const CreateCommandsWork = struct {
|
|
||||||
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 {
|
|
||||||
desc: handles.Texture.Desc,
|
|
||||||
loaded: *coral.asyncio.Future(std.mem.Allocator.Error!handles.Texture),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const RotateCommandsWork = struct {
|
|
||||||
finished: *std.Thread.ResetEvent,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const RenderFrameWork = struct {
|
|
||||||
clear_color: lina.Color,
|
|
||||||
width: u16,
|
|
||||||
height: u16,
|
|
||||||
finished: *std.Thread.ResetEvent,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const UnloadEffectWork = struct {
|
|
||||||
handle: handles.Effect,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const UnloadTextureWork = struct {
|
|
||||||
handle: handles.Texture,
|
|
||||||
};
|
|
||||||
|
|
||||||
var pending: coral.asyncio.BlockingQueue(1024, Work) = .{};
|
|
||||||
};
|
|
||||||
|
|
||||||
var default_sampler: sokol.gfx.Sampler = undefined;
|
var default_sampler: sokol.gfx.Sampler = undefined;
|
||||||
|
|
||||||
pub fn enqueue_work(work: Work) void {
|
|
||||||
Work.pending.enqueue(work);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn startup(window: *ext.SDL_Window) !void {
|
|
||||||
work_thread = try std.Thread.spawn(.{}, run, .{window});
|
|
||||||
}
|
|
||||||
|
|
||||||
var quad_index_buffer: sokol.gfx.Buffer = undefined;
|
var quad_index_buffer: sokol.gfx.Buffer = undefined;
|
||||||
|
|
||||||
var quad_vertex_buffer: sokol.gfx.Buffer = undefined;
|
var quad_vertex_buffer: sokol.gfx.Buffer = undefined;
|
||||||
|
@ -493,7 +689,7 @@ const vertex_indices = .{
|
||||||
.instance = 1,
|
.instance = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn run(window: *ext.SDL_Window) !void {
|
pub fn run(pending_work: *gfx.Assets.WorkQueue, 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,7 +726,7 @@ fn run(window: *ext.SDL_Window) !void {
|
||||||
sokol.gfx.shutdown();
|
sokol.gfx.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
var pools = try Pools.init(coral.heap.allocator);
|
var pools = try Resources.init(coral.heap.allocator);
|
||||||
|
|
||||||
defer {
|
defer {
|
||||||
pools.deinit();
|
pools.deinit();
|
||||||
|
@ -562,36 +758,8 @@ fn run(window: *ext.SDL_Window) !void {
|
||||||
|
|
||||||
default_sampler = sokol.gfx.makeSampler(.{});
|
default_sampler = sokol.gfx.makeSampler(.{});
|
||||||
|
|
||||||
var has_commands_head: ?*Commands = null;
|
|
||||||
var has_commands_tail: ?*Commands = null;
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
switch (Work.pending.dequeue()) {
|
switch (pending_work.dequeue()) {
|
||||||
.create_commands => |create_commands| {
|
|
||||||
const commands = coral.heap.allocator.create(Commands) catch {
|
|
||||||
_ = create_commands.created.resolve(error.OutOfMemory);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
commands.* = Commands.init(coral.heap.allocator);
|
|
||||||
|
|
||||||
if (create_commands.created.resolve(commands)) {
|
|
||||||
if (has_commands_tail) |tail_commands| {
|
|
||||||
tail_commands.has_next = commands;
|
|
||||||
commands.has_prev = tail_commands;
|
|
||||||
} else {
|
|
||||||
std.debug.assert(has_commands_head == null);
|
|
||||||
|
|
||||||
has_commands_head = commands;
|
|
||||||
}
|
|
||||||
|
|
||||||
has_commands_tail = commands;
|
|
||||||
} else {
|
|
||||||
coral.heap.allocator.destroy(commands);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
.load_effect => |load| {
|
.load_effect => |load| {
|
||||||
const effect = try pools.create_effect(load.desc);
|
const effect = try pools.create_effect(load.desc);
|
||||||
|
|
||||||
|
@ -614,7 +782,7 @@ fn run(window: *ext.SDL_Window) !void {
|
||||||
if (backbuffer.width != render_frame.width or backbuffer.height != render_frame.height) {
|
if (backbuffer.width != render_frame.width or backbuffer.height != render_frame.height) {
|
||||||
backbuffer.deinit();
|
backbuffer.deinit();
|
||||||
|
|
||||||
backbuffer.* = try resources.Texture.init(.{
|
backbuffer.* = try Resources.Texture.init(.{
|
||||||
.format = .rgba8,
|
.format = .rgba8,
|
||||||
|
|
||||||
.access = .{
|
.access = .{
|
||||||
|
@ -661,11 +829,15 @@ fn run(window: *ext.SDL_Window) !void {
|
||||||
break: pass pass;
|
break: pass pass;
|
||||||
});
|
});
|
||||||
|
|
||||||
var has_commands = has_commands_head;
|
var has_command_params = render_frame.has_command_params;
|
||||||
|
|
||||||
while (has_commands) |commands| : (has_commands = commands.has_next) {
|
while (has_command_params) |command_params| : (has_command_params = command_params.has_next) {
|
||||||
for (commands.submitted_commands()) |command| {
|
for (command_params.param.submitted_commands()) |command| {
|
||||||
try command.process(&pools, &frame);
|
try switch (command) {
|
||||||
|
.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),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
frame.flush(&pools);
|
frame.flush(&pools);
|
||||||
|
@ -714,16 +886,6 @@ fn run(window: *ext.SDL_Window) !void {
|
||||||
render_frame.finished.set();
|
render_frame.finished.set();
|
||||||
},
|
},
|
||||||
|
|
||||||
.rotate_commands => |rotate_commands| {
|
|
||||||
var has_commands = has_commands_head;
|
|
||||||
|
|
||||||
while (has_commands) |commands| : (has_commands = commands.has_next) {
|
|
||||||
commands.rotate();
|
|
||||||
}
|
|
||||||
|
|
||||||
rotate_commands.finished.set();
|
|
||||||
},
|
|
||||||
|
|
||||||
.shutdown => {
|
.shutdown => {
|
||||||
break;
|
break;
|
||||||
},
|
},
|
|
@ -0,0 +1,38 @@
|
||||||
|
const coral = @import("coral");
|
||||||
|
|
||||||
|
pub const Format = enum {
|
||||||
|
rgba8,
|
||||||
|
bgra8,
|
||||||
|
|
||||||
|
pub fn byte_size(self: Format) usize {
|
||||||
|
return switch (self) {
|
||||||
|
.rgba8, .bgra8 => 4,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Desc = struct {
|
||||||
|
format: Format,
|
||||||
|
access: Access,
|
||||||
|
|
||||||
|
pub const Access = union (enum) {
|
||||||
|
static: StaticAccess,
|
||||||
|
render: RenderAccess,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const StaticAccess = struct {
|
||||||
|
width: u16,
|
||||||
|
data: []const coral.io.Byte,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const RenderAccess = struct {
|
||||||
|
width: u16,
|
||||||
|
height: u16,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Handle = enum (u32) {
|
||||||
|
default,
|
||||||
|
backbuffer,
|
||||||
|
_,
|
||||||
|
};
|
|
@ -63,23 +63,23 @@ pub fn Receive(comptime Message: type) type {
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
pub const State = struct {
|
pub const Param = struct {
|
||||||
channel: *const TypedChannel,
|
channel: *const TypedChannel,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn bind(context: flow.system.BindContext) std.mem.Allocator.Error!State {
|
pub fn bind(context: flow.system.BindContext) std.mem.Allocator.Error!Param {
|
||||||
return .{
|
return .{
|
||||||
.channel = (try context.register_readable_resource_access(TypedChannel)) orelse set: {
|
.channel = (try context.register_readable_state_access(TypedChannel)) orelse set: {
|
||||||
try context.world.set_resource(TypedChannel.init(coral.heap.allocator));
|
try context.world.set_state(TypedChannel.init(coral.heap.allocator));
|
||||||
|
|
||||||
break: set (try context.register_readable_resource_access(TypedChannel)).?;
|
break: set (try context.register_readable_state_access(TypedChannel)).?;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(state: *State) Self {
|
pub fn init(param: *Param) Self {
|
||||||
return .{
|
return .{
|
||||||
.channel = state.channel,
|
.channel = param.channel,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,16 +97,16 @@ pub fn Send(comptime Message: type) type {
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
pub const State = struct {
|
pub const Param = struct {
|
||||||
channel: *TypedChannel,
|
channel: *TypedChannel,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn bind(context: flow.system.BindContext) std.mem.Allocator.Error!State {
|
pub fn bind(context: flow.system.BindContext) std.mem.Allocator.Error!Param {
|
||||||
return .{
|
return .{
|
||||||
.channel = (try context.register_writable_resource_access(TypedChannel)) orelse set: {
|
.channel = (try context.register_writable_state_access(TypedChannel)) orelse set: {
|
||||||
try context.world.set_resource(TypedChannel.init(coral.heap.allocator));
|
try context.world.set_state(TypedChannel.init(coral.heap.allocator));
|
||||||
|
|
||||||
const app = context.world.get_resource(App).?;
|
const app = context.world.get_state(App).?;
|
||||||
|
|
||||||
try context.world.on_event(app.events.post_update, flow.system_fn(TypedChannel.swap), .{
|
try context.world.on_event(app.events.post_update, flow.system_fn(TypedChannel.swap), .{
|
||||||
.label = "swap channel of " ++ @typeName(Message),
|
.label = "swap channel of " ++ @typeName(Message),
|
||||||
|
@ -116,14 +116,14 @@ pub fn Send(comptime Message: type) type {
|
||||||
.label = "clean up channel of " ++ @typeName(Message),
|
.label = "clean up channel of " ++ @typeName(Message),
|
||||||
});
|
});
|
||||||
|
|
||||||
break: set (try context.register_writable_resource_access(TypedChannel)).?;
|
break: set (try context.register_writable_state_access(TypedChannel)).?;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(state: *State) Self {
|
pub fn init(param: *Param) Self {
|
||||||
return .{
|
return .{
|
||||||
.channel = state.channel,
|
.channel = param.channel,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ pub fn start_app(setup: Setup, options: Options) anyerror!void {
|
||||||
.exit = try world.create_event("exit"),
|
.exit = try world.create_event("exit"),
|
||||||
};
|
};
|
||||||
|
|
||||||
const app = try world.set_get_resource(App{
|
const app = try world.set_get_state(App{
|
||||||
.events = &events,
|
.events = &events,
|
||||||
.target_frame_time = 1.0 / @as(f64, @floatFromInt(options.tick_rate)),
|
.target_frame_time = 1.0 / @as(f64, @floatFromInt(options.tick_rate)),
|
||||||
.elapsed_time = 0,
|
.elapsed_time = 0,
|
||||||
|
|
Loading…
Reference in New Issue