Iterative texture loading
This commit is contained in:
@@ -47,6 +47,9 @@ const AssetContext = struct {
|
|||||||
|
|
||||||
pub const LoadError = error{
|
pub const LoadError = error{
|
||||||
DependencyError,
|
DependencyError,
|
||||||
|
ParsingError,
|
||||||
|
SdlError,
|
||||||
|
FileTooBig,
|
||||||
} || std.mem.Allocator.Error || std.fs.File.OpenError || std.fs.File.ReadError;
|
} || std.mem.Allocator.Error || std.fs.File.OpenError || std.fs.File.ReadError;
|
||||||
|
|
||||||
pub const AssetType = enum {
|
pub const AssetType = enum {
|
||||||
@@ -129,8 +132,11 @@ pub fn AssetContainer(comptime T: type) type {
|
|||||||
// TODO: Do something else while the asset is locked?
|
// TODO: Do something else while the asset is locked?
|
||||||
self.asset_pointer.mutex.lock();
|
self.asset_pointer.mutex.lock();
|
||||||
defer self.asset_pointer.mutex.unlock();
|
defer self.asset_pointer.mutex.unlock();
|
||||||
|
self.last_state = self.asset_pointer.state;
|
||||||
|
if (self.last_state == .not_loaded) {
|
||||||
self.asset_pointer.load(Game.alloc);
|
self.asset_pointer.load(Game.alloc);
|
||||||
self.last_state = self.asset_pointer.state;
|
self.last_state = self.asset_pointer.state;
|
||||||
|
}
|
||||||
if (self.last_state == .loaded) {
|
if (self.last_state == .loaded) {
|
||||||
self.data_pointer = @alignCast(@ptrCast(self.asset_pointer.data));
|
self.data_pointer = @alignCast(@ptrCast(self.asset_pointer.data));
|
||||||
}
|
}
|
||||||
|
@@ -11,17 +11,88 @@ sampler: *sdl.GPUSampler,
|
|||||||
pub fn load(path: []const u8, alloc: std.mem.Allocator) Assets.LoadError!@This() {
|
pub fn load(path: []const u8, alloc: std.mem.Allocator) Assets.LoadError!@This() {
|
||||||
_ = alloc;
|
_ = alloc;
|
||||||
var file = Assets.load(.file, path);
|
var file = Assets.load(.file, path);
|
||||||
defer Assets.free(file);
|
const data = (file.getSync() catch return error.DependencyError).bytes;
|
||||||
const data = (try file.getSync()).bytes;
|
|
||||||
|
var width: u32 = undefined;
|
||||||
|
var height: u32 = undefined;
|
||||||
|
var channels: u32 = undefined;
|
||||||
|
const image = c.stbi_load_from_memory(
|
||||||
|
@ptrCast(data),
|
||||||
|
@intCast(data.len),
|
||||||
|
@ptrCast(&width),
|
||||||
|
@ptrCast(&height),
|
||||||
|
@ptrCast(&channels),
|
||||||
|
4,
|
||||||
|
);
|
||||||
|
Assets.free(file);
|
||||||
|
if (image == null) return error.ParsingError;
|
||||||
|
defer c.stbi_image_free(image);
|
||||||
|
const image_slice = image[0..@intCast(width * height * channels)];
|
||||||
|
|
||||||
|
if (width > 8192 or height > 8192) return error.FileTooBig;
|
||||||
|
|
||||||
|
const target_format = sdl.GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
|
||||||
|
const bytes_per_pixel = 4;
|
||||||
|
|
||||||
|
const texture = Graphics.createTexture(
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
target_format,
|
||||||
|
sdl.GPU_TEXTUREUSAGE_SAMPLER | sdl.GPU_TEXTUREUSAGE_COLOR_TARGET,
|
||||||
|
Graphics.MIP_LEVEL,
|
||||||
|
);
|
||||||
|
errdefer Graphics.freeTexture(texture);
|
||||||
|
|
||||||
|
const transfer_buffer_capacity = Graphics.TRANSFER_BUFFER_DEFAULT_CAPACITY;
|
||||||
|
const transfer_buffer = sdl.CreateGPUTransferBuffer(Graphics.device, &.{
|
||||||
|
.size = transfer_buffer_capacity,
|
||||||
|
.usage = sdl.GPU_TRANSFERBUFFERUSAGE_UPLOAD,
|
||||||
|
}) orelse return error.SdlError;
|
||||||
|
defer sdl.ReleaseGPUTransferBuffer(Graphics.device, transfer_buffer);
|
||||||
|
|
||||||
|
var rows_uploaded: u32 = 0;
|
||||||
|
while (rows_uploaded < height) {
|
||||||
|
const rows_to_upload = @min(height - rows_uploaded, transfer_buffer_capacity / width / bytes_per_pixel);
|
||||||
|
if (rows_to_upload == 0) return error.FileTooBig;
|
||||||
|
|
||||||
|
const command_buffer = sdl.AcquireGPUCommandBuffer(Graphics.device) orelse return error.SdlError;
|
||||||
|
{
|
||||||
|
errdefer _ = sdl.CancelGPUCommandBuffer(command_buffer);
|
||||||
|
const copy_pass = sdl.BeginGPUCopyPass(command_buffer) orelse return error.SdlError;
|
||||||
|
defer sdl.EndGPUCopyPass(copy_pass);
|
||||||
|
|
||||||
|
const map: [*]u8 = @ptrCast(sdl.MapGPUTransferBuffer(Graphics.device, transfer_buffer, false) orelse err.sdl());
|
||||||
|
@memcpy(map, image_slice[(rows_uploaded * width * bytes_per_pixel)..((rows_uploaded + rows_to_upload) * width * bytes_per_pixel)]);
|
||||||
|
sdl.UnmapGPUTransferBuffer(Graphics.device, transfer_buffer);
|
||||||
|
|
||||||
|
sdl.UploadToGPUTexture(copy_pass, &sdl.GPUTextureTransferInfo{
|
||||||
|
.offset = 0,
|
||||||
|
.pixels_per_row = width,
|
||||||
|
.rows_per_layer = rows_to_upload,
|
||||||
|
.transfer_buffer = transfer_buffer,
|
||||||
|
}, &sdl.GPUTextureRegion{
|
||||||
|
.texture = texture,
|
||||||
|
.mip_level = 0,
|
||||||
|
.layer = 0,
|
||||||
|
.x = 0,
|
||||||
|
.y = rows_uploaded,
|
||||||
|
.z = 0,
|
||||||
|
.w = width,
|
||||||
|
.h = rows_to_upload,
|
||||||
|
.d = 1,
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
rows_uploaded += rows_to_upload;
|
||||||
|
if (rows_to_upload == height) {
|
||||||
|
sdl.GenerateMipmapsForGPUTexture(command_buffer, texture);
|
||||||
|
}
|
||||||
|
const fence = sdl.SubmitGPUCommandBufferAndAcquireFence(command_buffer) orelse return error.SdlError;
|
||||||
|
defer sdl.ReleaseGPUFence(Graphics.device, fence);
|
||||||
|
if (!sdl.WaitForGPUFences(Graphics.device, true, &fence, 1)) return error.SdlError;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sampler = Graphics.createSampler();
|
||||||
|
|
||||||
var x: i32 = undefined;
|
|
||||||
var y: i32 = undefined;
|
|
||||||
var z: i32 = undefined;
|
|
||||||
const image = c.stbi_load_from_memory(@ptrCast(data), @intCast(data.len), &x, &y, &z, 4);
|
|
||||||
if (image == null) err.stbi();
|
|
||||||
const image_slice = image[0..@intCast(x * y * z)];
|
|
||||||
const texture, const sampler = Graphics.loadTexture(@intCast(x), @intCast(y), image_slice);
|
|
||||||
c.stbi_image_free(image);
|
|
||||||
return .{
|
return .{
|
||||||
.texture = texture,
|
.texture = texture,
|
||||||
.sampler = sampler,
|
.sampler = sampler,
|
||||||
@@ -30,5 +101,6 @@ pub fn load(path: []const u8, alloc: std.mem.Allocator) Assets.LoadError!@This()
|
|||||||
|
|
||||||
pub fn unload(self: @This(), alloc: std.mem.Allocator) void {
|
pub fn unload(self: @This(), alloc: std.mem.Allocator) void {
|
||||||
_ = alloc;
|
_ = alloc;
|
||||||
Graphics.unloadTexture(self.texture, self.sampler);
|
Graphics.freeTexture(self.texture);
|
||||||
|
Graphics.freeSampler(self.sampler);
|
||||||
}
|
}
|
||||||
|
@@ -12,8 +12,8 @@ pub const Mesh = struct {
|
|||||||
vertex_count: usize,
|
vertex_count: usize,
|
||||||
};
|
};
|
||||||
|
|
||||||
var window: *sdl.Window = undefined;
|
pub var window: *sdl.Window = undefined;
|
||||||
var device: *sdl.GPUDevice = undefined;
|
pub var device: *sdl.GPUDevice = undefined;
|
||||||
/// Only available while drawing
|
/// Only available while drawing
|
||||||
var command_buffer: ?*sdl.GPUCommandBuffer = null;
|
var command_buffer: ?*sdl.GPUCommandBuffer = null;
|
||||||
var render_pass: ?*sdl.GPURenderPass = null;
|
var render_pass: ?*sdl.GPURenderPass = null;
|
||||||
@@ -43,10 +43,10 @@ var to_resize: ?[2]u32 = null;
|
|||||||
|
|
||||||
const VERTEX_BUFFER_DEFAULT_CAPACITY = 1024;
|
const VERTEX_BUFFER_DEFAULT_CAPACITY = 1024;
|
||||||
const VERTEX_BUFFER_GROWTH_MULTIPLIER = 2;
|
const VERTEX_BUFFER_GROWTH_MULTIPLIER = 2;
|
||||||
const TRANSFER_BUFFER_DEFAULT_CAPACITY = 4096 * 1024;
|
|
||||||
const BYTES_PER_VERTEX = 5 * 4;
|
const BYTES_PER_VERTEX = 5 * 4;
|
||||||
const DEPTH_FORMAT = sdl.GPU_TEXTUREFORMAT_D32_FLOAT;
|
const DEPTH_FORMAT = sdl.GPU_TEXTUREFORMAT_D32_FLOAT;
|
||||||
const MIP_LEVEL = 4;
|
pub const TRANSFER_BUFFER_DEFAULT_CAPACITY = 256 * 1024;
|
||||||
|
pub const MIP_LEVEL = 4;
|
||||||
|
|
||||||
const Graphics = @This();
|
const Graphics = @This();
|
||||||
pub fn create() void {
|
pub fn create() void {
|
||||||
@@ -200,69 +200,6 @@ pub fn destroy() void {
|
|||||||
sdl.DestroyGPUDevice(Graphics.device);
|
sdl.DestroyGPUDevice(Graphics.device);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn loadTexture(width: u32, height: u32, texture_bytes: []const u8) struct { *sdl.GPUTexture, *sdl.GPUSampler } {
|
|
||||||
const target_format = sdl.GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
|
|
||||||
|
|
||||||
const texture = Graphics.createTexture(
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
target_format,
|
|
||||||
sdl.GPU_TEXTUREUSAGE_SAMPLER | sdl.GPU_TEXTUREUSAGE_COLOR_TARGET,
|
|
||||||
MIP_LEVEL,
|
|
||||||
);
|
|
||||||
|
|
||||||
const temp_command_buffer = sdl.AcquireGPUCommandBuffer(Graphics.device) orelse err.sdl();
|
|
||||||
{
|
|
||||||
const copy_pass = sdl.BeginGPUCopyPass(temp_command_buffer) orelse err.sdl();
|
|
||||||
defer sdl.EndGPUCopyPass(copy_pass);
|
|
||||||
|
|
||||||
const map: [*]u8 = @ptrCast(sdl.MapGPUTransferBuffer(Graphics.device, Graphics.transfer_buffer, true) orelse err.sdl());
|
|
||||||
@memcpy(map, texture_bytes);
|
|
||||||
sdl.UnmapGPUTransferBuffer(Graphics.device, Graphics.transfer_buffer);
|
|
||||||
|
|
||||||
sdl.UploadToGPUTexture(copy_pass, &sdl.GPUTextureTransferInfo{
|
|
||||||
.offset = 0,
|
|
||||||
.pixels_per_row = width,
|
|
||||||
.rows_per_layer = height,
|
|
||||||
.transfer_buffer = Graphics.transfer_buffer,
|
|
||||||
}, &sdl.GPUTextureRegion{
|
|
||||||
.texture = texture,
|
|
||||||
.mip_level = 0,
|
|
||||||
.layer = 0,
|
|
||||||
.x = 0,
|
|
||||||
.y = 0,
|
|
||||||
.z = 0,
|
|
||||||
.w = width,
|
|
||||||
.h = height,
|
|
||||||
.d = 1,
|
|
||||||
}, false);
|
|
||||||
}
|
|
||||||
sdl.GenerateMipmapsForGPUTexture(temp_command_buffer, texture);
|
|
||||||
if (!sdl.SubmitGPUCommandBuffer(temp_command_buffer)) err.sdl();
|
|
||||||
|
|
||||||
const sampler = sdl.CreateGPUSampler(Graphics.device, &sdl.GPUSamplerCreateInfo{
|
|
||||||
.address_mode_u = sdl.GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
|
|
||||||
.address_mode_v = sdl.GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
|
|
||||||
.address_mode_w = sdl.GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
|
|
||||||
.mag_filter = sdl.GPU_FILTER_NEAREST,
|
|
||||||
.min_filter = sdl.GPU_FILTER_LINEAR,
|
|
||||||
.mipmap_mode = sdl.GPU_SAMPLERMIPMAPMODE_LINEAR,
|
|
||||||
.min_lod = 0,
|
|
||||||
.max_lod = 16,
|
|
||||||
.mip_lod_bias = -2,
|
|
||||||
}) orelse err.sdl();
|
|
||||||
|
|
||||||
return .{
|
|
||||||
texture,
|
|
||||||
sampler,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unloadTexture(texture: *sdl.GPUTexture, sampler: *sdl.GPUSampler) void {
|
|
||||||
sdl.ReleaseGPUSampler(Graphics.device, sampler);
|
|
||||||
sdl.ReleaseGPUTexture(Graphics.device, texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn loadMesh(mesh_bytes: []const u8) Mesh {
|
pub fn loadMesh(mesh_bytes: []const u8) Mesh {
|
||||||
std.debug.assert(mesh_bytes.len < Graphics.transfer_buffer_capacity);
|
std.debug.assert(mesh_bytes.len < Graphics.transfer_buffer_capacity);
|
||||||
|
|
||||||
@@ -471,7 +408,7 @@ fn loadShader(path: []const u8, info: sdl.GPUShaderCreateInfo) *sdl.GPUShader {
|
|||||||
return sdl.CreateGPUShader(device, &updated_info) orelse err.sdl();
|
return sdl.CreateGPUShader(device, &updated_info) orelse err.sdl();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn createTexture(width: u32, height: u32, format: c_uint, usage: c_uint, mip_level: u32) *sdl.GPUTexture {
|
pub fn createTexture(width: u32, height: u32, format: c_uint, usage: c_uint, mip_level: u32) *sdl.GPUTexture {
|
||||||
return sdl.CreateGPUTexture(device, &.{
|
return sdl.CreateGPUTexture(device, &.{
|
||||||
.format = format,
|
.format = format,
|
||||||
.layer_count_or_depth = 1,
|
.layer_count_or_depth = 1,
|
||||||
@@ -483,6 +420,28 @@ fn createTexture(width: u32, height: u32, format: c_uint, usage: c_uint, mip_lev
|
|||||||
}) orelse err.sdl();
|
}) orelse err.sdl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn freeTexture(texture: *sdl.GPUTexture) void {
|
||||||
|
sdl.ReleaseGPUTexture(Graphics.device, texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn createSampler() *sdl.GPUSampler {
|
||||||
|
return sdl.CreateGPUSampler(Graphics.device, &sdl.GPUSamplerCreateInfo{
|
||||||
|
.address_mode_u = sdl.GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
|
||||||
|
.address_mode_v = sdl.GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
|
||||||
|
.address_mode_w = sdl.GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,
|
||||||
|
.mag_filter = sdl.GPU_FILTER_NEAREST,
|
||||||
|
.min_filter = sdl.GPU_FILTER_LINEAR,
|
||||||
|
.mipmap_mode = sdl.GPU_SAMPLERMIPMAPMODE_LINEAR,
|
||||||
|
.min_lod = 0,
|
||||||
|
.max_lod = 16,
|
||||||
|
.mip_lod_bias = -2,
|
||||||
|
}) orelse err.sdl();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn freeSampler(sampler: *sdl.GPUSampler) void {
|
||||||
|
sdl.ReleaseGPUSampler(Graphics.device, sampler);
|
||||||
|
}
|
||||||
|
|
||||||
fn resetTextures(width: u32, height: u32) void {
|
fn resetTextures(width: u32, height: u32) void {
|
||||||
sdl.ReleaseGPUTexture(Graphics.device, Graphics.depth_texture);
|
sdl.ReleaseGPUTexture(Graphics.device, Graphics.depth_texture);
|
||||||
Graphics.depth_texture = createTexture(
|
Graphics.depth_texture = createTexture(
|
||||||
|
Reference in New Issue
Block a user