renderer-mvp/asset-pipeline #53
|
@ -2,6 +2,8 @@ pub const ascii = @import("./ascii.zig");
|
||||||
|
|
||||||
pub const dag = @import("./dag.zig");
|
pub const dag = @import("./dag.zig");
|
||||||
|
|
||||||
|
pub const files = @import("./files.zig");
|
||||||
|
|
||||||
pub const hashes = @import("./hashes.zig");
|
pub const hashes = @import("./hashes.zig");
|
||||||
|
|
||||||
pub const heap = @import("./heap.zig");
|
pub const heap = @import("./heap.zig");
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
|
const io = @import("./io.zig");
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub const Error = error {
|
||||||
|
FileNotFound,
|
||||||
|
FileInaccessible,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Stat = struct {
|
||||||
|
size: u64,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Storage = struct {
|
||||||
|
userdata: *anyopaque,
|
||||||
|
vtable: *const VTable,
|
||||||
|
|
||||||
|
pub const VTable = struct {
|
||||||
|
stat: *const fn (*anyopaque, []const u8) Error!Stat,
|
||||||
|
read: *const fn (*anyopaque, []const u8, usize, []io.Byte) Error!usize,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn read_bytes(self: Storage, path: []const u8, offset: usize, output: []io.Byte) Error!usize {
|
||||||
|
return self.vtable.read(self.userdata, path, offset, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_foreign(self: Storage, path: []const u8, offset: usize, comptime Type: type) Error!?Type {
|
||||||
|
const decoded = (try self.read_native(path, offset, Type)) orelse {
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return switch (@typeInfo(Type)) {
|
||||||
|
.Struct => std.mem.byteSwapAllFields(Type, &decoded),
|
||||||
|
else => @byteSwap(decoded),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_native(self: Storage, path: []const u8, offset: usize, comptime Type: type) Error!?Type {
|
||||||
|
var buffer = @as([@sizeOf(Type)]io.Byte, undefined);
|
||||||
|
|
||||||
|
if (try self.vtable.read(self.userdata, path, offset, &buffer) != buffer.len) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return @as(*align(1) const Type, @ptrCast(&buffer)).*;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const read_little = switch (native_endian) {
|
||||||
|
.little => read_native,
|
||||||
|
.big => read_foreign,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const read_big = switch (native_endian) {
|
||||||
|
.little => read_foreign,
|
||||||
|
.big => read_native,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const bundle = init: {
|
||||||
|
const Bundle = struct {
|
||||||
|
fn full_path(path: []const u8) Error![4095:0]u8 {
|
||||||
|
var buffer = [_:0]u8{0} ** 4095;
|
||||||
|
|
||||||
|
_ = std.fs.cwd().realpath(path, &buffer) catch {
|
||||||
|
return error.FileInaccessible;
|
||||||
|
};
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(_: *anyopaque, path: []const u8, offset: usize, output: []io.Byte) Error!usize {
|
||||||
|
var file = std.fs.openFileAbsoluteZ(&(try full_path(path)), .{.mode = .read_only}) catch |open_error| {
|
||||||
|
return switch (open_error) {
|
||||||
|
error.FileNotFound => error.FileNotFound,
|
||||||
|
else => error.FileInaccessible,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
defer file.close();
|
||||||
|
|
||||||
|
if (offset != 0) {
|
||||||
|
file.seekTo(offset) catch {
|
||||||
|
return error.FileInaccessible;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return file.read(output) catch error.FileInaccessible;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stat(_: *anyopaque, path: []const u8) Error!Stat {
|
||||||
|
const file_stat = get: {
|
||||||
|
var file = std.fs.openFileAbsoluteZ(&(try full_path(path)), .{.mode = .read_only}) catch |open_error| {
|
||||||
|
return switch (open_error) {
|
||||||
|
error.FileNotFound => error.FileNotFound,
|
||||||
|
else => error.FileInaccessible,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
defer file.close();
|
||||||
|
|
||||||
|
break: get file.stat() catch {
|
||||||
|
return error.FileInaccessible;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.size = file_stat.size,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
break: init Storage{
|
||||||
|
.userdata = undefined,
|
||||||
|
|
||||||
|
.vtable = &.{
|
||||||
|
.stat = Bundle.stat,
|
||||||
|
.read = Bundle.read,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const native_endian = builtin.cpu.arch.endian();
|
|
@ -6,23 +6,30 @@ const slices = @import("./slices.zig");
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
pub const Byte = u8;
|
pub const Writable = struct {
|
||||||
kayomn marked this conversation as resolved
|
|||||||
|
data: []Byte,
|
||||||
|
|
||||||
pub const Decoder = coral.io.Functor(coral.io.Error!void, &.{[]coral.Byte});
|
pub fn writer(self: *Writable) Writer {
|
||||||
|
return Writer.bind(Writable, self, write);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(self: *Writable, buffer: []const u8) !usize {
|
||||||
|
const range = @min(buffer.len, self.data.len);
|
||||||
|
|
||||||
|
@memcpy(self.data[0 .. range], buffer[0 .. range]);
|
||||||
|
|
||||||
|
self.data = self.data[range ..];
|
||||||
|
|
||||||
|
return buffer.len;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Byte = u8;
|
||||||
|
|
||||||
pub const Error = error {
|
pub const Error = error {
|
||||||
UnavailableResource,
|
UnavailableResource,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn FixedBuffer(comptime len: usize, comptime default_value: anytype) type {
|
|
||||||
const Value = @TypeOf(default_value);
|
|
||||||
|
|
||||||
return struct {
|
|
||||||
filled: usize = 0,
|
|
||||||
values: [len]Value = [_]Value{default_value} ** len,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn Functor(comptime Output: type, comptime input_types: []const type) type {
|
pub fn Functor(comptime Output: type, comptime input_types: []const type) type {
|
||||||
const InputTuple = std.meta.Tuple(input_types);
|
const InputTuple = std.meta.Tuple(input_types);
|
||||||
|
|
||||||
|
@ -119,20 +126,6 @@ pub fn Generator(comptime Output: type, comptime input_types: []const type) type
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const NullWritable = struct {
|
|
||||||
written: usize = 0,
|
|
||||||
|
|
||||||
pub fn writer(self: *NullWritable) Writer {
|
|
||||||
return Writer.bind(NullWritable, self, write);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write(self: *NullWritable, buffer: []const u8) !usize {
|
|
||||||
self.written += buffer.len;
|
|
||||||
|
|
||||||
return buffer.len;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const PrintError = Error || error {
|
pub const PrintError = Error || error {
|
||||||
IncompleteWrite,
|
IncompleteWrite,
|
||||||
};
|
};
|
||||||
|
@ -141,8 +134,6 @@ pub const Reader = Generator(Error!usize, &.{[]coral.Byte});
|
||||||
|
|
||||||
pub const Writer = Generator(Error!usize, &.{[]const coral.Byte});
|
pub const Writer = Generator(Error!usize, &.{[]const coral.Byte});
|
||||||
|
|
||||||
const native_endian = builtin.cpu.arch.endian();
|
|
||||||
|
|
||||||
pub fn alloc_read(input: coral.io.Reader, allocator: std.mem.Allocator) []coral.Byte {
|
pub fn alloc_read(input: coral.io.Reader, allocator: std.mem.Allocator) []coral.Byte {
|
||||||
const buffer = coral.Stack(coral.Byte){.allocator = allocator};
|
const buffer = coral.Stack(coral.Byte){.allocator = allocator};
|
||||||
|
|
||||||
|
@ -153,20 +144,6 @@ pub fn alloc_read(input: coral.io.Reader, allocator: std.mem.Allocator) []coral.
|
||||||
return buffer.to_allocation(streamed);
|
return buffer.to_allocation(streamed);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn are_equal(a: []const Byte, b: []const Byte) bool {
|
|
||||||
if (a.len != b.len) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (0 .. a.len) |i| {
|
|
||||||
if (a[i] != b[i]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const bits_per_byte = 8;
|
pub const bits_per_byte = 8;
|
||||||
|
|
||||||
pub fn bytes_of(value: anytype) []const Byte {
|
pub fn bytes_of(value: anytype) []const Byte {
|
||||||
|
@ -179,14 +156,6 @@ pub fn bytes_of(value: anytype) []const Byte {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ends_with(haystack: []const Byte, needle: []const Byte) bool {
|
|
||||||
if (needle.len > haystack.len) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return are_equal(haystack[haystack.len - needle.len ..], needle);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print(writer: Writer, utf8: []const u8) PrintError!void {
|
pub fn print(writer: Writer, utf8: []const u8) PrintError!void {
|
||||||
if (try writer.yield(.{utf8}) != utf8.len) {
|
if (try writer.yield(.{utf8}) != utf8.len) {
|
||||||
return error.IncompleteWrite;
|
return error.IncompleteWrite;
|
||||||
|
@ -208,35 +177,6 @@ pub fn skip_n(input: Reader, distance: u64) Error!void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_foreign(input: Reader, comptime Type: type) Error!Type {
|
|
||||||
const decoded = try read_native(input, Type);
|
|
||||||
|
|
||||||
return switch (@typeInfo(input)) {
|
|
||||||
.Struct => std.mem.byteSwapAllFields(Type, &decoded),
|
|
||||||
else => @byteSwap(decoded),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_native(input: Reader, comptime Type: type) Error!Type {
|
|
||||||
var buffer = @as([@sizeOf(Type)]coral.Byte, undefined);
|
|
||||||
|
|
||||||
if (try input.yield(.{&buffer}) != buffer.len) {
|
|
||||||
return error.UnavailableResource;
|
|
||||||
}
|
|
||||||
|
|
||||||
return @as(*align(1) const Type, @ptrCast(&buffer)).*;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const read_little = switch (native_endian) {
|
|
||||||
.little => read_native,
|
|
||||||
.big => read_foreign,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const read_big = switch (native_endian) {
|
|
||||||
.little => read_foreign,
|
|
||||||
.big => read_native,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn slice_sentineled(comptime sen: anytype, ptr: [*:sen]const @TypeOf(sen)) [:sen]const @TypeOf(sen) {
|
pub fn slice_sentineled(comptime sen: anytype, ptr: [*:sen]const @TypeOf(sen)) [:sen]const @TypeOf(sen) {
|
||||||
var len = @as(usize, 0);
|
var len = @as(usize, 0);
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ const ona = @import("ona");
|
||||||
|
|
||||||
const Actors = struct {
|
const Actors = struct {
|
||||||
instances: coral.stack.Sequential(ona.gfx.Point2D) = .{.allocator = coral.heap.allocator},
|
instances: coral.stack.Sequential(ona.gfx.Point2D) = .{.allocator = coral.heap.allocator},
|
||||||
|
quad_mesh_2d: ona.gfx.Handle = .none,
|
||||||
body_texture: ona.gfx.Handle = .none,
|
body_texture: ona.gfx.Handle = .none,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,6 +25,7 @@ pub fn main() !void {
|
||||||
fn load(display: coral.Write(ona.gfx.Display), actors: coral.Write(Actors), assets: coral.Write(ona.gfx.Assets)) !void {
|
fn load(display: coral.Write(ona.gfx.Display), actors: coral.Write(Actors), assets: coral.Write(ona.gfx.Assets)) !void {
|
||||||
display.res.width, display.res.height = .{1280, 720};
|
display.res.width, display.res.height = .{1280, 720};
|
||||||
actors.res.body_texture = try assets.res.open_file("actor.bmp");
|
actors.res.body_texture = try assets.res.open_file("actor.bmp");
|
||||||
|
actors.res.quad_mesh_2d = try assets.res.open_quad_mesh_2d(@splat(1));
|
||||||
|
|
||||||
try actors.res.instances.push_grow(.{0, 0});
|
try actors.res.instances.push_grow(.{0, 0});
|
||||||
}
|
}
|
||||||
|
@ -32,11 +34,11 @@ fn exit(actors: coral.Write(Actors)) void {
|
||||||
actors.res.instances.deinit();
|
actors.res.instances.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(queue: ona.gfx.Queue, actors: coral.Write(Actors), assets: coral.Read(ona.gfx.Assets)) !void {
|
fn render(queue: ona.gfx.Queue, actors: coral.Write(Actors)) !void {
|
||||||
for (actors.res.instances.values) |instance| {
|
for (actors.res.instances.values) |instance| {
|
||||||
try queue.commands.append(.{
|
try queue.commands.append(.{
|
||||||
.instance_2d = .{
|
.instance_2d = .{
|
||||||
.mesh_2d = assets.res.primitives.quad_mesh,
|
.mesh_2d = actors.res.quad_mesh_2d,
|
||||||
.texture = actors.res.body_texture,
|
.texture = actors.res.body_texture,
|
||||||
|
|
||||||
.transform = .{
|
.transform = .{
|
||||||
|
|
152
src/ona/gfx.zig
152
src/ona/gfx.zig
|
@ -14,41 +14,99 @@ const std = @import("std");
|
||||||
|
|
||||||
pub const Assets = struct {
|
pub const Assets = struct {
|
||||||
context: device.Context,
|
context: device.Context,
|
||||||
primitives: Primitives,
|
|
||||||
formats: coral.stack.Sequential(Format),
|
formats: coral.stack.Sequential(Format),
|
||||||
|
staging_arena: std.heap.ArenaAllocator,
|
||||||
|
|
||||||
pub const Format = struct {
|
pub const Format = struct {
|
||||||
extension: []const u8,
|
extension: []const u8,
|
||||||
open: *const fn ([]const u8) std.mem.Allocator.Error!Handle,
|
open_file: *const fn (*std.heap.ArenaAllocator, []const u8) Error!Desc,
|
||||||
|
|
||||||
|
pub const Error = std.mem.Allocator.Error || coral.files.Error || error {
|
||||||
|
Unsupported,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const Primitives = struct {
|
pub fn open_file(self: *Assets, path: []const u8) (std.mem.Allocator.Error || Format.Error)!Handle {
|
||||||
quad_mesh: Handle,
|
defer {
|
||||||
};
|
const max_cache_size = 536870912;
|
||||||
|
|
||||||
pub fn close(self: *Assets, handle: Handle) void {
|
if (!self.staging_arena.reset(.{.retain_with_limit = max_cache_size})) {
|
||||||
return self.context.close(handle);
|
std.log.warn("failed to retain staging arena size of {} bytes", .{max_cache_size});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_file(self: *Assets, path: []const u8) std.mem.Allocator.Error!Handle {
|
|
||||||
for (self.formats.values) |format| {
|
for (self.formats.values) |format| {
|
||||||
if (!std.mem.endsWith(u8, path, format.extension)) {
|
if (!std.mem.endsWith(u8, path, format.extension)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return format.open(path);
|
return self.context.open(try format.open_file(&self.staging_arena, path));
|
||||||
}
|
}
|
||||||
|
|
||||||
return .none;
|
return .none;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_mesh_2d(self: *Assets, mesh_2d: Mesh2D) std.mem.Allocator.Error!Handle {
|
pub fn open_quad_mesh_2d(self: *Assets, extents: Point2D) std.mem.Allocator.Error!Handle {
|
||||||
return self.context.open(.{.mesh_2d = mesh_2d});
|
const width, const height = extents / @as(Point2D, @splat(2));
|
||||||
|
|
||||||
|
return self.context.open(.{
|
||||||
|
.mesh_2d = .{
|
||||||
|
.indices = &.{0, 1, 2, 0, 2, 3},
|
||||||
|
|
||||||
|
.vertices = &.{
|
||||||
|
.{.xy = .{-width, height}, .uv = .{0, 1}},
|
||||||
|
.{.xy = .{width, height}, .uv = .{1, 1}},
|
||||||
|
.{.xy = .{width, -height}, .uv = .{1, 0}},
|
||||||
|
.{.xy = .{-width, -height}, .uv = .{0, 0}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Color = @Vector(4, f32);
|
pub const Color = @Vector(4, f32);
|
||||||
|
|
||||||
|
pub const Desc = union (enum) {
|
||||||
|
texture: Texture,
|
||||||
|
mesh_2d: Mesh2D,
|
||||||
|
|
||||||
|
pub const Mesh2D = struct {
|
||||||
|
vertices: []const Vertex,
|
||||||
|
indices: []const u16,
|
||||||
|
|
||||||
|
pub const Vertex = struct {
|
||||||
|
xy: Point2D,
|
||||||
|
uv: Point2D,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Texture = struct {
|
||||||
|
data: []const coral.io.Byte,
|
||||||
|
width: u16,
|
||||||
|
format: Format,
|
||||||
|
access: Access,
|
||||||
|
|
||||||
|
pub const Access = enum {
|
||||||
|
static,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Format = enum {
|
||||||
|
rgba8888,
|
||||||
|
bgra8888,
|
||||||
|
argb8888,
|
||||||
|
rgb888,
|
||||||
|
bgr888,
|
||||||
|
|
||||||
|
pub fn byte_size(self: Format) usize {
|
||||||
|
return switch (self) {
|
||||||
|
.rgba8888, .bgra8888, .argb8888 => 4,
|
||||||
|
.rgb888, .bgr888 => 3,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
pub const Display = struct {
|
pub const Display = struct {
|
||||||
width: u16 = 1280,
|
width: u16 = 1280,
|
||||||
height: u16 = 720,
|
height: u16 = 720,
|
||||||
|
@ -85,16 +143,6 @@ pub const Input = union (enum) {
|
||||||
|
|
||||||
pub const Point2D = @Vector(2, f32);
|
pub const Point2D = @Vector(2, f32);
|
||||||
|
|
||||||
pub const Mesh2D = struct {
|
|
||||||
vertices: []const Vertex,
|
|
||||||
indices: []const u16,
|
|
||||||
|
|
||||||
pub const Vertex = struct {
|
|
||||||
xy: Point2D,
|
|
||||||
uv: Point2D,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Queue = struct {
|
pub const Queue = struct {
|
||||||
commands: *device.RenderList,
|
commands: *device.RenderList,
|
||||||
|
|
||||||
|
@ -104,6 +152,10 @@ pub const Queue = struct {
|
||||||
|
|
||||||
pub fn bind(_: coral.system.BindContext) std.mem.Allocator.Error!State {
|
pub fn bind(_: coral.system.BindContext) std.mem.Allocator.Error!State {
|
||||||
// TODO: Review how good of an idea this global state is, even if bind is guaranteed to always be ran on main.
|
// TODO: Review how good of an idea this global state is, even if bind is guaranteed to always be ran on main.
|
||||||
|
if (renders.is_empty()) {
|
||||||
|
renders = .{.allocator = coral.heap.allocator};
|
||||||
|
}
|
||||||
|
|
||||||
const command_index = renders.len();
|
const command_index = renders.len();
|
||||||
|
|
||||||
try renders.push_grow(device.RenderChain.init(coral.heap.allocator));
|
try renders.push_grow(device.RenderChain.init(coral.heap.allocator));
|
||||||
|
@ -121,39 +173,21 @@ pub const Queue = struct {
|
||||||
|
|
||||||
pub fn unbind(state: *State) void {
|
pub fn unbind(state: *State) void {
|
||||||
std.debug.assert(!renders.is_empty());
|
std.debug.assert(!renders.is_empty());
|
||||||
std.mem.swap(device.RenderChain, &renders.values[state.command_index], renders.get_ptr().?);
|
|
||||||
|
const render = &renders.values[state.command_index];
|
||||||
|
|
||||||
|
render.deinit();
|
||||||
|
std.mem.swap(device.RenderChain, render, renders.get_ptr().?);
|
||||||
std.debug.assert(renders.pop());
|
std.debug.assert(renders.pop());
|
||||||
|
|
||||||
|
if (renders.is_empty()) {
|
||||||
|
Queue.renders.deinit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var renders = coral.stack.Sequential(device.RenderChain){.allocator = coral.heap.allocator};
|
var renders = coral.stack.Sequential(device.RenderChain){.allocator = coral.heap.allocator};
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Texture = struct {
|
|
||||||
data: []const coral.io.Byte,
|
|
||||||
width: u16,
|
|
||||||
format: Format,
|
|
||||||
access: Access,
|
|
||||||
|
|
||||||
pub const Access = enum {
|
|
||||||
static,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Format = enum {
|
|
||||||
rgba8888,
|
|
||||||
bgra8888,
|
|
||||||
argb8888,
|
|
||||||
rgb888,
|
|
||||||
bgr888,
|
|
||||||
|
|
||||||
pub fn byte_size(self: Format) usize {
|
|
||||||
return switch (self) {
|
|
||||||
.rgba8888, .bgra8888, .argb8888 => 4,
|
|
||||||
.rgb888, .bgr888 => 3,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Transform2D = extern struct {
|
pub const Transform2D = extern struct {
|
||||||
xbasis: Point2D = .{1, 0},
|
xbasis: Point2D = .{1, 0},
|
||||||
ybasis: Point2D = .{0, 1},
|
ybasis: Point2D = .{0, 1},
|
||||||
|
@ -163,7 +197,7 @@ pub const Transform2D = extern struct {
|
||||||
const builtin_formats = [_]Assets.Format{
|
const builtin_formats = [_]Assets.Format{
|
||||||
.{
|
.{
|
||||||
.extension = "bmp",
|
.extension = "bmp",
|
||||||
.open = formats.open_bmp,
|
.open_file = formats.load_bmp,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -210,24 +244,8 @@ pub fn setup(world: *coral.World, events: App.Events) (error {Unsupported} || st
|
||||||
try registered_formats.grow(builtin_formats.len);
|
try registered_formats.grow(builtin_formats.len);
|
||||||
std.debug.assert(registered_formats.push_all(&builtin_formats));
|
std.debug.assert(registered_formats.push_all(&builtin_formats));
|
||||||
|
|
||||||
const half_extent = 0.5;
|
|
||||||
|
|
||||||
try world.set_resource(.none, Assets{
|
try world.set_resource(.none, Assets{
|
||||||
.primitives = .{
|
.staging_arena = std.heap.ArenaAllocator.init(coral.heap.allocator),
|
||||||
.quad_mesh = try context.open(.{
|
|
||||||
.mesh_2d = .{
|
|
||||||
.indices = &.{0, 1, 2, 0, 2, 3},
|
|
||||||
|
|
||||||
.vertices = &.{
|
|
||||||
.{.xy = .{-half_extent, half_extent}, .uv = .{0, 1}},
|
|
||||||
.{.xy = .{half_extent, half_extent}, .uv = .{1, 1}},
|
|
||||||
.{.xy = .{half_extent, -half_extent}, .uv = .{1, 0}},
|
|
||||||
.{.xy = .{-half_extent, -half_extent}, .uv = .{0, 0}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
|
|
||||||
.formats = registered_formats,
|
.formats = registered_formats,
|
||||||
.context = context,
|
.context = context,
|
||||||
});
|
});
|
||||||
|
@ -239,6 +257,8 @@ pub fn setup(world: *coral.World, events: App.Events) (error {Unsupported} || st
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop(assets: coral.Write(Assets)) void {
|
pub fn stop(assets: coral.Write(Assets)) void {
|
||||||
|
assets.res.staging_arena.deinit();
|
||||||
|
assets.res.formats.deinit();
|
||||||
assets.res.context.deinit();
|
assets.res.context.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,11 +10,6 @@ const sokol = @import("sokol");
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
pub const Asset = union (enum) {
|
|
||||||
texture: gfx.Texture,
|
|
||||||
mesh_2d: gfx.Mesh2D,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Context = struct {
|
pub const Context = struct {
|
||||||
window: *ext.SDL_Window,
|
window: *ext.SDL_Window,
|
||||||
thread: std.Thread,
|
thread: std.Thread,
|
||||||
|
@ -42,6 +37,7 @@ pub const Context = struct {
|
||||||
self.loop.is_running.store(false, .monotonic);
|
self.loop.is_running.store(false, .monotonic);
|
||||||
self.loop.ready.post();
|
self.loop.ready.post();
|
||||||
self.thread.join();
|
self.thread.join();
|
||||||
|
self.loop.deinit();
|
||||||
coral.heap.allocator.destroy(self.loop);
|
coral.heap.allocator.destroy(self.loop);
|
||||||
ext.SDL_DestroyWindow(self.window);
|
ext.SDL_DestroyWindow(self.window);
|
||||||
|
|
||||||
|
@ -84,16 +80,20 @@ pub const Context = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open(self: *Context, asset: Asset) std.mem.Allocator.Error!gfx.Handle {
|
pub fn open(self: *Context, desc: gfx.Desc) std.mem.Allocator.Error!gfx.Handle {
|
||||||
const open_commands = self.loop.opens.pending();
|
const open_commands = self.loop.opens.pending();
|
||||||
const index = self.loop.closed_indices.get() orelse open_commands.stack.len();
|
const index = self.loop.closed_indices.get() orelse open_commands.stack.len();
|
||||||
|
|
||||||
try open_commands.append(.{
|
try open_commands.append(.{
|
||||||
.index = index,
|
.index = index,
|
||||||
.payload = asset,
|
.desc = desc,
|
||||||
});
|
});
|
||||||
|
|
||||||
try self.loop.closes.pending().stack.grow(1);
|
const pending_closes = self.loop.closes.pending();
|
||||||
|
|
||||||
|
if (pending_closes.stack.len() == pending_closes.stack.cap) {
|
||||||
|
try pending_closes.stack.grow(1);
|
||||||
|
}
|
||||||
|
|
||||||
_ = self.loop.closed_indices.pop();
|
_ = self.loop.closed_indices.pop();
|
||||||
|
|
||||||
|
@ -145,13 +145,13 @@ const Loop = struct {
|
||||||
|
|
||||||
const OpenCommand = struct {
|
const OpenCommand = struct {
|
||||||
index: usize,
|
index: usize,
|
||||||
payload: Asset,
|
desc: gfx.Desc,
|
||||||
|
|
||||||
fn clone(command: OpenCommand, arena: *std.heap.ArenaAllocator) std.mem.Allocator.Error!OpenCommand {
|
fn clone(command: OpenCommand, arena: *std.heap.ArenaAllocator) std.mem.Allocator.Error!OpenCommand {
|
||||||
const allocator = arena.allocator();
|
const allocator = arena.allocator();
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.payload = switch (command.payload) {
|
.desc = switch (command.desc) {
|
||||||
.texture => |texture| .{
|
.texture => |texture| .{
|
||||||
.texture = .{
|
.texture = .{
|
||||||
.data = try allocator.dupe(coral.io.Byte, texture.data),
|
.data = try allocator.dupe(coral.io.Byte, texture.data),
|
||||||
|
@ -163,7 +163,7 @@ const Loop = struct {
|
||||||
|
|
||||||
.mesh_2d => |mesh_2d| .{
|
.mesh_2d => |mesh_2d| .{
|
||||||
.mesh_2d = .{
|
.mesh_2d = .{
|
||||||
.vertices = try allocator.dupe(gfx.Mesh2D.Vertex, mesh_2d.vertices),
|
.vertices = try allocator.dupe(gfx.Desc.Mesh2D.Vertex, mesh_2d.vertices),
|
||||||
.indices = try allocator.dupe(u16, mesh_2d.indices),
|
.indices = try allocator.dupe(u16, mesh_2d.indices),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -178,6 +178,12 @@ const Loop = struct {
|
||||||
|
|
||||||
const OpenChain = commands.Chain(OpenCommand, OpenCommand.clone);
|
const OpenChain = commands.Chain(OpenCommand, OpenCommand.clone);
|
||||||
|
|
||||||
|
fn deinit(self: *Loop) void {
|
||||||
|
self.closes.deinit();
|
||||||
|
self.opens.deinit();
|
||||||
|
self.closed_indices.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
fn run(self: *Loop, window: *ext.SDL_Window) !void {
|
fn run(self: *Loop, 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);
|
||||||
|
@ -230,7 +236,7 @@ const Loop = struct {
|
||||||
defer open_commands.clear();
|
defer open_commands.clear();
|
||||||
|
|
||||||
for (open_commands.stack.values) |command| {
|
for (open_commands.stack.values) |command| {
|
||||||
switch (command.payload) {
|
switch (command.desc) {
|
||||||
.texture => |texture| {
|
.texture => |texture| {
|
||||||
const stride = texture.width * texture.format.byte_size();
|
const stride = texture.width * texture.format.byte_size();
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,69 @@
|
||||||
|
const coral = @import("coral");
|
||||||
|
|
||||||
const gfx = @import("../gfx.zig");
|
const gfx = @import("../gfx.zig");
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
pub fn open_bmp(path: []const u8) std.mem.Allocator.Error!gfx.Handle {
|
pub fn load_bmp(arena: *std.heap.ArenaAllocator, path: []const u8) gfx.Assets.Format.Error!gfx.Desc {
|
||||||
// TODO: Implement.
|
const header = try coral.files.bundle.read_little(path, 0, extern struct {
|
||||||
_ = path;
|
type: [2]u8 align (1),
|
||||||
unreachable;
|
file_size: u32 align (1),
|
||||||
|
reserved: [2]u16 align (1),
|
||||||
|
image_offset: u32 align (1),
|
||||||
|
header_size: u32 align (1),
|
||||||
|
pixel_width: i32 align (1),
|
||||||
|
pixel_height: i32 align (1),
|
||||||
|
color_planes: u16 align (1),
|
||||||
|
bits_per_pixel: u16 align (1),
|
||||||
|
compression_method: u32 align (1),
|
||||||
|
image_size: u32 align(1),
|
||||||
|
pixels_per_meter_x: i32 align (1),
|
||||||
|
pixels_per_meter_y: i32 align (1),
|
||||||
|
palette_colors_used: u32 align (1),
|
||||||
|
important_colors_used: u32 align (1),
|
||||||
|
}) orelse {
|
||||||
|
return error.Unsupported;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!std.mem.eql(u8, &header.type, "BM")) {
|
||||||
|
return error.Unsupported;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pixel_width = std.math.cast(u16, header.pixel_width) orelse {
|
||||||
|
return error.Unsupported;
|
||||||
|
};
|
||||||
|
|
||||||
|
const pixels = try arena.allocator().alloc(coral.io.Byte, header.image_size);
|
||||||
|
const bytes_per_pixel = header.bits_per_pixel / coral.io.bits_per_byte;
|
||||||
|
const alignment = 4;
|
||||||
|
const byte_stride = pixel_width * bytes_per_pixel;
|
||||||
|
const padded_byte_stride = alignment * @divTrunc((byte_stride + alignment - 1), alignment);
|
||||||
|
const byte_padding = coral.scalars.sub(padded_byte_stride, byte_stride) orelse 0;
|
||||||
|
var buffer_offset: usize = 0;
|
||||||
|
var file_offset = @as(usize, header.image_offset);
|
||||||
|
|
||||||
|
while (buffer_offset < pixels.len) {
|
||||||
|
const line = pixels[buffer_offset .. buffer_offset + byte_stride];
|
||||||
|
|
||||||
|
if (try coral.files.bundle.read_bytes(path, file_offset, line) != byte_stride) {
|
||||||
|
return error.Unsupported;
|
||||||
|
}
|
||||||
|
|
||||||
|
file_offset = line.len + byte_padding;
|
||||||
|
buffer_offset += padded_byte_stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.texture = .{
|
||||||
|
.format = switch (header.bits_per_pixel) {
|
||||||
|
24 => .bgr888,
|
||||||
|
32 => .bgra8888,
|
||||||
|
else => return error.Unsupported,
|
||||||
|
},
|
||||||
|
|
||||||
|
.width = pixel_width,
|
||||||
|
.data = pixels,
|
||||||
|
.access = .static,
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Unused addition.