Harbor

branch main
showing the latest snapshot on main
texture.odin 2.6 KB · Plain text
resource/texture.odin 0644 Raw
package resource

import "core:log"
import bk "../backend"

// Load a texture from a file path. Returns pool slot ID, dimensions, and success.
load_texture_from_file :: proc(
	state: ^Resource_State,
	b: ^bk.Backend,
	path: string,
) -> (id: u32, width, height: i32, ok: bool) {
	img, img_ok := load_image_from_file(path)
	if !img_ok {
		return 0, 0, 0, false
	}
	defer destroy_image(img)

	w := i32(img.width)
	h := i32(img.height)
	pixels := img.pixels.buf[:]

	slot, slot_ok := load_texture_from_pixels(state, b, raw_data(pixels), u32(w), u32(h))
	if !slot_ok {
		return 0, 0, 0, false
	}

	state.textures[slot].width = w
	state.textures[slot].height = h

	return slot, w, h, true
}

// Load a texture from raw RGBA pixel data.
load_texture_from_pixels :: proc(
	state: ^Resource_State,
	b: ^bk.Backend,
	pixels: rawptr,
	width, height: u32,
) -> (id: u32, ok: bool) {
	slot, slot_ok := allocate_texture_slot(state)
	if !slot_ok {
		log.error("gpu/resource: texture pool full")
		return 0, false
	}

	tex := &state.textures[slot]

	// Create texture (backend handles staging, layout transitions, and image view)
	tex_handle, tex_ok := b.create_texture(bk.Texture_Desc{
		width  = width,
		height = height,
		format = .R8G8B8A8_SRGB,
		usage  = {.Sampled, .Transfer_Dst},
	}, pixels)
	if !tex_ok {
		return 0, false
	}
	tex.texture = tex_handle
	tex.owns_texture = true

	// Create sampler
	sam_handle, sam_ok := b.create_sampler(bk.Sampler_Desc{
		mag_filter     = .Linear,
		min_filter     = .Linear,
		address_mode_u = .Repeat,
		address_mode_v = .Repeat,
		enable_aniso   = true,
	})
	if !sam_ok {
		b.destroy_texture(tex_handle)
		return 0, false
	}
	tex.sampler = sam_handle

	// Allocate descriptor set
	desc_handle, desc_ok := b.allocate_descriptor_set(state.desc_pool, state.desc_layout)
	if !desc_ok {
		b.destroy_sampler(sam_handle)
		b.destroy_texture(tex_handle)
		return 0, false
	}
	tex.descriptor_set = desc_handle

	// Update descriptor with texture + sampler
	b.update_descriptor_image(desc_handle, 0, tex_handle, sam_handle, .Shader_Read_Only)

	tex.width = i32(width)
	tex.height = i32(height)
	tex.active = true

	return slot, true
}

unload_texture :: proc(state: ^Resource_State, b: ^bk.Backend, id: u32) {
	if id == WHITE_TEXTURE_ID {
		log.warn("gpu/resource: cannot unload the default white texture")
		return
	}
	if id >= MAX_TEXTURES || !state.textures[id].active {
		return
	}
	destroy_texture_slot(b, &state.textures[id])
}

@(private)
allocate_texture_slot :: proc(state: ^Resource_State) -> (u32, bool) {
	for i in 0..<u32(MAX_TEXTURES) {
		if !state.textures[i].active {
			return i, true
		}
	}
	return 0, false
}