在Wayland上使用EGL创建多个窗口时,遇到片段着色器的问题

后端开发 2026-07-08

我在Odin中使用了自己对libwayland的绑定,能够一次打开好几个窗口。顶点着色器工作正常,但片段着色器有问题。似乎只有在我应用中创建的最后一个窗口里才起作用(见截图)。

这可能为什么会这样?

截图显示第一个窗口颜色缺失

下面是我启动应用的代码:

package main

import "base:runtime"
import "core:c/libc"
import "core:fmt"
import "core:log"
import lg "core:math/linalg"
import "core:mem"
import "core:strings"
import "core:time"
import wl "packages/wayland"
import gl "vendor:OpenGL"
import "vendor:egl"

main :: proc() {
    when ODIN_DEBUG {
        track: mem.Tracking_Allocator
        mem.tracking_allocator_init(&track, context.allocator)
        context.allocator = mem.tracking_allocator(&track)

        context.logger = log.create_console_logger()
        defer {
            log.destroy_console_logger(context.logger)
            if len(track.allocation_map) > 0 {
                for _, entry in track.allocation_map {
                    log.warnf("%v leaked %v bytes\n", entry.location, entry.size)
                }
            }
            mem.tracking_allocator_destroy(&track)
        }
    }

    window_err := wl.create_new_window(200, 200, "TEST")
    if !window_err {
        fmt.println("Failed to create new window")
        return
    }
    window_err = wl.create_new_window(230, 700, "TEST2")
    if !window_err {
        fmt.println("Failed to create new window")
        return
    }
    olc := wl.olc

    vertices : [dynamic]f32
    append(&vertices,-1)
    append(&vertices,0)
    append(&vertices,0)
    append(&vertices,1)
    append(&vertices,0)
    append(&vertices,0)
    append(&vertices,1)

    append(&vertices,1)
    append(&vertices,0)
    append(&vertices,0)
    append(&vertices,0)
    append(&vertices,1)
    append(&vertices,0)
    append(&vertices,1)

    append(&vertices,0)
    append(&vertices,1)
    append(&vertices,0)
    append(&vertices,1)
    append(&vertices,0)
    append(&vertices,1)
    append(&vertices,1)

    // useful utility procedures that are part of vendor:OpenGl
    shaderProgram, program_ok := gl.load_shaders_source(vertex_source, fragment_source)
    if !program_ok {
        fmt.eprintln("Failed to create GLSL program")
        return
    }
    defer gl.DeleteProgram(shaderProgram)

    vao: u32
    gl.GenVertexArrays(1, &vao)
    defer gl.DeleteVertexArrays(1, &vao)

    // initialization of OpenGL buffers
    vbo: u32
    gl.GenBuffers(1, &vbo)
    defer gl.DeleteBuffers(1, &vbo)

    for olc.running && wl.display_dispatch(olc.display) != -1 {
        for w, idx in olc.windows {

            ft := time.now()

            if egl.MakeCurrent(w.egl_display, w.egl_surface, w.egl_surface, w.egl_context) {

                gl.load_up_to(4, 6, egl.gl_set_proc_address)
                gl.Viewport(0, 0, w.width, w.height)

                gl.ClearColor(250.0 / 255, f32(idx) * 100 / 255, 158.0 / 255, 1)
                gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT)

                if idx == 0 {
                    vertices[0] = lg.cos(f32(ft._nsec * 1000))
                } else {
                    vertices[0] = -1
                }

                gl.BindVertexArray(vao)
                gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
                gl.BufferData(
                    gl.ARRAY_BUFFER,
                    len(vertices) * size_of(f32),
                    raw_data(vertices[:]),
                    gl.DYNAMIC_DRAW,
                )
                gl.VertexAttribPointer(0, 3, gl.FLOAT, false, 7 * size_of(f32), 0)
                gl.VertexAttribPointer(1, 4, gl.FLOAT, false, 7 * size_of(f32), 3 * size_of(f32))
                gl.EnableVertexAttribArray(0)
                gl.EnableVertexAttribArray(1)

                gl.Enable(gl.BLEND)
                gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)

                gl.UseProgram(shaderProgram)
                gl.BindVertexArray(vao)
                gl.DrawArrays(gl.TRIANGLES, 0, 3)

                egl.SwapBuffers(w.egl_display, w.egl_surface)
            }
        }
    }

    free(wl.olc)

    when ODIN_DEBUG {
        if len(track.bad_free_array) > 0 {
            for b in track.bad_free_array {
                log.errorf("Bad free at: %v", b.location)
            }

            libc.getchar()
            panic("Bad free detected")
        }
    }
}

vertex_source := `#version 460 core

layout(location=0) in vec3 a_position;
layout(location=1) in vec4 a_color;

out vec4 v_color;

void main() {
    gl_Position = vec4(a_position, 1.0);
    v_color = a_color;
}
`

fragment_source := `#version 460 core

in vec4 v_color;
out vec4 fragColor;

void main() {
    fragColor = v_color;
}
`

这是创建窗口的代码:

