#define SOKOL_IMPL #define SOKOL_GLCORE #include #include #include #include #include #include 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 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; }