Sokol Когда минимализм творит чудеса в кросс-платформенной разработке
Знакомая ситуация? Вы хотите написать что-то на C или C++, что будет работать и на Windows, и на macOS, и на Linux, а в идеале — еще и в браузере через WebAssembly. И чтобы это что-то включало графику, звук, ввод с клавиатуры и мыши. Обычно это означает погружение в дебри платформозависимых API, тонны бойлерплейта и головную боль с зависимостями. Но что, если я скажу, что есть способ сделать это элегантно, легковесно и, главное, с удовольствием?
Представляю вам Sokol — проект, который словно глоток свежего воздуха для тех, кто ценит простоту и контроль. Это не очередной гигантский фреймворк, а скорее набор "умных" однофайловых C-хедеров, вдохновленных подходом stb (помните stb_image.h?). Sokol предоставляет минималистичные, но мощные обертки для базовых функций: от рендеринга 3D-графики до воспроизведения звука и обработки пользовательского ввода.
Зачем разработчику Sokol?
Если вы инди-разработчик игр, создатель инструментов, или просто любите низкоуровневое программирование без лишнего "жира", Sokol — это ваш выбор. Он позволяет сосредоточиться на логике приложения, абстрагируясь от рутины инициализации графических контекстов или обработки событий ОС. При этом вы сохраняете полный контроль над происходящим, что бесценно для оптимизации и понимания работы системы.