@(private = "file")
_init_egl :: proc(w: ^OL_Window) -> OL_Error {
    EGL_SAMPLES :: 0x3031
    EGL_SAMPLE_BUFFERS :: 0x3032

    w^.egl_config_attr = make([dynamic]i32, olc.allocator)
    append(&w.egl_config_attr, egl.SURFACE_TYPE)
    append(&w.egl_config_attr, egl.WINDOW_BIT)
    append(&w.egl_config_attr, egl.RED_SIZE)
    append(&w.egl_config_attr, 8)
    append(&w.egl_config_attr, egl.GREEN_SIZE)
    append(&w.egl_config_attr, 8)
    append(&w.egl_config_attr, egl.BLUE_SIZE)
    append(&w.egl_config_attr, 8)
    append(&w.egl_config_attr, egl.ALPHA_SIZE)
    append(&w.egl_config_attr, 8)
    append(&w.egl_config_attr, egl.DEPTH_SIZE)
    append(&w.egl_config_attr, 24)
    append(&w.egl_config_attr, egl.RENDERABLE_TYPE)
    append(&w.egl_config_attr, egl.OPENGL_BIT)
    append(&w.egl_config_attr, EGL_SAMPLE_BUFFERS)
    append(&w.egl_config_attr, 1)
    append(&w.egl_config_attr, EGL_SAMPLES)
    append(&w.egl_config_attr, 4)
    append(&w.egl_config_attr, egl.NONE)

    w^.egl_display = egl.GetDisplay(egl.NativeDisplayType(olc.display))
    if w.egl_display == egl.NO_DISPLAY {
        return OL_Create_EGL_Display_Error{}
    }

    if !egl.Initialize(w.egl_display, &w.egl_major, &w.egl_minor) {
        return OL_Initialize_EGL_Error{}
    }

    if !egl.ChooseConfig(
        w.egl_display,
        raw_data(w.egl_config_attr),
        &w.egl_config,
        1,
        &w.egl_num_config,
    ) {
        return OL_Create_EGL_Config_Error{}
    }

    egl.BindAPI(egl.OPENGL_API)

    w^.egl_context = egl.CreateContext(w.egl_display, w.egl_config, egl.NO_CONTEXT, nil)
    if w.egl_context == egl.NO_CONTEXT {
        return OL_Create_EGL_Context_Error{}
    }

    w^.egl_window = egl_window_create(w.wl_surface, int(w.width), int(w.height))
    w^.egl_surface = egl.CreateWindowSurface(
        w.egl_display,
        w.egl_config,
        egl.NativeWindowType(w.egl_window),
        nil,
    )
    if w.egl_surface == egl.NO_SURFACE {
        return OL_Create_EGL_Surface_Error{}
    }

    if !egl.MakeCurrent(w.egl_display, w.egl_surface, w.egl_surface, w.egl_context) {
        return OL_MakeCurrent_EGL_Error{}
    } else {
        egl.SwapInterval(w.egl_display, 1)
        gl.load_up_to(4, 6, egl.gl_set_proc_address)
    }

    return nil
}

感谢Vipul Bondugula,我设法修改了代码,应用现在可以运行。现在我对每个窗口都创建着色器、缓冲区和VAO:

    progs: [10]u32
    vaos: [10]u32
    vbos: [10]u32

    for w, idx in olc.windows {

        if egl.MakeCurrent(w.egl_display, w.egl_surface, w.egl_surface, w.egl_context) {

            program_ok: bool
            progs[idx], program_ok = gl.load_shaders_source(vertex_source, fragment_source)
            if !program_ok {
                fmt.eprintln("Failed to create GLSL program")
                return
            }
            progs[idx], program_ok = gl.load_shaders_source(vertex_source, fragment_source)
            if !program_ok {
                fmt.eprintln("Failed to create GLSL program")
                return
            }
            gl.GenVertexArrays(1, &vaos[idx])
            gl.GenBuffers(1, &vbos[idx])
        }
    }
    defer {
        for w, idx in olc.windows {
            if egl.MakeCurrent(w.egl_display, w.egl_surface, w.egl_surface, w.egl_context) {
                defer gl.DeleteVertexArrays(1, &vaos[idx])
                defer gl.DeleteBuffers(1, &vbos[idx])
                defer gl.DeleteProgram(progs[idx])
            }
        }
    }

解决方案

问题在于你为每个窗口创建了一个EGL/OpenGL上下文,但着色器程序、VAO与 VBO只创建了一次。

_init_egl() 中你调用了: egl.CreateContext(.., egl.NO_CONTEXT, nil) 因此上下文并不共享。着色器程序是在窗口创建之后才创建的,而最后一个窗口的上下文始终是当前的。这意味着该程序、VAO和 VBO只属于那个上下文。当你后续对另一个窗口/上下文调用MakeCurrent() 并使用相同的对象ID时,这些ID在该上下文中就不是有效的GL对象。

你有两种常见的选项:

  1. 使用一个OpenGL上下文,在渲染每个窗口之前让它成为当前,并切换到不同的EGL表面。

  2. 为每个窗口保留一个上下文,但要么:

  3. 为每个上下文单独创建着色器/缓冲区/VAO,或

  4. 使用eglCreateContext的 share_context参数将上下文设为共享上下文。

即使使用共享上下文,VAO也不会在上下文之间共享,因此仍需要为每个上下文/窗口维护一个VAO。着色器程序和缓冲区可以共享,但VAO的状态应当在每个上下文中分别初始化。

因此,修复的方法很可能是将着色器初始化移到按窗口/按上下文的设置中,或创建一个共享上下文的设置并为每个窗口保留VAO。

调试建议:在glUseProgram()、glBindVertexArray()、glBindBuffer()、glVertexAttribPointer() 和glDrawArrays() 之后也调用glGetError()。在绘制其上下文未创建这些对象的窗口时,你可能会看到错误。

OpenGL对象名默认是与上下文相关的,除非显式共享上下文;并且根据OpenGL的 VAO规范,VAO也不能在上下文之间共享。 https://developer.download.nvidia.com/opengl/specs/GL_ARB_vertex_array_object.txt

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章