Harbor

Changelog 8698b480046a

pin opengl resources

@sky · 1 month ago · parent 1cc7763354e2
1 added 3 modified 0 deleted
gpu/backend/opengl/gl_backend.odin +145 -0 modified
14 unchanged lines hidden
15 15
16 16 default_render_pass: bk.Render_Pass_Handle,
17 17 current_frame: u32,
18 + current_pipeline: bk.Pipeline_Handle,
18 19 frame_active: bool,
19 20 render_pass_active: bool,
21 +
22 + push_constant_buffer: u32,
23 +
24 + buffers: [MAX_BUFFERS]GL_Buffer_Entry,
25 + textures: [MAX_TEXTURES]GL_Texture_Entry,
26 + shaders: [MAX_SHADERS]GL_Shader_Entry,
27 + pipelines: [MAX_PIPELINES]GL_Pipeline_Entry,
28 + descriptors: [MAX_DESCRIPTORS]GL_Descriptor_Entry,
29 + render_passes: [MAX_RENDER_PASSES]GL_Render_Pass_Entry,
30 + framebuffers: [MAX_FRAMEBUFFERS]GL_Framebuffer_Entry,
31 + samplers: [MAX_SAMPLERS]GL_Sampler_Entry,
20 32 }
21 33
34 + MAX_BUFFERS :: 1024
35 + MAX_TEXTURES :: 512
36 + MAX_SHADERS :: 256
37 + MAX_PIPELINES :: 128
38 + MAX_DESCRIPTORS :: 512
39 + MAX_RENDER_PASSES :: 32
40 + MAX_FRAMEBUFFERS :: 64
41 + MAX_SAMPLERS :: 64
42 +
43 + OPENGL_BINDINGS_PER_GROUP :: 16
44 + OPENGL_PUSH_CONSTANT_BINDING :: 15
45 + PUSH_CONSTANT_MAX_SIZE :: 256
46 +
47 + GL_Buffer_Entry :: struct {
48 + id: u32,
49 + size: u64,
50 + usage: bk.Buffer_Usage_Flags,
51 + mapped_ptr: rawptr,
52 + active: bool,
53 + }
54 +
55 + GL_Texture_Entry :: struct {
56 + id: u32,
57 + width: u32,
58 + height: u32,
59 + format: bk.Format,
60 + usage: bk.Image_Usage_Flags,
61 + active: bool,
62 + }
63 +
64 + GL_Shader_Entry :: struct {
65 + id: u32,
66 + stage: bk.Shader_Stage,
67 + active: bool,
68 + }
69 +
70 + GL_Pipeline_Entry :: struct {
71 + program: u32,
72 + vao: u32,
73 + topology: u32,
74 + enable_blending: bool,
75 + blend_mode: bk.Blend_Mode,
76 + enable_depth_test: bool,
77 + cull_mode: bk.Cull_Mode,
78 + front_face: bk.Front_Face,
79 + vertex_stride: u32,
80 + push_constant_size: u32,
81 + push_constant_stages: bk.Shader_Stage_Flags,
82 + is_compute: bool,
83 + active: bool,
84 + }
85 +
86 + Descriptor_Kind :: enum {
87 + Set_Layout,
88 + Pool,
89 + Set,
90 + }
91 +
92 + GL_Descriptor_Binding :: struct {
93 + binding: u32,
94 + type: bk.Descriptor_Type,
95 + buffer: bk.Buffer_Handle,
96 + texture: bk.Texture_Handle,
97 + sampler: bk.Sampler_Handle,
98 + buf_size: u64,
99 + }
100 +
101 + GL_Descriptor_Entry :: struct {
102 + kind: Descriptor_Kind,
103 + layout_bindings: [16]bk.Descriptor_Set_Layout_Binding,
104 + layout_count: u32,
105 + bindings: [16]GL_Descriptor_Binding,
106 + binding_count: u32,
107 + active: bool,
108 + }
109 +
110 + GL_Render_Pass_Entry :: struct {
111 + desc: bk.Render_Pass_Desc,
112 + active: bool,
113 + }
114 +
115 + GL_Framebuffer_Entry :: struct {
116 + id: u32,
117 + color_tex: bk.Texture_Handle,
118 + depth_tex: bk.Texture_Handle,
119 + width: u32,
120 + height: u32,
121 + active: bool,
122 + }
123 +
124 + GL_Sampler_Entry :: struct {
125 + id: u32,
126 + active: bool,
127 + }
128 +
22 129 init_opengl_backend :: proc(surface: bk.Surface_Desc, width, height: u32, title: cstring) -> (backend: bk.Backend, ok: bool) {
23 130 state := new(OpenGL_State)
24 131 if state == nil {
13 unchanged lines hidden
38 145 state.width = width
39 146 state.height = height
40 147 state.default_render_pass = bk.Render_Pass_Handle(1)
148 + state.render_passes[1].desc = {has_color = true, has_depth = true, color_format = .R8G8B8A8_UNORM, depth_format = .D32_SFLOAT}
149 + state.render_passes[1].active = true
41 150
42 151 gl.load_up_to(4, 5, gl_context_set_proc_address)
43 152 gl.ClipControl(gl.UPPER_LEFT, gl.ZERO_TO_ONE)
44 153 gl.Disable(gl.DEPTH_TEST)
45 154 gl.Disable(gl.SCISSOR_TEST)
46 155 gl.Disable(gl.BLEND)
156 + gl.CreateBuffers(1, &state.push_constant_buffer)
157 + gl.NamedBufferData(state.push_constant_buffer, PUSH_CONSTANT_MAX_SIZE, nil, gl.DYNAMIC_DRAW)
47 158
48 159 backend = bk.Backend{
49 160 capabilities = bk.advertised_render_capabilities(),
75 unchanged lines hidden
125 236
126 237 shutdown_opengl :: proc() {
127 238 if g_gl == nil do return
239 + if g_gl.push_constant_buffer != 0 {
240 + id := g_gl.push_constant_buffer
241 + gl.DeleteBuffers(1, &id)
242 + }
243 + for i in 1..<MAX_PIPELINES {
244 + if g_gl.pipelines[i].active {
245 + destroy_graphics_pipeline_opengl(bk.Pipeline_Handle(i))
246 + }
247 + }
248 + for i in 1..<MAX_SHADERS {
249 + if g_gl.shaders[i].active {
250 + destroy_shader_opengl(bk.Shader_Handle(i))
251 + }
252 + }
253 + for i in 1..<MAX_BUFFERS {
254 + if g_gl.buffers[i].active {
255 + destroy_buffer_opengl(bk.Buffer_Handle(i))
256 + }
257 + }
258 + for i in 1..<MAX_TEXTURES {
259 + if g_gl.textures[i].active {
260 + destroy_texture_opengl(bk.Texture_Handle(i))
261 + }
262 + }
263 + for i in 1..<MAX_FRAMEBUFFERS {
264 + if g_gl.framebuffers[i].active {
265 + destroy_framebuffer_opengl(bk.Framebuffer_Handle(i))
266 + }
267 + }
268 + for i in 1..<MAX_SAMPLERS {
269 + if g_gl.samplers[i].active {
270 + destroy_sampler_opengl(bk.Sampler_Handle(i))
271 + }
272 + }
128 273 destroy_gl_context(&g_gl.gl_ctx)
129 274 free(g_gl)
130 275 g_gl = nil
32 unchanged lines hidden
gpu/backend/opengl/gl_convert.odin +96 -0 added
1 + package opengl_backend
2 +
3 + import gl "vendor:OpenGL"
4 + import bk ".."
5 +
6 + to_gl_topology :: proc(topology: bk.Topology) -> u32 {
7 + switch topology {
8 + case .Triangle_List: return gl.TRIANGLES
9 + case .Triangle_Strip: return gl.TRIANGLE_STRIP
10 + case .Line_List: return gl.LINES
11 + case .Point_List: return gl.POINTS
12 + }
13 + return gl.TRIANGLES
14 + }
15 +
16 + to_gl_cull_face :: proc(mode: bk.Cull_Mode) -> u32 {
17 + switch mode {
18 + case .Front: return gl.FRONT
19 + case .Back: return gl.BACK
20 + case .Front_And_Back: return gl.FRONT_AND_BACK
21 + case .None: return gl.BACK
22 + }
23 + return gl.BACK
24 + }
25 +
26 + to_gl_front_face :: proc(face: bk.Front_Face) -> u32 {
27 + switch face {
28 + case .Counter_Clockwise: return gl.CCW
29 + case .Clockwise: return gl.CW
30 + }
31 + return gl.CCW
32 + }
33 +
34 + vertex_format_parts :: proc(format: bk.Vertex_Format) -> (size: i32, typ: u32) {
35 + switch format {
36 + case .Float2: return 2, gl.FLOAT
37 + case .Float3: return 3, gl.FLOAT
38 + case .Float4: return 4, gl.FLOAT
39 + }
40 + return 4, gl.FLOAT
41 + }
42 +
43 + to_gl_filter :: proc(filter: bk.Filter) -> i32 {
44 + switch filter {
45 + case .Nearest: return gl.NEAREST
46 + case .Linear: return gl.LINEAR
47 + }
48 + return gl.LINEAR
49 + }
50 +
51 + to_gl_address_mode :: proc(mode: bk.Address_Mode) -> i32 {
52 + switch mode {
53 + case .Repeat: return gl.REPEAT
54 + case .Clamp_To_Edge: return gl.CLAMP_TO_EDGE
55 + case .Clamp_To_Border: return gl.CLAMP_TO_EDGE
56 + }
57 + return gl.CLAMP_TO_EDGE
58 + }
59 +
60 + format_pixel_size :: proc(format: bk.Format) -> u32 {
61 + switch format {
62 + case .R8G8B8A8_SRGB, .R8G8B8A8_UNORM, .B8G8R8A8_SRGB:
63 + return 4
64 + case .D32_SFLOAT:
65 + return 4
66 + case .D32_SFLOAT_S8_UINT, .D24_UNORM_S8_UINT:
67 + return 4
68 + case .Undefined:
69 + return 0
70 + }
71 + return 0
72 + }
73 +
74 + gl_format_info :: proc(format: bk.Format) -> (internal, external, typ: u32, ok: bool) {
75 + switch format {
76 + case .R8G8B8A8_SRGB:
77 + return gl.SRGB8_ALPHA8, gl.RGBA, gl.UNSIGNED_BYTE, true
78 + case .R8G8B8A8_UNORM:
79 + return gl.RGBA8, gl.RGBA, gl.UNSIGNED_BYTE, true
80 + case .B8G8R8A8_SRGB:
81 + return gl.SRGB8_ALPHA8, gl.BGRA, gl.UNSIGNED_BYTE, true
82 + case .D32_SFLOAT:
83 + return gl.DEPTH_COMPONENT32F, gl.DEPTH_COMPONENT, gl.FLOAT, true
84 + case .D24_UNORM_S8_UINT:
85 + return gl.DEPTH24_STENCIL8, gl.DEPTH_STENCIL, gl.UNSIGNED_INT_24_8, true
86 + case .D32_SFLOAT_S8_UINT:
87 + return gl.DEPTH32F_STENCIL8, gl.DEPTH_STENCIL, gl.FLOAT_32_UNSIGNED_INT_24_8_REV, true
88 + case .Undefined:
89 + return 0, 0, 0, false
90 + }
91 + return 0, 0, 0, false
92 + }
93 +
94 + descriptor_binding_point :: proc(group, binding: u32) -> u32 {
95 + return group * OPENGL_BINDINGS_PER_GROUP + binding
96 + }
gpu/backend/opengl/gl_frame.odin +20 -2 modified
46 unchanged lines hidden
47 47 begin_default_pass_opengl(ctx, desc.clear_color)
48 48 return
49 49 }
50 - log.error("gpu/opengl: offscreen render passes not implemented yet")
50 + fb := &g_gl.framebuffers[desc.framebuffer]
51 + if !fb.active {
52 + log.error("gpu/opengl: invalid framebuffer")
53 + return
54 + }
55 + gl.BindFramebuffer(gl.FRAMEBUFFER, fb.id)
56 + gl.Viewport(0, 0, i32(desc.width), i32(desc.height))
57 + gl.ClearColor(desc.clear_color[0], desc.clear_color[1], desc.clear_color[2], desc.clear_color[3])
58 + gl.ClearDepth(f64(desc.clear_depth))
59 + mask := u32(0)
60 + pass := &g_gl.render_passes[desc.pass]
61 + if pass.active {
62 + if pass.desc.has_color do mask |= gl.COLOR_BUFFER_BIT
63 + if pass.desc.has_depth do mask |= gl.DEPTH_BUFFER_BIT
64 + } else {
65 + mask = gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT
66 + }
67 + if mask != 0 do gl.Clear(mask)
68 + g_gl.render_pass_active = true
51 69 }
52 70
53 71 end_render_pass_opengl :: proc(ctx: bk.Frame_Context) {
11 unchanged lines hidden
65 83 }
66 84
67 85 set_depth_bias_opengl :: proc(ctx: bk.Frame_Context, constant, slope: f32) {
68 - log.error("gpu/opengl: depth bias not implemented yet")
86 + gl.PolygonOffset(slope, constant)
69 87 }
gpu/backend/opengl/gl_ops.odin +608 -65 modified
1 1 package opengl_backend
2 2
3 + import "core:fmt"
3 4 import "core:log"
4 5 import gl "vendor:OpenGL"
5 6
6 7 import bk ".."
7 8
8 - create_graphics_pipeline_opengl :: proc(desc: bk.Pipeline_Desc) -> (bk.Pipeline_Handle, bool) {
9 - log.error("gpu/opengl: graphics pipelines not implemented yet")
9 + alloc_buffer_handle :: proc() -> (bk.Buffer_Handle, bool) {
10 + for i in 1..<u64(MAX_BUFFERS) {
11 + if !g_gl.buffers[i].active do return bk.Buffer_Handle(i), true
12 + }
13 + return bk.NULL_BUFFER, false
14 + }
15 +
16 + alloc_texture_handle :: proc() -> (bk.Texture_Handle, bool) {
17 + for i in 1..<u64(MAX_TEXTURES) {
18 + if !g_gl.textures[i].active do return bk.Texture_Handle(i), true
19 + }
20 + return bk.NULL_TEXTURE, false
21 + }
22 +
23 + alloc_shader_handle :: proc() -> (bk.Shader_Handle, bool) {
24 + for i in 1..<u64(MAX_SHADERS) {
25 + if !g_gl.shaders[i].active do return bk.Shader_Handle(i), true
26 + }
27 + return bk.NULL_SHADER, false
28 + }
29 +
30 + alloc_pipeline_handle :: proc() -> (bk.Pipeline_Handle, bool) {
31 + for i in 1..<u64(MAX_PIPELINES) {
32 + if !g_gl.pipelines[i].active do return bk.Pipeline_Handle(i), true
33 + }
10 34 return bk.NULL_PIPELINE, false
11 35 }
12 36
13 - destroy_graphics_pipeline_opengl :: proc(handle: bk.Pipeline_Handle) {}
37 + alloc_descriptor_handle :: proc() -> (bk.Descriptor_Handle, bool) {
38 + for i in 1..<u64(MAX_DESCRIPTORS) {
39 + if !g_gl.descriptors[i].active do return bk.Descriptor_Handle(i), true
40 + }
41 + return bk.NULL_DESCRIPTOR, false
42 + }
14 43
15 - bind_graphics_pipeline_opengl :: proc(ctx: bk.Frame_Context, handle: bk.Pipeline_Handle) {}
44 + alloc_render_pass_handle :: proc() -> (bk.Render_Pass_Handle, bool) {
45 + for i in 1..<u64(MAX_RENDER_PASSES) {
46 + if !g_gl.render_passes[i].active do return bk.Render_Pass_Handle(i), true
47 + }
48 + return bk.NULL_RENDER_PASS, false
49 + }
16 50
17 - push_constants_opengl :: proc(ctx: bk.Frame_Context, pipeline: bk.Pipeline_Handle, stages: bk.Shader_Stage_Flags, offset, size: u32, data: rawptr) {
18 - log.error("gpu/opengl: push constants not implemented yet")
51 + alloc_framebuffer_handle :: proc() -> (bk.Framebuffer_Handle, bool) {
52 + for i in 1..<u64(MAX_FRAMEBUFFERS) {
53 + if !g_gl.framebuffers[i].active do return bk.Framebuffer_Handle(i), true
54 + }
55 + return bk.NULL_FRAMEBUFFER, false
19 56 }
20 57
58 + alloc_sampler_handle :: proc() -> (bk.Sampler_Handle, bool) {
59 + for i in 1..<u64(MAX_SAMPLERS) {
60 + if !g_gl.samplers[i].active do return bk.Sampler_Handle(i), true
61 + }
62 + return bk.NULL_SAMPLER, false
63 + }
64 +
21 65 create_buffer_opengl :: proc(desc: bk.Buffer_Desc) -> (bk.Buffer_Handle, bool) {
22 - log.error("gpu/opengl: buffers not implemented yet")
23 - return bk.NULL_BUFFER, false
66 + handle, ok := alloc_buffer_handle()
67 + if !ok do return bk.NULL_BUFFER, false
68 +
69 + id: u32
70 + gl.CreateBuffers(1, &id)
71 + if id == 0 {
72 + log.error("gpu/opengl: glCreateBuffers failed")
73 + return bk.NULL_BUFFER, false
74 + }
75 +
76 + usage := u32(gl.DYNAMIC_DRAW) if .Host_Visible in desc.memory else u32(gl.STATIC_DRAW)
77 + gl.NamedBufferData(id, int(desc.size), nil, usage)
78 +
79 + entry := &g_gl.buffers[handle]
80 + entry.id = id
81 + entry.size = desc.size
82 + entry.usage = desc.usage
83 + entry.active = true
84 + return handle, true
24 85 }
25 86
26 87 create_buffer_staged_opengl :: proc(data: rawptr, size: int, usage: bk.Buffer_Usage_Flags) -> (bk.Buffer_Handle, bool) {
27 - log.error("gpu/opengl: staged buffers not implemented yet")
28 - return bk.NULL_BUFFER, false
88 + handle, ok := alloc_buffer_handle()
89 + if !ok do return bk.NULL_BUFFER, false
90 +
91 + id: u32
92 + gl.CreateBuffers(1, &id)
93 + if id == 0 {
94 + log.error("gpu/opengl: glCreateBuffers failed")
95 + return bk.NULL_BUFFER, false
96 + }
97 + gl.NamedBufferData(id, size, data, gl.STATIC_DRAW)
98 +
99 + entry := &g_gl.buffers[handle]
100 + entry.id = id
101 + entry.size = u64(size)
102 + entry.usage = usage
103 + entry.active = true
104 + return handle, true
29 105 }
30 106
31 - destroy_buffer_opengl :: proc(handle: bk.Buffer_Handle) {}
107 + destroy_buffer_opengl :: proc(handle: bk.Buffer_Handle) {
108 + if handle == bk.NULL_BUFFER do return
109 + entry := &g_gl.buffers[handle]
110 + if !entry.active do return
111 + if entry.id != 0 {
112 + id := entry.id
113 + gl.DeleteBuffers(1, &id)
114 + }
115 + entry^ = {}
116 + }
32 117
33 118 map_buffer_opengl :: proc(handle: bk.Buffer_Handle) -> rawptr {
34 - log.error("gpu/opengl: buffer mapping not implemented yet")
35 - return nil
119 + if handle == bk.NULL_BUFFER do return nil
120 + entry := &g_gl.buffers[handle]
121 + if !entry.active do return nil
122 + ptr := gl.MapNamedBufferRange(entry.id, 0, int(entry.size), gl.MAP_WRITE_BIT | gl.MAP_INVALIDATE_BUFFER_BIT)
123 + entry.mapped_ptr = ptr
124 + return ptr
36 125 }
37 126
38 - unmap_buffer_opengl :: proc(handle: bk.Buffer_Handle) {}
127 + unmap_buffer_opengl :: proc(handle: bk.Buffer_Handle) {
128 + if handle == bk.NULL_BUFFER do return
129 + entry := &g_gl.buffers[handle]
130 + if !entry.active || entry.mapped_ptr == nil do return
131 + gl.UnmapNamedBuffer(entry.id)
132 + entry.mapped_ptr = nil
133 + }
39 134
40 135 get_buffer_mapped_opengl :: proc(handle: bk.Buffer_Handle) -> rawptr {
41 - return nil
136 + if handle == bk.NULL_BUFFER do return nil
137 + entry := &g_gl.buffers[handle]
138 + if !entry.active do return nil
139 + if entry.mapped_ptr == nil do return map_buffer_opengl(handle)
140 + return entry.mapped_ptr
42 141 }
43 142
44 - bind_vertex_buffer_opengl :: proc(ctx: bk.Frame_Context, handle: bk.Buffer_Handle) {}
143 + bind_vertex_buffer_opengl :: proc(ctx: bk.Frame_Context, handle: bk.Buffer_Handle) {
144 + if handle == bk.NULL_BUFFER || g_gl.current_pipeline == bk.NULL_PIPELINE do return
145 + buffer := &g_gl.buffers[handle]
146 + pipeline := &g_gl.pipelines[g_gl.current_pipeline]
147 + if !buffer.active || !pipeline.active || pipeline.vao == 0 do return
148 + gl.VertexArrayVertexBuffer(pipeline.vao, 0, buffer.id, 0, i32(pipeline.vertex_stride))
149 + }
45 150
46 - bind_index_buffer_opengl :: proc(ctx: bk.Frame_Context, handle: bk.Buffer_Handle) {}
151 + bind_index_buffer_opengl :: proc(ctx: bk.Frame_Context, handle: bk.Buffer_Handle) {
152 + if handle == bk.NULL_BUFFER || g_gl.current_pipeline == bk.NULL_PIPELINE do return
153 + buffer := &g_gl.buffers[handle]
154 + pipeline := &g_gl.pipelines[g_gl.current_pipeline]
155 + if !buffer.active || !pipeline.active || pipeline.vao == 0 do return
156 + gl.VertexArrayElementBuffer(pipeline.vao, buffer.id)
157 + }
47 158
48 159 create_texture_opengl :: proc(desc: bk.Texture_Desc, pixels: rawptr) -> (bk.Texture_Handle, bool) {
49 - log.error("gpu/opengl: textures not implemented yet")
50 - return bk.NULL_TEXTURE, false
160 + return create_texture_internal(desc, pixels)
51 161 }
52 162
53 - destroy_texture_opengl :: proc(handle: bk.Texture_Handle) {}
163 + create_image_opengl :: proc(desc: bk.Texture_Desc) -> (bk.Texture_Handle, bool) {
164 + return create_texture_internal(desc, nil)
165 + }
54 166
55 - create_sampler_opengl :: proc(desc: bk.Sampler_Desc) -> (bk.Sampler_Handle, bool) {
56 - log.error("gpu/opengl: samplers not implemented yet")
57 - return bk.NULL_SAMPLER, false
167 + create_texture_internal :: proc(desc: bk.Texture_Desc, pixels: rawptr) -> (bk.Texture_Handle, bool) {
168 + handle, ok := alloc_texture_handle()
169 + if !ok do return bk.NULL_TEXTURE, false
170 + internal, external, typ, fmt_ok := gl_format_info(desc.format)
171 + if !fmt_ok {
172 + log.errorf("gpu/opengl: unsupported texture format %v", desc.format)
173 + return bk.NULL_TEXTURE, false
174 + }
175 +
176 + id: u32
177 + gl.CreateTextures(gl.TEXTURE_2D, 1, &id)
178 + if id == 0 {
179 + log.error("gpu/opengl: glCreateTextures failed")
180 + return bk.NULL_TEXTURE, false
181 + }
182 + gl.TextureStorage2D(id, 1, internal, i32(desc.width), i32(desc.height))
183 + if pixels != nil && format_pixel_size(desc.format) > 0 {
184 + gl.TextureSubImage2D(id, 0, 0, 0, i32(desc.width), i32(desc.height), external, typ, pixels)
185 + }
186 +
187 + entry := &g_gl.textures[handle]
188 + entry.id = id
189 + entry.width = desc.width
190 + entry.height = desc.height
191 + entry.format = desc.format
192 + entry.usage = desc.usage
193 + entry.active = true
194 + return handle, true
58 195 }
59 196
60 - destroy_sampler_opengl :: proc(handle: bk.Sampler_Handle) {}
197 + destroy_texture_opengl :: proc(handle: bk.Texture_Handle) {
198 + if handle == bk.NULL_TEXTURE do return
199 + entry := &g_gl.textures[handle]
200 + if !entry.active do return
201 + if entry.id != 0 {
202 + id := entry.id
203 + gl.DeleteTextures(1, &id)
204 + }
205 + entry^ = {}
206 + }
61 207
62 - create_image_opengl :: proc(desc: bk.Texture_Desc) -> (bk.Texture_Handle, bool) {
63 - log.error("gpu/opengl: images not implemented yet")
64 - return bk.NULL_TEXTURE, false
208 + destroy_image_opengl :: proc(handle: bk.Texture_Handle) {
209 + destroy_texture_opengl(handle)
65 210 }
66 211
67 212 create_image_view_opengl :: proc(texture: bk.Texture_Handle, format: bk.Format, aspect: bk.Image_Aspect_Flags) -> bool {
68 - log.error("gpu/opengl: image views not implemented yet")
69 - return false
213 + if texture == bk.NULL_TEXTURE do return false
214 + entry := &g_gl.textures[texture]
215 + return entry.active
70 216 }
71 217
72 - destroy_image_opengl :: proc(handle: bk.Texture_Handle) {}
218 + create_sampler_opengl :: proc(desc: bk.Sampler_Desc) -> (bk.Sampler_Handle, bool) {
219 + handle, ok := alloc_sampler_handle()
220 + if !ok do return bk.NULL_SAMPLER, false
221 + id: u32
222 + gl.CreateSamplers(1, &id)
223 + if id == 0 {
224 + log.error("gpu/opengl: glCreateSamplers failed")
225 + return bk.NULL_SAMPLER, false
226 + }
227 + gl.SamplerParameteri(id, gl.TEXTURE_MIN_FILTER, to_gl_filter(desc.min_filter))
228 + gl.SamplerParameteri(id, gl.TEXTURE_MAG_FILTER, to_gl_filter(desc.mag_filter))
229 + gl.SamplerParameteri(id, gl.TEXTURE_WRAP_S, to_gl_address_mode(desc.address_mode_u))
230 + gl.SamplerParameteri(id, gl.TEXTURE_WRAP_T, to_gl_address_mode(desc.address_mode_v))
73 231
232 + entry := &g_gl.samplers[handle]
233 + entry.id = id
234 + entry.active = true
235 + return handle, true
236 + }
237 +
238 + destroy_sampler_opengl :: proc(handle: bk.Sampler_Handle) {
239 + if handle == bk.NULL_SAMPLER do return
240 + entry := &g_gl.samplers[handle]
241 + if !entry.active do return
242 + if entry.id != 0 {
243 + id := entry.id
244 + gl.DeleteSamplers(1, &id)
245 + }
246 + entry^ = {}
247 + }
248 +
249 + create_shader_module_opengl :: proc(desc: bk.Shader_Module_Desc) -> (bk.Shader_Handle, bool) {
250 + if desc.format != .GLSL {
251 + log.error("gpu/opengl: shader module requires GLSL source")
252 + return bk.NULL_SHADER, false
253 + }
254 +
255 + shader_type: u32
256 + switch desc.stage {
257 + case .Vertex: shader_type = gl.VERTEX_SHADER
258 + case .Fragment: shader_type = gl.FRAGMENT_SHADER
259 + case .Compute: shader_type = gl.COMPUTE_SHADER
260 + }
261 +
262 + id := gl.CreateShader(shader_type)
263 + if id == 0 {
264 + log.error("gpu/opengl: glCreateShader failed")
265 + return bk.NULL_SHADER, false
266 + }
267 + source := cstring(raw_data(desc.data))
268 + length := i32(len(desc.data))
269 + gl.ShaderSource(id, 1, &source, &length)
270 + gl.CompileShader(id)
271 +
272 + status: i32
273 + gl.GetShaderiv(id, gl.COMPILE_STATUS, &status)
274 + if status == 0 {
275 + log_opengl_shader_info(id, desc.name)
276 + gl.DeleteShader(id)
277 + return bk.NULL_SHADER, false
278 + }
279 +
280 + handle, ok := alloc_shader_handle()
281 + if !ok {
282 + gl.DeleteShader(id)
283 + return bk.NULL_SHADER, false
284 + }
285 + entry := &g_gl.shaders[handle]
286 + entry.id = id
287 + entry.stage = desc.stage
288 + entry.active = true
289 + return handle, true
290 + }
291 +
292 + destroy_shader_opengl :: proc(handle: bk.Shader_Handle) {
293 + if handle == bk.NULL_SHADER do return
294 + entry := &g_gl.shaders[handle]
295 + if !entry.active do return
296 + if entry.id != 0 do gl.DeleteShader(entry.id)
297 + entry^ = {}
298 + }
299 +
300 + create_graphics_pipeline_opengl :: proc(desc: bk.Pipeline_Desc) -> (bk.Pipeline_Handle, bool) {
301 + if desc.vert_shader == bk.NULL_SHADER || desc.frag_shader == bk.NULL_SHADER {
302 + log.error("gpu/opengl: graphics pipeline requires vertex and fragment shaders")
303 + return bk.NULL_PIPELINE, false
304 + }
305 + vs := &g_gl.shaders[desc.vert_shader]
306 + fs := &g_gl.shaders[desc.frag_shader]
307 + if !vs.active || !fs.active {
308 + log.error("gpu/opengl: graphics pipeline shader handle is invalid")
309 + return bk.NULL_PIPELINE, false
310 + }
311 +
312 + program, link_ok := link_program(vs.id, fs.id, 0, "graphics")
313 + if !link_ok do return bk.NULL_PIPELINE, false
314 +
315 + vao: u32
316 + gl.CreateVertexArrays(1, &vao)
317 + if vao == 0 {
318 + gl.DeleteProgram(program)
319 + log.error("gpu/opengl: glCreateVertexArrays failed")
320 + return bk.NULL_PIPELINE, false
321 + }
322 +
323 + for attr in desc.vertex_attributes {
324 + size, typ := vertex_format_parts(attr.format)
325 + gl.EnableVertexArrayAttrib(vao, attr.location)
326 + gl.VertexArrayAttribFormat(vao, attr.location, size, typ, false, attr.offset)
327 + gl.VertexArrayAttribBinding(vao, attr.location, 0)
328 + }
329 +
330 + handle, ok := alloc_pipeline_handle()
331 + if !ok {
332 + gl.DeleteVertexArrays(1, &vao)
333 + gl.DeleteProgram(program)
334 + return bk.NULL_PIPELINE, false
335 + }
336 +
337 + vertex_stride: u32
338 + if len(desc.vertex_bindings) > 0 do vertex_stride = desc.vertex_bindings[0].stride
339 + entry := &g_gl.pipelines[handle]
340 + entry.program = program
341 + entry.vao = vao
342 + entry.topology = to_gl_topology(desc.topology)
343 + entry.enable_blending = desc.enable_blending
344 + entry.blend_mode = desc.blend_mode
345 + entry.enable_depth_test = desc.enable_depth_test
346 + entry.cull_mode = desc.cull_mode
347 + entry.front_face = desc.front_face
348 + entry.vertex_stride = vertex_stride
349 + entry.push_constant_size = desc.push_constant_size
350 + entry.push_constant_stages = desc.push_constant_stages
351 + entry.active = true
352 + return handle, true
353 + }
354 +
355 + destroy_graphics_pipeline_opengl :: proc(handle: bk.Pipeline_Handle) {
356 + if handle == bk.NULL_PIPELINE do return
357 + entry := &g_gl.pipelines[handle]
358 + if !entry.active do return
359 + if entry.vao != 0 {
360 + id := entry.vao
361 + gl.DeleteVertexArrays(1, &id)
362 + }
363 + if entry.program != 0 do gl.DeleteProgram(entry.program)
364 + entry^ = {}
365 + }
366 +
367 + bind_graphics_pipeline_opengl :: proc(ctx: bk.Frame_Context, handle: bk.Pipeline_Handle) {
368 + if handle == bk.NULL_PIPELINE do return
369 + entry := &g_gl.pipelines[handle]
370 + if !entry.active do return
371 + g_gl.current_pipeline = handle
372 + gl.UseProgram(entry.program)
373 + gl.BindVertexArray(entry.vao)
374 + apply_graphics_state(entry)
375 + }
376 +
377 + create_compute_pipeline_opengl :: proc(shader: bk.Shader_Handle, num_buffers: u32, push_constant_size: u32) -> (bk.Pipeline_Handle, bool) {
378 + if shader == bk.NULL_SHADER do return bk.NULL_PIPELINE, false
379 + cs := &g_gl.shaders[shader]
380 + if !cs.active || cs.stage != .Compute {
381 + log.error("gpu/opengl: compute pipeline requires compute shader")
382 + return bk.NULL_PIPELINE, false
383 + }
384 + program, link_ok := link_program(0, 0, cs.id, "compute")
385 + if !link_ok do return bk.NULL_PIPELINE, false
386 +
387 + handle, ok := alloc_pipeline_handle()
388 + if !ok {
389 + gl.DeleteProgram(program)
390 + return bk.NULL_PIPELINE, false
391 + }
392 + entry := &g_gl.pipelines[handle]
393 + entry.program = program
394 + entry.push_constant_size = push_constant_size
395 + entry.is_compute = true
396 + entry.active = true
397 + return handle, true
398 + }
399 +
400 + destroy_compute_pipeline_opengl :: proc(handle: bk.Pipeline_Handle) {
401 + destroy_graphics_pipeline_opengl(handle)
402 + }
403 +
404 + bind_compute_pipeline_opengl :: proc(ctx: bk.Frame_Context, handle: bk.Pipeline_Handle) {
405 + if handle == bk.NULL_PIPELINE do return
406 + entry := &g_gl.pipelines[handle]
407 + if !entry.active || !entry.is_compute do return
408 + g_gl.current_pipeline = handle
409 + gl.UseProgram(entry.program)
410 + }
411 +
412 + push_constants_opengl :: proc(ctx: bk.Frame_Context, pipeline: bk.Pipeline_Handle, stages: bk.Shader_Stage_Flags, offset, size: u32, data: rawptr) {
413 + if g_gl == nil || g_gl.push_constant_buffer == 0 || size == 0 do return
414 + if offset + size > PUSH_CONSTANT_MAX_SIZE {
415 + log.error("gpu/opengl: push constants exceed OpenGL reserved buffer size")
416 + return
417 + }
418 + gl.NamedBufferSubData(g_gl.push_constant_buffer, int(offset), int(size), data)
419 + gl.BindBufferBase(gl.UNIFORM_BUFFER, OPENGL_PUSH_CONSTANT_BINDING, g_gl.push_constant_buffer)
420 + }
421 +
74 422 create_descriptor_set_layout_opengl :: proc(bindings: []bk.Descriptor_Set_Layout_Binding) -> (bk.Descriptor_Handle, bool) {
75 - log.error("gpu/opengl: descriptor set layouts not implemented yet")
76 - return bk.NULL_DESCRIPTOR, false
423 + if len(bindings) > 16 {
424 + log.error("gpu/opengl: descriptor set layout exceeds 16 bindings")
425 + return bk.NULL_DESCRIPTOR, false
426 + }
427 + handle, ok := alloc_descriptor_handle()
428 + if !ok do return bk.NULL_DESCRIPTOR, false
429 + entry := &g_gl.descriptors[handle]
430 + entry.kind = .Set_Layout
431 + entry.layout_count = u32(len(bindings))
432 + for b, i in bindings do entry.layout_bindings[i] = b
433 + entry.active = true
434 + return handle, true
77 435 }
78 436
79 - destroy_descriptor_set_layout_opengl :: proc(handle: bk.Descriptor_Handle) {}
437 + destroy_descriptor_set_layout_opengl :: proc(handle: bk.Descriptor_Handle) {
438 + if handle == bk.NULL_DESCRIPTOR do return
439 + g_gl.descriptors[handle] = {}
440 + }
80 441
81 442 create_descriptor_pool_opengl :: proc(max_sets: u32, types: []bk.Descriptor_Type, counts: []u32) -> (bk.Descriptor_Handle, bool) {
82 - log.error("gpu/opengl: descriptor pools not implemented yet")
83 - return bk.NULL_DESCRIPTOR, false
443 + handle, ok := alloc_descriptor_handle()
444 + if !ok do return bk.NULL_DESCRIPTOR, false
445 + entry := &g_gl.descriptors[handle]
446 + entry.kind = .Pool
447 + entry.active = true
448 + return handle, true
84 449 }
85 450
86 - destroy_descriptor_pool_opengl :: proc(handle: bk.Descriptor_Handle) {}
451 + destroy_descriptor_pool_opengl :: proc(handle: bk.Descriptor_Handle) {
452 + if handle == bk.NULL_DESCRIPTOR do return
453 + g_gl.descriptors[handle] = {}
454 + }
87 455
88 456 allocate_descriptor_set_opengl :: proc(pool: bk.Descriptor_Handle, layout: bk.Descriptor_Handle) -> (bk.Descriptor_Handle, bool) {
89 - log.error("gpu/opengl: descriptor sets not implemented yet")
90 - return bk.NULL_DESCRIPTOR, false
457 + if pool == bk.NULL_DESCRIPTOR || layout == bk.NULL_DESCRIPTOR do return bk.NULL_DESCRIPTOR, false
458 + layout_entry := &g_gl.descriptors[layout]
459 + if !layout_entry.active || layout_entry.kind != .Set_Layout do return bk.NULL_DESCRIPTOR, false
460 + handle, ok := alloc_descriptor_handle()
461 + if !ok do return bk.NULL_DESCRIPTOR, false
462 + entry := &g_gl.descriptors[handle]
463 + entry.kind = .Set
464 + entry.layout_count = layout_entry.layout_count
465 + entry.binding_count = layout_entry.layout_count
466 + for i in 0..<int(layout_entry.layout_count) {
467 + lb := layout_entry.layout_bindings[i]
468 + entry.layout_bindings[i] = lb
469 + entry.bindings[i].binding = lb.binding
470 + entry.bindings[i].type = lb.type
471 + }
472 + entry.active = true
473 + return handle, true
91 474 }
92 475
93 476 bind_descriptor_set_opengl :: proc(ctx: bk.Frame_Context, pipeline: bk.Pipeline_Handle, set: bk.Descriptor_Handle, index: u32) {
94 - log.error("gpu/opengl: descriptor binding not implemented yet")
477 + if set == bk.NULL_DESCRIPTOR do return
478 + entry := &g_gl.descriptors[set]
479 + if !entry.active || entry.kind != .Set do return
480 + for i in 0..<int(entry.binding_count) {
481 + b := entry.bindings[i]
482 + point := descriptor_binding_point(index, b.binding)
483 + switch b.type {
484 + case .Combined_Image_Sampler:
485 + if b.texture != bk.NULL_TEXTURE {
486 + tex := &g_gl.textures[b.texture]
487 + if tex.active do gl.BindTextureUnit(point, tex.id)
488 + }
489 + if b.sampler != bk.NULL_SAMPLER {
490 + samp := &g_gl.samplers[b.sampler]
491 + if samp.active do gl.BindSampler(point, samp.id)
492 + }
493 + case .Uniform_Buffer:
494 + if point == OPENGL_PUSH_CONSTANT_BINDING {
495 + log.error("gpu/opengl: descriptor binding collides with reserved push-constant binding")
496 + continue
497 + }
498 + if b.buffer != bk.NULL_BUFFER {
499 + buf := &g_gl.buffers[b.buffer]
500 + if buf.active do gl.BindBufferBase(gl.UNIFORM_BUFFER, point, buf.id)
501 + }
502 + case .Storage_Buffer:
503 + if b.buffer != bk.NULL_BUFFER {
504 + buf := &g_gl.buffers[b.buffer]
505 + if buf.active do gl.BindBufferBase(gl.SHADER_STORAGE_BUFFER, point, buf.id)
506 + }
507 + }
508 + }
95 509 }
96 510
97 511 update_descriptor_image_opengl :: proc(set: bk.Descriptor_Handle, binding: u32, texture: bk.Texture_Handle, sampler: bk.Sampler_Handle, layout: bk.Image_Layout) {
98 - log.error("gpu/opengl: image descriptor updates not implemented yet")
512 + if set == bk.NULL_DESCRIPTOR do return
513 + entry := &g_gl.descriptors[set]
514 + if !entry.active || entry.kind != .Set do return
515 + for i in 0..<int(entry.binding_count) {
516 + if entry.bindings[i].binding == binding {
517 + entry.bindings[i].texture = texture
518 + entry.bindings[i].sampler = sampler
519 + return
520 + }
521 + }
99 522 }
100 523
101 524 update_descriptor_buffer_opengl :: proc(set: bk.Descriptor_Handle, binding: u32, buffer: bk.Buffer_Handle, size: u64) {
102 - log.error("gpu/opengl: buffer descriptor updates not implemented yet")
525 + if set == bk.NULL_DESCRIPTOR do return
526 + entry := &g_gl.descriptors[set]
527 + if !entry.active || entry.kind != .Set do return
528 + for i in 0..<int(entry.binding_count) {
529 + if entry.bindings[i].binding == binding {
530 + entry.bindings[i].buffer = buffer
531 + entry.bindings[i].buf_size = size
532 + return
533 + }
534 + }
103 535 }
104 536
105 537 create_render_pass_opengl :: proc(desc: bk.Render_Pass_Desc) -> (bk.Render_Pass_Handle, bool) {
106 - log.error("gpu/opengl: render pass objects not implemented yet")
107 - return bk.NULL_RENDER_PASS, false
538 + handle, ok := alloc_render_pass_handle()
539 + if !ok do return bk.NULL_RENDER_PASS, false
540 + entry := &g_gl.render_passes[handle]
541 + entry.desc = desc
542 + entry.active = true
543 + return handle, true
108 544 }
109 545
110 - destroy_render_pass_opengl :: proc(handle: bk.Render_Pass_Handle) {}
546 + destroy_render_pass_opengl :: proc(handle: bk.Render_Pass_Handle) {
547 + if handle == bk.NULL_RENDER_PASS || handle == g_gl.default_render_pass do return
548 + g_gl.render_passes[handle] = {}
549 + }
111 550
112 551 create_framebuffer_opengl :: proc(desc: bk.Framebuffer_Desc) -> (bk.Framebuffer_Handle, bool) {
113 - log.error("gpu/opengl: framebuffers not implemented yet")
114 - return bk.NULL_FRAMEBUFFER, false
115 - }
552 + handle, ok := alloc_framebuffer_handle()
553 + if !ok do return bk.NULL_FRAMEBUFFER, false
116 554
117 - destroy_framebuffer_opengl :: proc(handle: bk.Framebuffer_Handle) {}
555 + id: u32
556 + gl.CreateFramebuffers(1, &id)
557 + if id == 0 {
558 + log.error("gpu/opengl: glCreateFramebuffers failed")
559 + return bk.NULL_FRAMEBUFFER, false
560 + }
118 561
119 - create_shader_module_opengl :: proc(desc: bk.Shader_Module_Desc) -> (bk.Shader_Handle, bool) {
120 - if desc.format != .GLSL {
121 - log.error("gpu/opengl: shader module requires GLSL source")
122 - return bk.NULL_SHADER, false
562 + if desc.color_view != bk.NULL_TEXTURE {
563 + color := &g_gl.textures[desc.color_view]
564 + if !color.active {
565 + gl.DeleteFramebuffers(1, &id)
566 + return bk.NULL_FRAMEBUFFER, false
567 + }
568 + gl.NamedFramebufferTexture(id, gl.COLOR_ATTACHMENT0, color.id, 0)
569 + buf := u32(gl.COLOR_ATTACHMENT0)
570 + gl.NamedFramebufferDrawBuffers(id, 1, &buf)
123 571 }
124 - log.error("gpu/opengl: shader modules not implemented yet")
125 - return bk.NULL_SHADER, false
126 - }
572 + if desc.depth_view != bk.NULL_TEXTURE {
573 + depth := &g_gl.textures[desc.depth_view]
574 + if !depth.active {
575 + gl.DeleteFramebuffers(1, &id)
576 + return bk.NULL_FRAMEBUFFER, false
577 + }
578 + gl.NamedFramebufferTexture(id, gl.DEPTH_ATTACHMENT, depth.id, 0)
579 + }
127 580
128 - destroy_shader_opengl :: proc(handle: bk.Shader_Handle) {}
581 + status := gl.CheckNamedFramebufferStatus(id, gl.FRAMEBUFFER)
582 + if status != gl.FRAMEBUFFER_COMPLETE {
583 + log.errorf("gpu/opengl: framebuffer incomplete: 0x%X", status)
584 + gl.DeleteFramebuffers(1, &id)
585 + return bk.NULL_FRAMEBUFFER, false
586 + }
129 587
130 - draw_indexed_opengl :: proc(ctx: bk.Frame_Context, index_count, instance_count: u32, first_index: u32, vertex_offset: i32, first_instance: u32) {
131 - log.error("gpu/opengl: draw_indexed not implemented yet")
588 + entry := &g_gl.framebuffers[handle]
589 + entry.id = id
590 + entry.color_tex = desc.color_view
591 + entry.depth_tex = desc.depth_view
592 + entry.width = desc.width
593 + entry.height = desc.height
594 + entry.active = true
595 + return handle, true
132 596 }
133 597
134 - create_compute_pipeline_opengl :: proc(shader: bk.Shader_Handle, num_buffers: u32, push_constant_size: u32) -> (bk.Pipeline_Handle, bool) {
135 - log.error("gpu/opengl: compute pipelines not implemented yet")
136 - return bk.NULL_PIPELINE, false
598 + destroy_framebuffer_opengl :: proc(handle: bk.Framebuffer_Handle) {
599 + if handle == bk.NULL_FRAMEBUFFER do return
600 + entry := &g_gl.framebuffers[handle]
601 + if !entry.active do return
602 + if entry.id != 0 {
603 + id := entry.id
604 + gl.DeleteFramebuffers(1, &id)
605 + }
606 + entry^ = {}
137 607 }
138 608
139 - destroy_compute_pipeline_opengl :: proc(handle: bk.Pipeline_Handle) {}
140 -
141 - bind_compute_pipeline_opengl :: proc(ctx: bk.Frame_Context, handle: bk.Pipeline_Handle) {}
609 + draw_indexed_opengl :: proc(ctx: bk.Frame_Context, index_count, instance_count: u32, first_index: u32, vertex_offset: i32, first_instance: u32) {
610 + if g_gl.current_pipeline == bk.NULL_PIPELINE do return
611 + pipeline := &g_gl.pipelines[g_gl.current_pipeline]
612 + if !pipeline.active do return
613 + offset := rawptr(uintptr(first_index) * uintptr(size_of(u32)))
614 + gl.DrawElementsInstancedBaseVertexBaseInstance(pipeline.topology, i32(index_count), gl.UNSIGNED_INT, offset, i32(instance_count), vertex_offset, first_instance)
615 + }
142 616
143 617 dispatch_compute_opengl :: proc(ctx: bk.Frame_Context, groups_x, groups_y, groups_z: u32) {
144 - log.error("gpu/opengl: compute dispatch not implemented yet")
618 + gl.DispatchCompute(groups_x, groups_y, groups_z)
145 619 }
146 620
147 621 compute_barrier_opengl :: proc(ctx: bk.Frame_Context) {
148 622 gl.MemoryBarrier(gl.SHADER_STORAGE_BARRIER_BIT | gl.TEXTURE_FETCH_BARRIER_BIT | gl.FRAMEBUFFER_BARRIER_BIT)
149 623 }
624 +
625 + link_program :: proc(vs, fs, cs: u32, name: string) -> (u32, bool) {
626 + program := gl.CreateProgram()
627 + if program == 0 {
628 + log.error("gpu/opengl: glCreateProgram failed")
629 + return 0, false
630 + }
631 + if vs != 0 do gl.AttachShader(program, vs)
632 + if fs != 0 do gl.AttachShader(program, fs)
633 + if cs != 0 do gl.AttachShader(program, cs)
634 + gl.LinkProgram(program)
635 +
636 + status: i32
637 + gl.GetProgramiv(program, gl.LINK_STATUS, &status)
638 + if status == 0 {
639 + log_opengl_program_info(program, name)
640 + gl.DeleteProgram(program)
641 + return 0, false
642 + }
643 + return program, true
644 + }
645 +
646 + log_opengl_shader_info :: proc(shader: u32, name: string) {
647 + log_len: i32
648 + gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &log_len)
649 + if log_len <= 1 {
650 + log.errorf("gpu/opengl: shader compile failed: %s", name)
651 + return
652 + }
653 + buf := make([]u8, log_len)
654 + defer delete(buf)
655 + written: i32
656 + gl.GetShaderInfoLog(shader, log_len, &written, raw_data(buf))
657 + log.errorf("gpu/opengl: shader compile failed: %s: %s", name, string(buf[:max(0, int(written))]))
658 + }
659 +
660 + log_opengl_program_info :: proc(program: u32, name: string) {
661 + log_len: i32
662 + gl.GetProgramiv(program, gl.INFO_LOG_LENGTH, &log_len)
663 + if log_len <= 1 {
664 + log.errorf("gpu/opengl: program link failed: %s", name)
665 + return
666 + }
667 + buf := make([]u8, log_len)
668 + defer delete(buf)
669 + written: i32
670 + gl.GetProgramInfoLog(program, log_len, &written, raw_data(buf))
671 + log.errorf("gpu/opengl: program link failed: %s: %s", name, string(buf[:max(0, int(written))]))
672 + }
673 +
674 + apply_graphics_state :: proc(entry: ^GL_Pipeline_Entry) {
675 + if entry.enable_depth_test {
676 + gl.Enable(gl.DEPTH_TEST)
677 + } else {
678 + gl.Disable(gl.DEPTH_TEST)
679 + }
680 + if entry.enable_blending {
681 + gl.Enable(gl.BLEND)
682 + } else {
683 + gl.Disable(gl.BLEND)
684 + }
685 + if entry.cull_mode == .None {
686 + gl.Disable(gl.CULL_FACE)
687 + } else {
688 + gl.Enable(gl.CULL_FACE)
689 + gl.CullFace(to_gl_cull_face(entry.cull_mode))
690 + }
691 + gl.FrontFace(to_gl_front_face(entry.front_face))
692 + }