Skip to content

RFC: 关于支持 macOS 和 Linux 的讨论 #6

@yuchanns

Description

@yuchanns

这不是一个 feat request,仅仅只是一个讨论。但是放在 discussion 感觉容易被忽略,因此放到这里。

我在自己尝试为 soluna 添加 mac 平台和 linux 的支持。代码在: https://github.com/cloudwu/soluna/compare/master...yuchanns:soluna:feat/multiplatform-support.diff

当然代码实现很粗造,主要是想先跑起来。

现在已经在 mac 上运行起来,并且可以运行 deepfuture:

Image

目前遇到两个问题,想咨询一下云风的想法。我知道现在这块不是你关注的点,可能并不在意这些问题,不过我还是想提出来问问。因为我真的很想让它在另外两个平台也运行起来。

1. mac 平台,不支持在非主线程改变窗体属性

例如设置标题。会直接 panic。

我已经阅读过你的关于 SetWindowText 的文章,理解了添加 window service 的理由。另外我阅读 commit 发现你曾经尝试通过更改 CTX 的属性来通知主线程更改标题,当然这可能会有竞争问题。这也是为什么 macos 禁止非主线程更改窗体的原因。

这点我觉得 windows 的设计挺好的,不管什么线程都只是发送一个 message 排队修改。

我在想,是否可以通过类似的方式,将更改标题通过 message 发送给 soluna 主线程,例如:

function frame(count)
   sendmessage("frame", count)
   local cmd, data = barrier:wait() -- 返回 message 通知一些必须在主线程进行操作
   commands[cmd](data) -- 更改窗体标题等
end

看起来 event 只是一个 condvar 应该不支持这么改造,just an idea. :)

2. linux 平台,渲染只能在主线程

这个问题属于比较严重的问题了,直接动摇 soluna 现在的设计。

上面的分支,在 Linux 平台也可以构建,以及运行。但是起来之后黑屏,游戏本身还是可以通过鼠标点击交互触发逻辑运行,观看日志输出也是正常的,只不过看不见图形效果。

一开始我进行了断点调试,尝试发现代码错误。后来发现原因是非主线程渲染就会黑屏。(当然,可能是我理解错误。我只有写 web 后端业务的经验,对这方面知识积累为零。但是从下面的 demo 来看符合我的结论。)

注:demo 我是直接让 sonnet-4 生成的。

最小可渲染版本

#define SOKOL_IMPL
#define SOKOL_GLCORE

#include <stdint.h>
#include <stdio.h>
#include "sokol_app.h"
#include "sokol_gfx.h"
#include "sokol_glue.h"

#include "colorquad.glsl.h"

typedef struct {
    float pos[4];    // position (x, y, z, w)
    uint32_t idx;    // idx
    float color[4];  // c (color)
} vertex_t;

static struct {
    sg_pass_action pass_action;
    sg_pipeline pip;
    sg_bindings bind;
    sg_buffer vertex_buffer;
    sg_buffer storage_buffer;
    vs_params_t vs_params;
} state;

static void init(void) {
    printf("Starting init...\n");
    
    sg_setup(&(sg_desc){
        .environment = sglue_environment(),
    });
    
    printf("Sokol setup complete\n");



    state.pass_action = (sg_pass_action) {
        .colors[0] = { .load_action=SG_LOADACTION_CLEAR, .clear_value={0.2f, 0.3f, 0.4f, 1.0f} }
    };
    
    printf("Init complete\n");
}

static void frame(void) {

    sg_begin_pass(&(sg_pass) {
        .action = state.pass_action,
        .swapchain = sglue_swapchain(),
    });
    
    sg_end_pass();
    sg_commit();
}

static void cleanup(void) {
    sg_shutdown();
}

sapp_desc sokol_main(int argc, char* argv[]) {
    return (sapp_desc){
        .init_cb = init,
        .frame_cb = frame,
        .cleanup_cb = cleanup,
        .width = 800,
        .height = 600,
        .window_title = "ColorQuad Example",
    };
}

