1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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
}