diff --git a/.gitignore b/.gitignore index 0def275..d9050d3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,21 +1,2 @@ -# Compiled Object files -*.slo -*.lo -*.o -*.obj - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app +build +.syntastic diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..5b37663 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,28 @@ +PROJECT(cairo-gl-sdl2) + +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) + +SET(CMAKE_CXX_FLAGS "-W -Wall -Wno-unused-parameter -Werror -O3 -std=c++11") + +INCLUDE(FindPkgConfig) + +PKG_CHECK_MODULES(CAIRO REQUIRED cairo) +PKG_CHECK_MODULES(SDL2 REQUIRED sdl2) + +INCLUDE_DIRECTORIES( + ${CAIRO_INCLUDE_DIRS} + ${SDL2_INCLUDE_DIRS} +) + +LINK_DIRECTORIES( + ${CAIRO_LIBRARY_DIRS} + ${SDL2_LIBRARY_DIRS} +) + +ADD_EXECUTABLE(cairo-gl-sdl2 "src/main.cpp") + +TARGET_LINK_LIBRARIES(cairo-gl-sdl2 + ${CAIRO_LIBRARIES} + ${SDL2_LIBRARIES} +) + diff --git a/README.md b/README.md index 3185762..a6dc7b4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,9 @@ cairo-gl-sdl2 ============= -Cairo, SDL2, and GL/GLES2 tests. +This project is a test bed for using Cairo, SDL2, and GL/GLES2 together. +It differs from existing projects in that it uses the new (ACTUAL) OpenGL +Cairo backend, which has recently received a lot of development attention. + +I have found it to be almost twice as fast as the image backend, though +it can be up to triple the speed in some cases. diff --git a/src/SDL2.hpp b/src/SDL2.hpp new file mode 100644 index 0000000..7ee036c --- /dev/null +++ b/src/SDL2.hpp @@ -0,0 +1,154 @@ +#ifndef SDL2_HPP +#define SDL2_HPP + +#include +#include +#include +#include + +class SDL2Window { +public: + typedef std::tuple size_type; + + SDL2Window(): + _window(nullptr), + _context(nullptr) { + } + + bool init(int width, int height, unsigned int flags=0) { + if(SDL_Init(SDL_INIT_VIDEO)) { + std::cerr << "SDL_Init failed!" << std::endl; + + return false; + } + + _window = SDL_CreateWindow( + "Cairo+OpenGL+SDL2", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + width, + height, + SDL_WINDOW_OPENGL | flags + ); + + if(!_window) { + std::cerr << "SDL_CreateWindow failed!" << std::endl; + + return false; + } + + _context = SDL_GL_CreateContext(_window); + + if(!_context) { + std::cerr << "SDL_GL_CreateContext failed." << std::endl; + + return false; + } + + SDL_VERSION(&_info.version); + + if(!SDL_GetWindowWMInfo(_window, &_info)) { + std::cout << SDL_FALSE << std::endl; + std::cerr << "SDL_GetWindowWMInfofailed." << std::endl; + std::cerr << SDL_GetError() << std::endl; + + return false; + } + + return true; + } + + double main() { + double frames = 0.0; + bool running = true; + Uint32 start = SDL_GetTicks(); + + SDL_Event event; + + while(running) { + while(SDL_PollEvent(&event)) { + if(_mainQuit(event)) running = false; + } + + // DO STUFF + + SDL_GL_SwapWindow(_window); + + frames += 1.0; + } + + SDL_GL_DeleteContext(_context); + SDL_DestroyWindow(_window); + SDL_Quit(); + + return frames / static_cast(SDL_GetTicks() - start); + } + + size_type getSize() const { + int w = 0; + int h = 0; + + SDL_GetWindowSize(_window, &w, &h); + + return std::make_tuple(w, h); + } + + int getWidth() const { + int w = 0; + + std::tie(w, std::ignore) = getSize(); + + return w; + } + + int getHeight() const { + int h = 0; + + std::tie(std::ignore, h) = getSize(); + + return h; + } + + bool isFullscreen() const { + return SDL_GetWindowFlags(_window) & SDL_WINDOW_FULLSCREEN; + } + + void setFullscreen(bool go_fullscreen) const { + SDL_SetWindowFullscreen(_window, go_fullscreen ? 1 : 0); + } + + const char* getTitle() const { + return SDL_GetWindowTitle(_window); + } + + void setTitle(const std::string& title) { + SDL_SetWindowTitle(_window, title.c_str()); + } + + Display* getDisplay() { + return _info.info.x11.display; + }; + + SDL_GLContext getContext() { + // if(!SDL_GL_MakeCurrent(_window, _context)) return nullptr; + + return _context; + } + +protected: + inline bool _mainQuit(const SDL_Event& event) const { + if(event.type == SDL_QUIT || ( + event.type == SDL_KEYUP && + event.key.keysym.sym == SDLK_ESCAPE + )) return true; + + return false; + } + + SDL_Window* _window; + SDL_GLContext _context; + SDL_SysWMinfo _info; +}; + +#endif + diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..3bc305c --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,121 @@ +#include "SDL2.hpp" + +#include +#include +#include +#include +#include +#include + +// Size of the surface. +const unsigned int WIDTH = 512; +const unsigned int HEIGHT = 512; + +inline void draw(cairo_t* cr) { + cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 1.0); + cairo_paint(cr); + cairo_arc(cr, 256.0, 256.0, 200.0, 0.0, 2.0 * 3.14159); + cairo_set_source_rgba(cr, 1.0, 0.0, 0.5, 0.5); + cairo_fill(cr); +} + +int main(int argc, char** argv) { + if(argc != 3) { + std::cerr + << "Usage: " << argv[0] + << " num_draws [image | gl | gl_texture]" << std::endl + ; + + return 1; + } + + SDL2Window window; + + if(!window.init(WIDTH, HEIGHT, SDL_WINDOW_HIDDEN)) { + std::cerr << "Couldn't initialize SDL2 window; fatal." << std::endl; + + return 2; + } + + cairo_device_t* device = cairo_glx_device_create( + window.getDisplay(), + reinterpret_cast(window.getContext()) + ); + + if(!device) { + std::cerr << "Couldn't create device; fatal." << std::endl; + + return 3; + } + + // Variable initialization. In the future, I'll need to setup + // the OpenGL texture to use here as well. + auto num_draws = std::atoi(argv[1]); + auto method = std::string(argv[2]); + cairo_surface_t* surface = nullptr; + + if(method == "image" ) surface = cairo_image_surface_create( + CAIRO_FORMAT_ARGB32, + WIDTH, + HEIGHT + ); + + else if(method == "gl") surface = cairo_gl_surface_create( + device, + CAIRO_CONTENT_COLOR_ALPHA, + WIDTH, + HEIGHT + ); + + // TODO: Implemente cairo_gl_surface_create_for_texture test. + // else if(method == "gl_texture") + + else { + std::cerr << "Unknown surface type '" << method << "'; fatal." << std::endl; + + return 4; + } + + if(!surface) { + std::cerr << "Couldn't create surface; fatal." << std::endl; + + return 5; + } + + std::cout << "Performing " << num_draws << " iterations: " << std::flush; + + auto start = std::chrono::system_clock::now(); + auto last_tick = 0; + + for(auto i = 0; i < num_draws; i++) { + cairo_t* cr = cairo_create(surface); + + draw(cr); + + cairo_destroy(cr); + + // This is a completely wretched way of doing a progress meter, + // but it's the best I'm willing to do for now. + double pct = (static_cast(i) / static_cast(num_draws)) * 100.0; + + if(pct >= last_tick + 10.0) { + std::cout << "+" << std::flush; + + last_tick = pct; + } + } + + auto end = std::chrono::system_clock::now(); + + std::cout + << " done! (" + << std::chrono::duration_cast(end - start).count() + << "ms)" << std::endl + ; + + cairo_surface_destroy(surface); + cairo_device_destroy(device); + + return 0; +} +