添加多线程之后就不 work 了,即使没有竞态,只是在一个线程中做这件事,也会黑屏。

#define SOKOL_IMPL
#define SOKOL_GLCORE

#include <stdint.h>
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include "sokol_app.h"
#include "sokol_gfx.h"
#include "sokol_glue.h"


#include "colorquad.glsl.h"

typedef struct {
    float pos[4];    // position (x, y, z, w)
    uint32_t idx;    // idx
    float color[4];  // c (color)
} vertex_t;


typedef struct {
    pthread_t render_thread;
    sem_t frame_start;
    sem_t frame_done;
    volatile int should_exit;
    volatile int render_ready;
} thread_sync_t;

static struct {
    sg_pass_action pass_action;
    sg_pipeline pip;
    sg_bindings bind;
    sg_buffer vertex_buffer;
    sg_buffer storage_buffer;
    // vs_params_t vs_params;
    thread_sync_t sync;
} state;


static void* render_thread_func(void* arg) {
    printf("Render thread started (thread ID: %lu)\n", pthread_self());
    
    while (!state.sync.should_exit) {
   
        sem_wait(&state.sync.frame_start);
        
        if (state.sync.should_exit) break;
        
        printf("Render thread: executing render commands\n");
        
       
        sg_begin_pass(&(sg_pass) {
            .action = state.pass_action,
            .swapchain = sglue_swapchain(),
        });
        
        sg_end_pass();
        sg_commit();
        
        printf("Render thread: render commands completed\n");
        
        
        sem_post(&state.sync.frame_done);
    }
    
    printf("Render thread exiting\n");
    return NULL;
}

static void init(void) {
    printf("Starting init... (main thread ID: %lu)\n", pthread_self());
    
    sg_setup(&(sg_desc){
        .environment = sglue_environment(),
    });
    
    printf("Sokol setup complete\n");

    
    state.pass_action = (sg_pass_action) {
        .colors[0] = { .load_action=SG_LOADACTION_CLEAR, .clear_value={0.2f, 0.3f, 0.4f, 1.0f} }
    };
    
   
    state.sync.should_exit = 0;
    state.sync.render_ready = 0;
    
    if (sem_init(&state.sync.frame_start, 0, 0) != 0) {
        printf("Failed to initialize frame_start semaphore\n");
        return;
    }
    
    if (sem_init(&state.sync.frame_done, 0, 0) != 0) {
        printf("Failed to initialize frame_done semaphore\n");
        return;
    }
    
    
    if (pthread_create(&state.sync.render_thread, NULL, render_thread_func, NULL) != 0) {
        printf("Failed to create render thread\n");
        return;
    }
    
    state.sync.render_ready = 1;
    printf("Init complete, render thread created\n");
}

static void frame(void) {
    if (!state.sync.render_ready) return;
    
    printf("Main thread: triggering render\n");
    
    
    sem_post(&state.sync.frame_start);
    
    
    printf("Main thread: waiting for render completion\n");
    sem_wait(&state.sync.frame_done);
    printf("Main thread: render completed\n");
}

static void cleanup(void) {
    printf("Starting cleanup\n");
    
    if (state.sync.render_ready) {
       
        state.sync.should_exit = 1;
        sem_post(&state.sync.frame_start);
        
        
        pthread_join(state.sync.render_thread, NULL);
        
        
        sem_destroy(&state.sync.frame_start);
        sem_destroy(&state.sync.frame_done);
    }
    
    sg_shutdown();
    printf("Cleanup complete\n");
}

sapp_desc sokol_main(int argc, char* argv[]) {
    return (sapp_desc){
        .init_cb = init,
        .frame_cb = frame,
        .cleanup_cb = cleanup,
        .width = 800,
        .height = 600,
        .window_title = "ColorQuad Example - Threading Test",
    };
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions