#include "gl_xcb.h" #include #include #include namespace saw { namespace gfx { namespace { glx_library_extensions load_glx_library_extensions(const char* extension_string){ std::string_view extensions_view{extension_string}; std::set extensions; for(;;){ size_t n = extensions_view.find_first_of(' '); if( n != extensions_view.npos && n < extensions_view.size()){ std::string_view sub_glx_ext = extensions_view.substr(0,n); extensions.insert(sub_glx_ext); extensions_view.remove_prefix(n+1); } else { break; } } auto find = extensions.find("GLX_ARB_create_context"); GLXContext (*glXCreateContextAttribsARB)(Display*, GLXFBConfig, GLXContext, Bool, const int*) = nullptr; if(find != extensions.end()){ glXCreateContextAttribsARB = reinterpret_cast( glXGetProcAddress(reinterpret_cast("glXCreateContextAttribsARB"))); } return {extensions_view, glXCreateContextAttribsARB}; } int translate_render_type_settings(gl_settings::render_type cmp){ switch(cmp){ case gl_settings::render_type::rgba: return GLX_RGBA_BIT; } return 0; } int translate_drawable_type_settings(gl_settings::drawable_type cmp){ int i = 0; if (static_cast(cmp) & static_cast(gl_settings::drawable_type::window_bit)) { i |= static_cast(GLX_WINDOW_BIT); } if (static_cast(cmp) & static_cast(gl_settings::drawable_type::pixmap_bit)) { i |= static_cast(GLX_PIXMAP_BIT); } if (static_cast(cmp) & static_cast(gl_settings::drawable_type::pbuffer_bit)) { i |= static_cast(GLX_PBUFFER_BIT); } return i; } } gpu_context::gpu_context(const glx_library_extensions& ext_lib, own> dev, int visual_id, GLXContext context, GLXFBConfig fb_config): ext_lib_{ext_lib}, device_{std::move(dev)}, visual_id_{visual_id}, context_{context}, fb_config_{fb_config}{} gpu_context::~gpu_context(){ assert(device_); assert(device_->get_xcb_display()); if(context_){ /// @todo Check if this context is bound and only unbind if that is the case // ::glXMakeContextCurrent(device_->get_xcb_display(), None, None, nullptr); ::glXDestroyContext(device_->get_xcb_display(), context_); } device_->flush(); } own> gpu_context::create_window(const video_mode& vid_mode, std::string_view title_view){ SAW_ASSERT(device_){ return nullptr; } own> win = device_->create_xcb_window(vid_mode, title_view, visual_id_); if(!win){ return nullptr; } ::GLXWindow glx_win = glXCreateWindow(device_->get_xcb_display(), fb_config_, win->get_xcb_window_handle(), nullptr); return heap>(std::move(win), *this, glx_win); } void gpu_context::flush(){ assert(device_); if(device_){ device_->flush(); } } own > create_gl_context(io_provider& prov, const gl_settings& settings){ auto eodev = create_xcb_device(prov); if(eodev.is_error()){ return nullptr; } own>& device = eodev.get_value(); if (!device) { return nullptr; } /* * Translate all attributes */ std::vector attributes; attributes.reserve(33); attributes.push_back(GLX_X_RENDERABLE); attributes.push_back(settings.renderable ? True : False); attributes.push_back(GLX_RENDER_TYPE); attributes.push_back(translate_render_type_settings(settings.render_t)); attributes.push_back(GLX_RED_SIZE); attributes.push_back(settings.red_bits); attributes.push_back(GLX_GREEN_SIZE); attributes.push_back(settings.green_bits); attributes.push_back(GLX_BLUE_SIZE); attributes.push_back(settings.blue_bits); attributes.push_back(GLX_ALPHA_SIZE); attributes.push_back(settings.alpha_bits); attributes.push_back(GLX_DEPTH_SIZE); attributes.push_back(settings.depth_bits); attributes.push_back(GLX_STENCIL_SIZE); attributes.push_back(settings.stencil_bits); attributes.push_back(GLX_DOUBLEBUFFER); attributes.push_back(settings.double_buffer ? True : False); attributes.push_back(GLX_DRAWABLE_TYPE); attributes.push_back( translate_drawable_type_settings(settings.drawable_t)); attributes.push_back(GLX_X_VISUAL_TYPE); attributes.push_back(GLX_TRUE_COLOR); attributes.push_back(None); int num_fb_configs = 0; glx_library_extensions lib_ext = load_glx_library_extensions( glXQueryExtensionsString(device->get_xcb_display(), device->get_xcb_screen())); GLXFBConfig *fb_configs = glXChooseFBConfig( device->get_xcb_display(), device->get_xcb_screen(), &attributes[0], &num_fb_configs); if (!fb_configs || num_fb_configs == 0) { /// @todo log errors return nullptr; } if (lib_ext.glXCreateContextAttribsARB) { ::GLXFBConfig fb_config = fb_configs[0]; ::GLXContext context; std::vector glx_attribs; glx_attribs.reserve(11); glx_attribs.push_back(GLX_CONTEXT_MAJOR_VERSION_ARB); glx_attribs.push_back(settings.gl_major); glx_attribs.push_back(GLX_CONTEXT_MINOR_VERSION_ARB); glx_attribs.push_back(settings.gl_minor); glx_attribs.push_back(GLX_CONTEXT_PROFILE_MASK_ARB); glx_attribs.push_back(settings.core_profile ? GLX_CONTEXT_CORE_PROFILE_BIT_ARB : GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB); glx_attribs.push_back(None); context = lib_ext.glXCreateContextAttribsARB( device->get_xcb_display(), fb_config, NULL, True, &glx_attribs[0]); ::XFree(fb_configs); if (!context) { return nullptr; } int visual_id = 0; glXGetFBConfigAttrib(device->get_xcb_display(), fb_config, GLX_VISUAL_ID, &visual_id); return heap>(lib_ext, std::move(device), visual_id, context, fb_config); } return nullptr; } gpu_window::gpu_window(own> win, gpu_context &ctx, ::GLXWindow glx_win) : window_{std::move(win)}, context_{&ctx}, glx_window_handle_{glx_win} {} gpu_window::~gpu_window() { assert(context_->device_); if (context_->device_) { ::glXDestroyWindow(context_->device_->get_xcb_display(), glx_window_handle_); } } void gpu_window::bind() { assert(window_ && context_->device_ && context_->device_->get_xcb_display()); if (window_) { if (context_->device_ && context_->device_->get_xcb_display()) { ::glXMakeContextCurrent(context_->device_->get_xcb_display(), glx_window_handle_, glx_window_handle_, context_->context_); } } } void gpu_window::show() { assert(window_); if (window_) { window_->show(); } } void gpu_window::hide() { assert(window_); if (window_) { window_->hide(); } } void gpu_window::swap() { assert(context_->device_); assert(context_->device_->get_xcb_display()); if (context_->device_ && context_->device_->get_xcb_display()) { ::glXSwapBuffers(context_->device_->get_xcb_display(), glx_window_handle_); } } const video_mode &gpu_window::get_video_mode() const { assert(window_); return window_->get_video_mode(); } const std::string_view gpu_window::get_title() const { assert(window_); if (window_) { return window_->get_title(); } // Kinky return "Bad window"; } void gpu_window::resize(size_t height, size_t width) { assert(window_); if (window_) { window_->resize(height, width); } } conveyor> gpu_window::on_event() { assert(window_); return window_->on_event(); } } }