Created
June 8, 2024 01:54
-
-
Save CaptainJH/2d3d08036b346853ba03a97a9dfe28fd to your computer and use it in GitHub Desktop.
Create multiple window with SDL2 and sokol
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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