Ключевые возможности, которые упростят вашу жизнь
Sokol состоит из нескольких независимых модулей, каждый из которых решает свою конкретную задачу. Давайте пройдемся по самым интересным:
1. Графика и приложение: sokol_gfx.h и sokol_app.h
Это, пожалуй, сердце Sokol. sokol_gfx.h — это тонкая обертка над современными 3D-API: OpenGL ES3/WebGL2, OpenGL 3.3, Direct3D 11, Metal и даже экспериментальный Vulkan (в разработке!). Он предоставляет унифицированный интерфейс для работы с буферами, текстурами, шейдерами и рендер-пассами.
Но что толку от графики без окна и обработки ввода? Здесь на помощь приходит sokol_app.h. Он берет на себя всю рутину по созданию окна, инициализации 3D-контекста и обработке событий клавиатуры, мыши и тачскрина. И самое главное — он кросс-платформенный! Ваши приложения будут работать на Windows, macOS, Linux (X11), iOS, Android и, конечно же, в браузере через WebAssembly.
Посмотрите, как просто выглядит "Hello Triangle" с Sokol:
#include "sokol_app.h"
#include "sokol_gfx.h"
#include "sokol_log.h"
#include "sokol_glue.h"
// ... и ваш сгенерированный шейдерный код
static struct {
sg_pipeline pip;
sg_bindings bind;
sg_pass_action pass_action;
} state;
static void init(void) {
sg_setup(&(sg_desc){
.environment = sglue_environment(),
.logger.func = slog_func,
});
float vertices[] = {
0.0f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f
};
state.bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){
.data = SG_RANGE(vertices),
});
state.pip = sg_make_pipeline(&(sg_pipeline_desc){
.shader = sg_make_shader(triangle_shader_desc(sg_query_backend())),
.layout = {
.attrs = {
[ATTR_triangle_position].format = SG_VERTEXFORMAT_FLOAT3,
[ATTR_triangle_color0].format = SG_VERTEXFORMAT_FLOAT4
}
},
});
state.pass_action = (sg_pass_action) {
.colors[0] = { .load_action=SG_LOADACTION_CLEAR, .clear_value={0.0f, 0.0f, 0.0f, 1.0f } }
};
}
void frame(void) {
sg_begin_pass(&(sg_pass){ .action = state.pass_action, .swapchain = sglue_swapchain() });
sg_apply_pipeline(state.pip);
sg_apply_bindings(&state.bind);
sg_draw(0, 3, 1);
sg_end_pass();
sg_commit();
}
void cleanup(void) {
sg_shutdown();
}
sapp_desc sokol_main(int argc, char* argv[]) {
(void)argc; (void)argv;
return (sapp_desc){
.init_cb = init,
.frame_cb = frame,
.cleanup_cb = cleanup,
.width = 640,
.height = 480,
.window_title = "Triangle",
.icon.sokol_default = true,
.logger.func = slog_func,
};
}
2. Звук без боли: sokol_audio.h
Работа со звуком часто становится камнем преткновения. sokol_audio.h предлагает минималистичный, но функциональный API для потокового воспроизведения аудио. Вы можете либо заполнять буферы в колбэке, работающем в аудиопотоке, либо "пушить" небольшие порции данных из основного цикла. Поддерживаются WASAPI, CoreAudio, ALSA и WebAudio — полный набор для кросс-платформенного звука.
// Пример генерации квадратной волны с помощью колбэка
static void stream_cb(float* buffer, int num_frames, int num_channels) {
assert(1 == num_channels);
static uint32_t count = 0;
for (int i = 0; i < num_frames; i++) {
buffer[i] = (count++ & (1<<3)) ? 0.5f : -0.5f;
}
}
int main() {
saudio_setup(&(saudio_desc){
.stream_cb = stream_cb,
.logger.func = slog_func,
});
// ... ваш основной цикл ...
saudio_shutdown();
return 0;
}
3. Асинхронная загрузка: sokol_fetch.h
В современных приложениях, особенно веб-ориентированных, асинхронная загрузка данных — это must-have. sokol_fetch.h позволяет легко загружать файлы или потоково передавать данные по HTTP (для WebAssembly) или из локальной файловой системы. Это избавляет вас от необходимости писать платформозависимый код для работы с сетью или файлами.
// Пример асинхронной загрузки файла
#include "sokol_fetch.h"
#include "sokol_log.h"
static uint8_t buffer[1024*1024]; // Максимальный размер файла
static void response_callback(const sfetch_response_t* response) {
if (response->fetched) {
// Данные загружены, работаем с ними
const void* data = response->buffer_ptr;
uint64_t data_size = response->fetched_size;
// ...
} else if (response->failed) {
// Ошибка загрузки
switch (response->error_code) {
case SFETCH_ERROR_FILE_NOT_FOUND: /* ... */ break;
// ...
}
}
}
static void init(void) {
sfetch_setup(&(sfetch_desc_t){ .logger.func = slog_func });
sfetch_send(&(sfetch_request_t){
.path = "hello_world.txt",
.callback = response_callback,
.buffer_ptr = buffer,
.buffer_size = sizeof(buffer)
});
}
static void frame(void) {
sfetch_dowork(); // Не забываем вызывать в каждом кадре
}
static void shutdown(void) {
sfetch_shutdown();
}
4. Удобный парсер аргументов: sokol_args.h
Мелочь, а приятно! sokol_args.h предоставляет унифицированный способ парсинга аргументов командной строки для нативных приложений и параметров URL-запроса для веб-приложений. Это позволяет использовать один и тот же код для настройки вашего приложения в разных окружениях.
#include "sokol_args.h"
int main(int argc, char* argv[]) {
sargs_setup(&(sargs_desc){ .argc=argc, .argv=argv });
if (sargs_exists("type")) {
if (sargs_equals("type", "kc85_4")) {
// Запускаем как KC85/4
}
// ...
}
sargs_shutdown();
return 0;
}
Почему именно C? И при чем тут другие языки?
Автор Sokol сознательно выбрал C для реализации основных хедеров. И вот почему:
- Легкая интеграция: C-код гораздо проще интегрировать в проекты на других языках.
- Минимальный оверхед: Исполняемые файлы получаются компактными, без лишних зависимостей.
Кстати, благодаря этому подходу, у Sokol есть "официальные" привязки к таким языкам, как Zig, Odin, Nim, Rust, D, Jai и C3. Это значит, что вы можете наслаждаться всеми преимуществами Sokol, работая в своей любимой среде!
Sokol в деле: от Doom до игр на Steam
Самое убедительное доказательство ценности проекта — это его реальные применения. И тут Sokol есть чем похвастаться!
- Порт Doom Shareware: Да, легендарный Doom был портирован на Sokol! Это отличный пример того, как можно оживить старые проекты на современных платформах.
- Игры на Steam: Несколько коммерческих игр, таких как Solar Storm и Spanking Runners (Samogonki), были разработаны с использованием Sokol. Это говорит о его надежности и производительности.
- Эмуляторы: Проект активно используется для создания эмуляторов 8-битных компьютеров, что демонстрирует его пригодность для низкоуровневой графики и систем.
- Интеграция с UI: Есть готовые утилиты для интеграции с популярными Immediate Mode UI библиотеками, такими как Dear ImGui и Nuklear. Это позволяет быстро добавлять отладочные интерфейсы или даже полноценные редакторы.
- WebAssembly — гражданин первого класса: Как уже упоминалось, WebAssembly не просто "поддерживается", а является одним из ключевых приоритетов проекта. Это открывает огромные возможности для создания интерактивных веб-приложений и игр, работающих прямо в браузере.
Выводы: Стоит ли попробовать Sokol?
Однозначно да, если вы:
- Разрабатываете игры или графические приложения на C/C++ и ищете легковесное, кросс-платформенное решение.
- Устали от тяжелых фреймворков и хотите больше контроля над кодом.
- Планируете создавать приложения для WebAssembly и нуждаетесь в минималистичном API.
- Экспериментируете с новыми языками программирования, такими как Zig или Rust, и цените легкую интеграцию с C-библиотеками.
Sokol — это не просто набор хедеров, это философия разработки: простота, эффективность и кросс-платформенность без компромиссов. Он демонстрирует, что даже в эпоху сложных движков и фреймворков, низкоуровневый C-код может быть элегантным, удобным и невероятно продуктивным. Загляните в репозиторий, изучите примеры — возможно, Sokol станет вашим новым любимым инструментом!
URL репозитория: https://github.com/floooh/sokol