Skip to content

Instantly share code, notes, and snippets.

@CaptainJH
Created June 8, 2024 01:54
Show Gist options
  • Save CaptainJH/2d3d08036b346853ba03a97a9dfe28fd to your computer and use it in GitHub Desktop.
Save CaptainJH/2d3d08036b346853ba03a97a9dfe28fd to your computer and use it in GitHub Desktop.
Create multiple window with SDL2 and sokol
#define SOKOL_IMPL
#define SOKOL_GLCORE
#include <SDL.h>
#include <sokol_gfx.h>
#include <sokol_log.h>
#include <iostream>
#include <sstream>
#include <array>
constexpr auto WndWidth = 640;
constexpr auto WndHeight = 480;
constexpr int g_WindowCount = 2;
struct WindowInfo {
bool stop_rendering = false;
bool shown = true;
Uint32 winId;
SDL_Window* win = nullptr;
sg_color clearColor = { 0.5f, 0.5f, 0.5f, 1.0f };
SDL_GLContext ctx;
sg_attachments main_attach;
sg_attachments win_attach;
};
void HandleWindowEvent(WindowInfo& wnd, SDL_Event e) {
if (e.type == SDL_WINDOWEVENT) {
if (e.window.event == SDL_WINDOWEVENT_MINIMIZED)
wnd.stop_rendering = true;
else if (e.window.event == SDL_WINDOWEVENT_RESTORED)
wnd.stop_rendering = false;
else if (e.window.event == SDL_WINDOWEVENT_CLOSE) {
SDL_HideWindow(wnd.win);
wnd.shown = false;
}
}
}
void CloseWindow(WindowInfo& win) {
SDL_DestroyWindow(win.win); win.win = nullptr;
}
int main(int argc, char* argv[]) {
if (SDL_Init(SDL_INIT_EVERYTHING) < 0) {
std::cout << "error initializing SDL:" << SDL_GetError() << std::endl;
return 1;
}
std::array<WindowInfo, g_WindowCount> windows;
int preWndPosX = 0;
int preWndPosY = 0;
for (auto i = 0; i < g_WindowCount; ++i) {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
if (i > 0) {
SDL_GL_MakeCurrent(windows.front().win, windows.front().ctx);
SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
}
SDL_GL_LoadLibrary(nullptr);
std::stringstream ss;
ss << "Sokol with SDL " << i;
WindowInfo win = {};
win.win = SDL_CreateWindow(
ss.str().c_str(),
i == 0 ? SDL_WINDOWPOS_CENTERED : preWndPosX + WndWidth,
i == 0 ? SDL_WINDOWPOS_CENTERED : preWndPosY,
WndWidth,
WndHeight,
SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN
);
/* Create our opengl context and attach it to our window */
win.ctx = SDL_GL_CreateContext(win.win);
/* This makes our buffer swap syncronized with the monitor's vertical refresh */
SDL_GL_SetSwapInterval(-1);
win.winId = SDL_GetWindowID(win.win);
if (i == 0) {
// setup sokol_gfx
sg_setup({
.logger = {
.func = slog_func
},
.environment = {
.defaults = {
.color_format = SG_PIXELFORMAT_RGBA8,
.depth_format = SG_PIXELFORMAT_DEPTH_STENCIL,
.sample_count = 1,
} },
});
assert(sg_isvalid());
}
else {
SDL_GL_MakeCurrent(windows.front().win, windows.front().ctx);
win.clearColor = { 1.0f, 0.5f, 0.5f, 1.0f };
}
sg_image color_img = sg_make_image({
.render_target = true,
.width = WndWidth,
.height = WndHeight,
.pixel_format = SG_PIXELFORMAT_RGBA8,
.sample_count = 1,
.label = "color_img"
});
// create a framebuffer on the main gl context, this is where sokol-rendering goes into
win.main_attach = sg_make_attachments({
.colors = {
{.image = color_img}
},
.depth_stencil = {
.image = sg_make_image({
.render_target = true,
.width = WndWidth,
.height = WndHeight,
.pixel_format = SG_PIXELFORMAT_DEPTH_STENCIL,
.label = "depth_img"
}),
},
.label = "main_attachment"
});
assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
// create a framebuffer on the window gl context which shares its renderbuffers with
// the main-context framebuffer, this framebuffer will be the source for a blit-framebuffer operation
SDL_GL_MakeCurrent(win.win, win.ctx);
win.win_attach = sg_make_attachments({
.colors = {
{.image = color_img}
},
.label = "win_attach"
});
assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
windows[i] = win;
SDL_GetWindowPosition(win.win, &preWndPosX, &preWndPosY);
}
SDL_GL_MakeCurrent(windows.front().win, windows.front().ctx);
// create a vertex buffer
float vertices[] = {
// positions colors
-0.5f, 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,
-0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 0.0f, 1.0f,
};
sg_buffer_desc vbuf_desc = {
.data = SG_RANGE(vertices)
};
sg_buffer vbuf = sg_make_buffer(&vbuf_desc);
// create an index buffer
uint16_t indices[] = {
0, 1, 2, // first triangle
0, 2, 3, // second triangle
};
sg_buffer_desc ibuf_desc = {
.type = SG_BUFFERTYPE_INDEXBUFFER,
.data = SG_RANGE(indices)
};
sg_buffer ibuf = sg_make_buffer(&ibuf_desc);
// define the resource bindings
sg_bindings bind = {
.vertex_buffers = { vbuf },
.index_buffer = ibuf
};
// create a shader (use vertex attribute locations)
sg_shader shd = sg_make_shader({
.vs = {
.source =
"#version 330\n"
"layout(location=0) in vec4 position;\n"
"layout(location=1) in vec4 color0;\n"
"out vec4 color;\n"
"void main() {\n"
" gl_Position = position;\n"
" color = color0;\n"
"}\n"
},
.fs = {
.source =
"#version 330\n"
"in vec4 color;\n"
"out vec4 frag_color;\n"
"void main() {\n"
" frag_color = color;\n"
"}\n"
},
});
// create a pipeline object (default render state is fine)
sg_pipeline pip = sg_make_pipeline({
.shader = shd,
.layout = {
.attrs = {
// vertex attrs can also be bound by location instead of name
{.offset = 0, .format = SG_VERTEXFORMAT_FLOAT3 },
{.offset = 12, .format = SG_VERTEXFORMAT_FLOAT4 }
}
},
.index_type = SG_INDEXTYPE_UINT16,
});
// draw loop
SDL_Event e;
bool bQuit = false;
while (!bQuit) {
while (SDL_PollEvent(&e) != 0) {
if (e.type == SDL_QUIT)
bQuit = true;
for (auto& w : windows) {
if (e.window.windowID == w.winId)
HandleWindowEvent(w, e);
}
}
const auto& main_win = windows.front();
bQuit = !main_win.shown;
SDL_GL_MakeCurrent(main_win.win, main_win.ctx);
sg_reset_state_cache();
for (const auto& w : windows) {
if (w.stop_rendering)
continue;
sg_begin_pass({
.action = {
.colors = {
{
.load_action = SG_LOADACTION_CLEAR,
.clear_value = w.clearColor
}
}
},
.attachments = w.main_attach,
});
sg_apply_pipeline(pip);
sg_apply_bindings(bind);
sg_draw(0, 6, 1);
sg_end_pass();
}
sg_commit();
for (const auto& w : windows) {
if (w.stop_rendering)
continue;
SDL_GL_MakeCurrent(w.win, w.ctx);
glDisable(GL_FRAMEBUFFER_SRGB);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
const auto win_gl_info = sg_gl_query_attachments_info(w.win_attach);
glBindFramebuffer(GL_READ_FRAMEBUFFER, win_gl_info.framebuffer);
glBlitFramebuffer(0, 0, WndWidth, WndHeight, 0, 0, WndWidth, WndHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
SDL_GL_SwapWindow(w.win);
}
}
/* cleanup */
sg_shutdown();
for (auto& w : windows)
CloseWindow(w);
SDL_Quit();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment