From 36a32385ed599834bd4d9df537014645fefe0d33 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Sun, 19 Apr 2015 18:18:05 +0200 Subject: [PATCH 01/32] buildsystem: inotify package search name lowercased --- buildsystem/modules/FindInotify.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buildsystem/modules/FindInotify.cmake b/buildsystem/modules/FindInotify.cmake index 18addb5202..4b0cd367e3 100644 --- a/buildsystem/modules/FindInotify.cmake +++ b/buildsystem/modules/FindInotify.cmake @@ -1,4 +1,4 @@ -# Copyright 2014-2014 the openage authors. See copying.md for legal info. +# Copyright 2014-2015 the openage authors. See copying.md for legal info. # This module defines # @@ -8,6 +8,6 @@ find_path(INOTIFY_INCLUDE_DIR sys/inotify.h HINTS /usr/include/${CMAKE_LIBRARY_ARCHITECTURE}) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(INOTIFY DEFAULT_MSG INOTIFY_INCLUDE_DIR) +find_package_handle_standard_args(inotify DEFAULT_MSG INOTIFY_INCLUDE_DIR) mark_as_advanced(INOTIFY_INCLUDE_DIR) From 16aea568834b3ff220462926de6c4ad7097d6cd4 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Sun, 19 Apr 2015 18:20:03 +0200 Subject: [PATCH 02/32] buildsystem: implemented vulkan cmake module --- buildsystem/modules/FindVulkan.cmake | 50 ++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 buildsystem/modules/FindVulkan.cmake diff --git a/buildsystem/modules/FindVulkan.cmake b/buildsystem/modules/FindVulkan.cmake new file mode 100644 index 0000000000..6f5b3909ed --- /dev/null +++ b/buildsystem/modules/FindVulkan.cmake @@ -0,0 +1,50 @@ +# Copyright 2015-2015 the openage authors. See copying.md for legal info. +# +# (To distribute this file outside of openage, extend the above +# copyright line with an appropriate GPLv3 or later reference, +# as you probably don't have a our copying.md) + + +#================================================================= +# Locate Vulkan graphics library +# +# usage: +# find_package(Vulkan) +# +# This module sets the following variables: +# VULKAN_FOUND True, if the system has Vulkan. +# VULKAN_INCLUDE_DIR Path to the Vulkan include directory. +# VULKAN_LIBRARIES Paths to the Vulkan libraries. +#================================================================= + +set(_Vulkan_REQUIRED_VARS "VULKAN_vk_LIBRARY" "VULKAN_INCLUDE_DIR") + +find_path(VULKAN_INCLUDE_DIR + VK/vk.h + /opt/graphics/Vulkan/include +) + +find_library(VULKAN_vk_LIBRARY + NAMES VK Vulkan + PATHS + /opt/graphics/Vulkan/lib +) + +if(VULKAN_vk_LIBRARY) + set(VULKAN_LIBRARIES ${VULKAN_vk_LIBRARY}) +endif() + + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + Vulkan + REQUIRED_VARS ${_Vulkan_REQUIRED_VARS} + FAIL_MESSAGE "Vulkan NOT found." +) + +unset(_Vulkan_REQUIRED_VARS) + +mark_as_advanced( + VULKAN_INCLUDE_DIR + VULKAN_vk_LIBRARY +) From 7880ff0d73a00f4760ae19b4d628ea85b7e1a312 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Sun, 19 Apr 2015 18:21:06 +0200 Subject: [PATCH 03/32] buildsystem: integrated vulkan and opengl detection --- CMakeLists.txt | 10 ++++++++++ configure | 2 ++ libopenage/CMakeLists.txt | 30 +++++++++++++++++++++++++++++- libopenage/config.h.in | 2 ++ 4 files changed, 43 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e028e768e2..67584b346e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,6 +52,14 @@ if(NOT DEFINED WANT_INOTIFY) set(WANT_INOTIFY if_available) endif() +if(NOT DEFINED WANT_OPENGL) + set(WANT_OPENGL if_available) +endif() + +if(NOT DEFINED WANT_VULKAN) + set(WANT_VULKAN if_available) +endif() + if(NOT DEFINED WANT_GPERFTOOLS_PROFILER) set(WANT_GPERFTOOLS_PROFILER if_available) endif() @@ -59,7 +67,9 @@ endif() if(NOT DEFINED WANT_GPERFTOOLS_TCMALLOC) set(WANT_GPERFTOOLS_TCMALLOC false) endif() +# -- options +# set cmake paths set(BUILDSYSTEM_DIR "${CMAKE_SOURCE_DIR}/buildsystem") set(CMAKE_MODULE_PATH "${BUILDSYSTEM_DIR}" "${BUILDSYSTEM_DIR}/modules/") diff --git a/configure b/configure index e9c110ff25..12b7fcc9b8 100755 --- a/configure +++ b/configure @@ -69,6 +69,8 @@ def features(args, parser): options = { "backtrace": "if_available", "inotify": "if_available", + "opengl": "if_available", + "vulkan": "if_available", "gperftools-tcmalloc": False, "gperftools-profiler": "if_available", } diff --git a/libopenage/CMakeLists.txt b/libopenage/CMakeLists.txt index c1653f29c6..7dea7eb532 100644 --- a/libopenage/CMakeLists.txt +++ b/libopenage/CMakeLists.txt @@ -79,7 +79,6 @@ if(NOT WIN32) endif() find_package(Freetype REQUIRED) -find_package(OpenGL REQUIRED) find_package(SDL2 REQUIRED) find_package(SDL2Image REQUIRED) find_package(Opusfile REQUIRED) @@ -134,6 +133,35 @@ else() have_config_option(inotify INOTIFY false) endif() +# opengl support +if(WANT_OPENGL) + find_package(OpenGL) +endif() + +# vulkan support +if(WANT_VULKAN) + find_package(Vulkan) +endif() + +if(WANT_OPENGL AND OPENGL_FOUND) + have_config_option(opengl OPENGL true) + include_directories(${OPENGL_INCLUDE_DIR}) +else() + have_config_option(opengl OPENGL false) +endif() + +if(WANT_VULKAN AND VULKAN_FOUND) + have_config_option(vulkan VULKAN true) + include_directories(${VULKAN_INCLUDE_DIR}) +else() + have_config_option(vulkan VULKAN false) +endif() + +if(NOT (OPENGL_FOUND OR VULKAN_FOUND)) + message(FATAL_ERROR "One of OpenGL or Vulkan is required!") +endif() + + get_config_option_string() configure_file(config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/config.h) diff --git a/libopenage/config.h.in b/libopenage/config.h.in index ded482392f..fb22991587 100644 --- a/libopenage/config.h.in +++ b/libopenage/config.h.in @@ -8,6 +8,8 @@ #define WITH_BACKTRACE ${WITH_BACKTRACE} #define WITH_INOTIFY ${WITH_INOTIFY} +#define WITH_OPENGL ${WITH_OPENGL} +#define WITH_VULKAN ${WITH_VULKAN} #define WITH_GPERFTOOLS_PROFILER ${WITH_GPERFTOOLS_PROFILER} #define WITH_GPERFTOOLS_TCMALLOC ${WITH_GPERFTOOLS_TCMALLOC} From f36e375b4255a2a50183c6216723fea5429d9122 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Tue, 21 Apr 2015 01:57:40 +0200 Subject: [PATCH 04/32] renderer: modular render window and context creation --- libopenage/CMakeLists.txt | 3 +- libopenage/engine.cpp | 89 ++-------------------- libopenage/engine.h | 24 +++--- libopenage/renderer/CMakeLists.txt | 6 +- libopenage/renderer/context.cpp | 56 ++++++++++++++ libopenage/renderer/context.h | 38 ++++++++++ libopenage/renderer/opengl/CMakeLists.txt | 3 + libopenage/renderer/opengl/context.cpp | 92 +++++++++++++++++++++++ libopenage/renderer/opengl/context.h | 31 ++++++++ libopenage/renderer/tests.cpp | 55 ++++++++++++++ libopenage/renderer/vulkan/context.h | 28 +++++++ libopenage/renderer/window.cpp | 71 +++++++++++++++++ libopenage/renderer/window.h | 39 ++++++++++ openage/testing/testlist.py | 2 + 14 files changed, 438 insertions(+), 99 deletions(-) create mode 100644 libopenage/renderer/context.cpp create mode 100644 libopenage/renderer/context.h create mode 100644 libopenage/renderer/opengl/CMakeLists.txt create mode 100644 libopenage/renderer/opengl/context.cpp create mode 100644 libopenage/renderer/opengl/context.h create mode 100644 libopenage/renderer/tests.cpp create mode 100644 libopenage/renderer/vulkan/context.h create mode 100644 libopenage/renderer/window.cpp create mode 100644 libopenage/renderer/window.h diff --git a/libopenage/CMakeLists.txt b/libopenage/CMakeLists.txt index 7dea7eb532..8569b10fca 100644 --- a/libopenage/CMakeLists.txt +++ b/libopenage/CMakeLists.txt @@ -40,8 +40,8 @@ add_subdirectory("gui") add_subdirectory("error") add_subdirectory("gamestate") add_subdirectory("input") -add_subdirectory("log") add_subdirectory("job") +add_subdirectory("log") add_subdirectory("pathfinding") add_subdirectory("pyinterface") add_subdirectory("renderer") @@ -52,7 +52,6 @@ add_subdirectory("testing") add_subdirectory("unit") add_subdirectory("util") - # run codegen, add files to executable codegen_run() add_sources(libopenage GENERATED ${CODEGEN_TARGET_TUS}) diff --git a/libopenage/engine.cpp b/libopenage/engine.cpp index 3bcd168524..972660842f 100644 --- a/libopenage/engine.cpp +++ b/libopenage/engine.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include "error/error.h" #include "error/gl_debug.h" @@ -104,84 +103,10 @@ Engine::Engine(util::Dir *data_dir, int32_t fps_limit, bool gl_debug, const char // execution list. this->register_resize_action(this); - if (SDL_Init(SDL_INIT_VIDEO) < 0) { - throw Error(MSG(err) << "SDL video initialization: " << SDL_GetError()); - } else { - log::log(MSG(info) << "Initialized SDL video subsystems."); - } - - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); - SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); - - int32_t window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_MAXIMIZED; - this->window = SDL_CreateWindow( - windowtitle, - SDL_WINDOWPOS_CENTERED, - SDL_WINDOWPOS_CENTERED, - this->engine_coord_data->window_size.x, - this->engine_coord_data->window_size.y, - window_flags - ); - - if (this->window == nullptr) { - throw Error(MSG(err) << "Failed to create SDL window: " << SDL_GetError()); - } - - // load support for the PNG image formats, jpg bit: IMG_INIT_JPG - int wanted_image_formats = IMG_INIT_PNG; - int sdlimg_inited = IMG_Init(wanted_image_formats); - if ((sdlimg_inited & wanted_image_formats) != wanted_image_formats) { - throw Error(MSG(err) << "Failed to init PNG support: " << IMG_GetError()); - } - - if (gl_debug) - this->glcontext = error::create_debug_context(this->window); - else - this->glcontext = SDL_GL_CreateContext(this->window); - - if (this->glcontext == nullptr) { - throw Error(MSG(err) << "Failed creating OpenGL context: " << SDL_GetError()); - } - - // check the OpenGL version, for shaders n stuff - if (!epoxy_is_desktop_gl() || epoxy_gl_version() < 21) { - throw Error(MSG(err) << "OpenGL 2.1 not available"); - } - - // to quote the standard doc: - // 'The value gives a rough estimate - // of the largest texture that the GL can handle' - // -> wat? - // anyways, we need at least 1024x1024. - int max_texture_size; - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); - log::log(MSG(dbg) << "Maximum supported texture size: " << max_texture_size); - if (max_texture_size < 1024) { - throw Error(MSG(err) << "Maximum supported texture size too small: " << max_texture_size); - } - - int max_texture_units; - glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_texture_units); - log::log(MSG(dbg) << "Maximum supported texture units: " << max_texture_units); - if (max_texture_units < 2) { - throw Error(MSG(err) << "Your GPU has not enough texture units: " << max_texture_units); - } - - // vsync on - SDL_GL_SetSwapInterval(1); - - // enable alpha blending - glEnable(GL_BLEND); - - // order of drawing relevant for depth - // what gets drawn last is displayed on top. - glDisable(GL_DEPTH_TEST); + // register the engines input manager + this->register_input_action(&this->input_manager); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + this->window = std::make_unique(windowtitle); // qml sources will be installed to the asset dir // otherwise assume that launched from the source dir @@ -251,10 +176,6 @@ Engine::~Engine() { this->gui.reset(); delete this->job_manager; - SDL_GL_DeleteContext(glcontext); - SDL_DestroyWindow(window); - IMG_Quit(); - SDL_Quit(); } bool Engine::on_resize(coord::window new_size) { @@ -443,7 +364,6 @@ void Engine::loop() { if (this->drawing_debug_overlay.value) { this->draw_debug_overlay(); - } if (this->drawing_huds.value) { @@ -467,7 +387,7 @@ void Engine::loop() { // the rendering is done // swap the drawing buffers to actually show the frame - SDL_GL_SwapWindow(window); + this->window->swap(); if (this->ns_per_frame != 0) { uint64_t ns_for_current_frame = cap_timer.getval(); @@ -476,6 +396,7 @@ void Engine::loop() { } } + // vsync wait time is over. this->profiler.end_measure("idle"); this->profiler.end_frame_measure(); diff --git a/libopenage/engine.h b/libopenage/engine.h index 1fea0559e9..5d5ac3c391 100644 --- a/libopenage/engine.h +++ b/libopenage/engine.h @@ -5,11 +5,8 @@ #include #include #include - #include -#include - #include #include "log/log.h" @@ -24,10 +21,11 @@ #include "game_singletons_info.h" #include "handlers.h" #include "options.h" -#include "job/job_manager.h" // pxd: from libopenage.input.input_manager cimport InputManager #include "input/input_manager.h" #include "input/action.h" +#include "job/job_manager.h" +#include "renderer/window.h" #include "util/externalprofiler.h" #include "util/dir.h" #include "util/fps.h" @@ -424,15 +422,10 @@ class Engine : public ResizeHandler, public options::OptionNode { std::unordered_map fonts; /** - * SDL window where everything is displayed within. - */ - SDL_Window *window; - - /** - * SDL OpenGL context, we'll only have one, - * but it would allow having multiple ones. + * The render window. Everything is drawn in here. + * Also contains the context. */ - SDL_GLContext glcontext; + std::unique_ptr window; /** * the gui binding @@ -444,7 +437,14 @@ class Engine : public ResizeHandler, public options::OptionNode { */ util::Profiler profiler; + /** + * The font manager to provide different sized and styled fonts. + */ std::unique_ptr font_manager; + + /** + * The engine's text renderer. To be integrated into the main renderer. + */ std::unique_ptr text_renderer; public: diff --git a/libopenage/renderer/CMakeLists.txt b/libopenage/renderer/CMakeLists.txt index fbaa233231..cd727608a2 100644 --- a/libopenage/renderer/CMakeLists.txt +++ b/libopenage/renderer/CMakeLists.txt @@ -1,6 +1,10 @@ add_sources(libopenage color.cpp + context.cpp + tests.cpp text.cpp + window.cpp ) -add_subdirectory(font) +add_subdirectory(font/) +add_subdirectory(opengl/) diff --git a/libopenage/renderer/context.cpp b/libopenage/renderer/context.cpp new file mode 100644 index 0000000000..e87a501c8c --- /dev/null +++ b/libopenage/renderer/context.cpp @@ -0,0 +1,56 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#include "context.h" + +#include "../config.h" +#include "../log/log.h" +#include "../util/error.h" + +#if WITH_OPENGL +#include "gl/context.h" +#endif + +#if WITH_VULKAN +#include "vulkan/context.h" +#endif + + +namespace openage { +namespace renderer { + +Context::Context() {} +Context::~Context() {} + +std::unique_ptr Context::generate(context_type t) { + if (t == context_type::opengl and not WITH_OPENGL) { + throw util::Error(MSG(err) << "OpenGL support not enabled!"); + } + else if (t == context_type::vulkan and not WITH_VULKAN) { + throw util::Error(MSG(err) << "Vulkan support not enabled!"); + } + else if (t == context_type::autodetect) { + // priority: vulkan > opengl + if (WITH_VULKAN) { +#if WITH_VULKAN + log::log(MSG(dbg) << "Using Vulkan context..."); + return std::make_unique(); +#endif + } + else if (WITH_OPENGL) { +#if WITH_OPENGL + log::log(MSG(dbg) << "Using OpenGL context..."); + return std::make_unique(); +#endif + } + else { + throw util::Error(MSG(err) << "No render context available!"); + } + } + else { + throw util::Error(MSG(err) << "Unknown context type requested!"); + } + + throw util::Error(MSG(err) << "Context creation dead code reached! Veeery bad."); +} + +}} // namespace openage::renderer diff --git a/libopenage/renderer/context.h b/libopenage/renderer/context.h new file mode 100644 index 0000000000..c63551302e --- /dev/null +++ b/libopenage/renderer/context.h @@ -0,0 +1,38 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_CONTEXT_H_ +#define OPENAGE_RENDERER_CONTEXT_H_ + +#include + +#include + +#include "../coord/window.h" + + +namespace openage { +namespace renderer { + +enum class context_type { + autodetect, + opengl, + vulkan, +}; + +class Context { +public: + Context(); + virtual ~Context(); + + static std::unique_ptr generate(context_type t); + + virtual void prepare() = 0; + virtual uint32_t get_window_flags() = 0; + virtual void create(SDL_Window *window) = 0; + virtual void setup() = 0; + virtual void destroy() = 0; +}; + +}} // namespace openage::renderer + +#endif diff --git a/libopenage/renderer/opengl/CMakeLists.txt b/libopenage/renderer/opengl/CMakeLists.txt new file mode 100644 index 0000000000..79a04cc645 --- /dev/null +++ b/libopenage/renderer/opengl/CMakeLists.txt @@ -0,0 +1,3 @@ +add_sources(libopenage + context.cpp +) diff --git a/libopenage/renderer/opengl/context.cpp b/libopenage/renderer/opengl/context.cpp new file mode 100644 index 0000000000..0e7a66888b --- /dev/null +++ b/libopenage/renderer/opengl/context.cpp @@ -0,0 +1,92 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#include "../../config.h" +#if WITH_OPENGL + +#include "context.h" + +#include +#include + +#include "../../log/log.h" +#include "../../util/error.h" + +namespace openage { +namespace renderer { + +// TODO: get max available gl version +constexpr int opengl_version_major = 2; +constexpr int opengl_version_minor = 1; + +GLContext::GLContext() {} +GLContext::~GLContext() {} + +uint32_t GLContext::get_window_flags() { + return SDL_WINDOW_OPENGL; +} + +void GLContext::prepare() { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, opengl_version_major); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, opengl_version_minor); + SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); +} + +void GLContext::create(SDL_Window *window) { + this->glcontext = SDL_GL_CreateContext(window); + + if (this->glcontext == nullptr) { + throw util::Error(MSG(err) << "Failed creating OpenGL context: " << SDL_GetError()); + } + + // check the OpenGL version, for shaders n stuff + int epoxy_glv = opengl_version_major * 10 + opengl_version_minor; + if (not epoxy_is_desktop_gl() or epoxy_gl_version() < epoxy_glv) { + throw util::Error(MSG(err) << "OpenGL " + << opengl_version_major << "." << opengl_version_minor + << " not available"); + } +} + +void GLContext::setup() { + // to quote the standard doc: 'The value gives a rough estimate of the + // largest texture that the GL can handle' + // -> wat? anyways, we need at least 1024x1024. + int max_texture_size; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); + log::log(MSG(dbg) << "Maximum supported texture size: " << max_texture_size); + if (max_texture_size < 1024) { + throw util::Error(MSG(err) << "Maximum supported texture size too small: " << max_texture_size); + } + + int max_texture_units; + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_texture_units); + log::log(MSG(dbg) << "Maximum supported texture units: " << max_texture_units); + if (max_texture_units < 2) { + throw util::Error(MSG(err) << "Your GPU has not enough texture units: " << max_texture_units); + } + + // vsync on + SDL_GL_SetSwapInterval(1); + + // TODO: move the following statements to some other place. + + // enable alpha blending + glEnable(GL_BLEND); + + // order of drawing relevant for depth + // what gets drawn last is displayed on top. + glDisable(GL_DEPTH_TEST); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + +void GLContext::destroy() { + SDL_GL_DeleteContext(this->glcontext); +} + +}} // namespace openage::renderer + +#endif // if WITH_OPENGL diff --git a/libopenage/renderer/opengl/context.h b/libopenage/renderer/opengl/context.h new file mode 100644 index 0000000000..086d612211 --- /dev/null +++ b/libopenage/renderer/opengl/context.h @@ -0,0 +1,31 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_OPENGL_CONTEXT_H_ +#define OPENAGE_RENDERER_OPENGL_CONTEXT_H_ + +#include + +#include "../context.h" +#include "../../config.h" + + +namespace openage { +namespace renderer { + +class GLContext : public Context { +public: + GLContext(); + ~GLContext(); + + SDL_GLContext glcontext; + + virtual void prepare(); + virtual uint32_t get_window_flags(); + virtual void create(SDL_Window *window); + virtual void setup(); + virtual void destroy(); +}; + +}} // namespace openage::renderer + +#endif diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp new file mode 100644 index 0000000000..478809a45a --- /dev/null +++ b/libopenage/renderer/tests.cpp @@ -0,0 +1,55 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#include "../log/log.h" +#include "window.h" + +namespace openage { +namespace renderer { +namespace tests { + + +void renderer_demo(int /*argc*/, char **/*argv*/) { + Window window{"testing!"}; + SDL_Event event; + + bool running = true; + while (running) { + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_WINDOWEVENT: + switch(event.window.event) { + case SDL_WINDOWEVENT_RESIZED: { + coord::window new_size{event.window.data1, event.window.data2}; + log::log( + MSG(info) << "new window size: " + << new_size.x << " x " << new_size.y << "." + ); + break; + }} + break; + + case SDL_QUIT: + running = false; + break; + + case SDL_KEYUP: { + SDL_Keycode sym = reinterpret_cast(&event)->keysym.sym; + switch (sym) { + case SDLK_ESCAPE: + running = false; + break; + default: + break; + } + break; + }} + } + + window.swap(); + } + + return; +} + + +}}} // namespace openage::renderer::tests diff --git a/libopenage/renderer/vulkan/context.h b/libopenage/renderer/vulkan/context.h new file mode 100644 index 0000000000..815935ea43 --- /dev/null +++ b/libopenage/renderer/vulkan/context.h @@ -0,0 +1,28 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_VULKAN_CONTEXT_H_ +#define OPENAGE_RENDERER_VULKAN_CONTEXT_H_ + +#include + +#include "../context.h" +#include "../../config.h" + + +namespace openage { +namespace renderer { + +class VulkanContext : public Context { +public: + SDL_VulkanContext vkcontext; + + virtual void prepare() = 0; + virtual uint32_t get_window_flags() = 0; + virtual void create(SDL_Window *window) = 0; + virtual void setup() = 0; + virtual void destroy() = 0; +}; + +}} // namespace openage::renderer + +#endif diff --git a/libopenage/renderer/window.cpp b/libopenage/renderer/window.cpp new file mode 100644 index 0000000000..ffffa7ce7a --- /dev/null +++ b/libopenage/renderer/window.cpp @@ -0,0 +1,71 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#include "window.h" + +#include +#include +#include + +#include "../log/log.h" +#include "../util/error.h" + + +namespace openage { +namespace renderer { + +Window::Window(const char *title) { + this->context = Context::generate(context_type::autodetect); + + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + throw util::Error(MSG(err) << "SDL video initialization: " << SDL_GetError()); + } else { + log::log(MSG(info) << "Initialized SDL video subsystems."); + } + + this->context->prepare(); + + int32_t window_flags = this->context->get_window_flags() | SDL_WINDOW_RESIZABLE | SDL_WINDOW_MAXIMIZED; + this->window = SDL_CreateWindow( + title, + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + this->window_size.x, + this->window_size.y, + window_flags + ); + + if (this->window == nullptr) { + throw util::Error(MSG(err) << "Failed to create SDL window: " << SDL_GetError()); + } else { + log::log(MSG(info) << "Created SDL window."); + } + + this->context->create(this->window); + + // load support for the PNG image formats, jpg bit: IMG_INIT_JPG + int wanted_image_formats = IMG_INIT_PNG; + int sdlimg_inited = IMG_Init(wanted_image_formats); + if ((sdlimg_inited & wanted_image_formats) != wanted_image_formats) { + throw util::Error(MSG(err) << "Failed to init PNG support: " << IMG_GetError()); + } + + this->context->setup(); +} + +Window::~Window() { + log::log(MSG(info) << "Destroying render context..."); + this->context->destroy(); + + log::log(MSG(info) << "Destroying window..."); + SDL_DestroyWindow(this->window); + + log::log(MSG(info) << "Exiting SDL..."); + IMG_Quit(); + SDL_Quit(); +} + +void Window::swap() { + SDL_GL_SwapWindow(this->window); +} + +}} // namespace openage::renderer diff --git a/libopenage/renderer/window.h b/libopenage/renderer/window.h new file mode 100644 index 0000000000..9a59113e18 --- /dev/null +++ b/libopenage/renderer/window.h @@ -0,0 +1,39 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_WINDOW_H_ +#define OPENAGE_RENDERER_WINDOW_H_ + +#include "context.h" + +#include +#include + +#include "../coord/window.h" + +namespace openage { +namespace renderer { + + +class Window { +public: + Window(const Window &other) = delete; + Window(Window &&other) = delete; + Window &operator =(const Window &other) = delete; + Window &operator =(Window &&other) = delete; + + + Window(const char *title); + ~Window(); + + void swap(); + +private: + coord::window window_size; + SDL_Window *window; + + std::unique_ptr context; +}; + +}} // namespace openage::renderer + +#endif diff --git a/openage/testing/testlist.py b/openage/testing/testlist.py index d6bb2a3dd8..17b38ba03c 100644 --- a/openage/testing/testlist.py +++ b/openage/testing/testlist.py @@ -86,3 +86,5 @@ def demos_cpp(): "translates a Python exception to C++") yield ("openage::pyinterface::tests::pyobject_demo", "a tiny interactive interpreter using PyObjectRef") + yield ("openage::renderer::tests::renderer_demo", + "showcases the new renderer") From 4b745d21c8dfcd6d56b3dc02bdf5b02a0e7dc4d7 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Fri, 26 Jun 2015 01:13:38 +0200 Subject: [PATCH 05/32] renderer: legacy opengl square rendering --- libopenage/renderer/context.cpp | 41 +++++++++------ libopenage/renderer/context.h | 3 -- libopenage/renderer/opengl/context.cpp | 17 +++--- libopenage/renderer/opengl/context.h | 11 ++-- libopenage/renderer/tests.cpp | 72 ++++++++++++++++++++++++-- libopenage/renderer/vulkan/context.h | 3 +- libopenage/renderer/window.cpp | 22 ++++++-- libopenage/renderer/window.h | 4 +- 8 files changed, 131 insertions(+), 42 deletions(-) diff --git a/libopenage/renderer/context.cpp b/libopenage/renderer/context.cpp index e87a501c8c..bbd893ccf2 100644 --- a/libopenage/renderer/context.cpp +++ b/libopenage/renderer/context.cpp @@ -7,7 +7,7 @@ #include "../util/error.h" #if WITH_OPENGL -#include "gl/context.h" +#include "opengl/context.h" #endif #if WITH_VULKAN @@ -22,30 +22,39 @@ Context::Context() {} Context::~Context() {} std::unique_ptr Context::generate(context_type t) { - if (t == context_type::opengl and not WITH_OPENGL) { - throw util::Error(MSG(err) << "OpenGL support not enabled!"); - } - else if (t == context_type::vulkan and not WITH_VULKAN) { - throw util::Error(MSG(err) << "Vulkan support not enabled!"); - } - else if (t == context_type::autodetect) { + context_type ctx_requested = t; + + if (t == context_type::autodetect) { // priority: vulkan > opengl if (WITH_VULKAN) { -#if WITH_VULKAN - log::log(MSG(dbg) << "Using Vulkan context..."); - return std::make_unique(); -#endif + ctx_requested = context_type::vulkan; } else if (WITH_OPENGL) { -#if WITH_OPENGL - log::log(MSG(dbg) << "Using OpenGL context..."); - return std::make_unique(); -#endif + ctx_requested = context_type::opengl; } else { throw util::Error(MSG(err) << "No render context available!"); } } + + if (ctx_requested == context_type::opengl) { + if (not WITH_OPENGL) { + throw util::Error(MSG(err) << "OpenGL support not enabled!"); + } +#if WITH_OPENGL + log::log(MSG(dbg) << "Using OpenGL context..."); + return std::make_unique(); +#endif + } + else if (ctx_requested == context_type::vulkan) { + if (not WITH_VULKAN) { + throw util::Error(MSG(err) << "Vulkan support not enabled!"); + } +#if WITH_VULKAN + log::log(MSG(dbg) << "Using Vulkan context..."); + return std::make_unique(); +#endif + } else { throw util::Error(MSG(err) << "Unknown context type requested!"); } diff --git a/libopenage/renderer/context.h b/libopenage/renderer/context.h index c63551302e..9c7e479dd1 100644 --- a/libopenage/renderer/context.h +++ b/libopenage/renderer/context.h @@ -7,9 +7,6 @@ #include -#include "../coord/window.h" - - namespace openage { namespace renderer { diff --git a/libopenage/renderer/opengl/context.cpp b/libopenage/renderer/opengl/context.cpp index 0e7a66888b..0da6fc1178 100644 --- a/libopenage/renderer/opengl/context.cpp +++ b/libopenage/renderer/opengl/context.cpp @@ -13,19 +13,20 @@ namespace openage { namespace renderer { +namespace opengl { // TODO: get max available gl version constexpr int opengl_version_major = 2; constexpr int opengl_version_minor = 1; -GLContext::GLContext() {} -GLContext::~GLContext() {} +Context::Context() {} +Context::~Context() {} -uint32_t GLContext::get_window_flags() { +uint32_t Context::get_window_flags() { return SDL_WINDOW_OPENGL; } -void GLContext::prepare() { +void Context::prepare() { SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, opengl_version_major); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, opengl_version_minor); @@ -34,7 +35,7 @@ void GLContext::prepare() { SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); } -void GLContext::create(SDL_Window *window) { +void Context::create(SDL_Window *window) { this->glcontext = SDL_GL_CreateContext(window); if (this->glcontext == nullptr) { @@ -50,7 +51,7 @@ void GLContext::create(SDL_Window *window) { } } -void GLContext::setup() { +void Context::setup() { // to quote the standard doc: 'The value gives a rough estimate of the // largest texture that the GL can handle' // -> wat? anyways, we need at least 1024x1024. @@ -83,10 +84,10 @@ void GLContext::setup() { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } -void GLContext::destroy() { +void Context::destroy() { SDL_GL_DeleteContext(this->glcontext); } -}} // namespace openage::renderer +}}} // namespace openage::renderer::opengl #endif // if WITH_OPENGL diff --git a/libopenage/renderer/opengl/context.h b/libopenage/renderer/opengl/context.h index 086d612211..416b426558 100644 --- a/libopenage/renderer/opengl/context.h +++ b/libopenage/renderer/opengl/context.h @@ -4,18 +4,19 @@ #define OPENAGE_RENDERER_OPENGL_CONTEXT_H_ #include +#include #include "../context.h" -#include "../../config.h" namespace openage { namespace renderer { +namespace opengl { -class GLContext : public Context { +class Context : public renderer::Context { public: - GLContext(); - ~GLContext(); + Context(); + ~Context(); SDL_GLContext glcontext; @@ -26,6 +27,6 @@ class GLContext : public Context { virtual void destroy(); }; -}} // namespace openage::renderer +}}} // namespace openage::renderer #endif diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index 478809a45a..d905ea9cde 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -1,5 +1,10 @@ // Copyright 2015-2015 the openage authors. See copying.md for legal info. +#include +#include +#include +#include + #include "../log/log.h" #include "window.h" @@ -8,22 +13,33 @@ namespace renderer { namespace tests { -void renderer_demo(int /*argc*/, char **/*argv*/) { - Window window{"testing!"}; +struct render_demo { + std::function setup; + std::function frame; + std::function resize; +}; + + +void create_window(const render_demo *actions) { + Window window{"openage renderer testing"}; SDL_Event event; + actions->setup(&window); + bool running = true; while (running) { while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_WINDOWEVENT: - switch(event.window.event) { + switch (event.window.event) { case SDL_WINDOWEVENT_RESIZED: { coord::window new_size{event.window.data1, event.window.data2}; log::log( MSG(info) << "new window size: " - << new_size.x << " x " << new_size.y << "." + << new_size.x << " x " << new_size.y ); + window.set_size(new_size); + actions->resize(new_size); break; }} break; @@ -45,10 +61,56 @@ void renderer_demo(int /*argc*/, char **/*argv*/) { }} } + actions->frame(); + window.swap(); } +} - return; + +void renderer_demo(int argc, char **argv) { + int demo_id ; + if (argc == 2) { + demo_id = atoi(argv[1]); + log::log(MSG(info) << "running renderer demo " << demo_id << "..."); + } else { + demo_id = 0; + log::log(MSG(info) << "executing default renderer demo " << demo_id << "..."); + } + + render_demo test0{ + // init + [=](Window */*window*/) { + glEnable(GL_BLEND); + }, + // frame + [] { + glClearColor(0.0, 0.0, 1.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + glColor3f(1, 0, 0); + glBegin(GL_QUADS); { + glVertex2f(.0f, .0f); + glVertex2f(1.0f, .0f); + glVertex2f(1.0f, 1.0f); + glVertex2f(.0f, 1.0f); + } glEnd(); + }, + // resize + [](const coord::window &new_size) { + glViewport(0, 0, new_size.x, new_size.y); + } + }; + + std::unordered_map demos; + demos[0] = &test0; + + if (demos.find(demo_id) != demos.end()) { + create_window(demos[demo_id]); + } + else { + log::log(MSG(err) << "unknown renderer demo " << demo_id << " requested."); + } } diff --git a/libopenage/renderer/vulkan/context.h b/libopenage/renderer/vulkan/context.h index 815935ea43..c2f6a16926 100644 --- a/libopenage/renderer/vulkan/context.h +++ b/libopenage/renderer/vulkan/context.h @@ -11,8 +11,9 @@ namespace openage { namespace renderer { +namespace vulkan { -class VulkanContext : public Context { +class Context : public renderer::Context { public: SDL_VulkanContext vkcontext; diff --git a/libopenage/renderer/window.cpp b/libopenage/renderer/window.cpp index ffffa7ce7a..1fc7a8a367 100644 --- a/libopenage/renderer/window.cpp +++ b/libopenage/renderer/window.cpp @@ -13,7 +13,10 @@ namespace openage { namespace renderer { -Window::Window(const char *title) { +Window::Window(const char *title) + : + size{800, 600} { + this->context = Context::generate(context_type::autodetect); if (SDL_Init(SDL_INIT_VIDEO) < 0) { @@ -29,8 +32,8 @@ Window::Window(const char *title) { title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - this->window_size.x, - this->window_size.y, + this->size.x, + this->size.y, window_flags ); @@ -68,4 +71,17 @@ void Window::swap() { SDL_GL_SwapWindow(this->window); } +coord::window Window::get_size() { + return this->size; +} + +void Window::set_size(const coord::window &new_size, bool update) { + if (this->size.x != new_size.x or this->size.y != new_size.y) { + this->size = new_size; + if (update) { + SDL_SetWindowSize(this->window, this->size.x, this->size.y); + } + } +} + }} // namespace openage::renderer diff --git a/libopenage/renderer/window.h b/libopenage/renderer/window.h index 9a59113e18..1c06987207 100644 --- a/libopenage/renderer/window.h +++ b/libopenage/renderer/window.h @@ -21,6 +21,8 @@ class Window { Window &operator =(const Window &other) = delete; Window &operator =(Window &&other) = delete; + coord::window get_size(); + void set_size(const coord::window &, bool update=false); Window(const char *title); ~Window(); @@ -28,7 +30,7 @@ class Window { void swap(); private: - coord::window window_size; + coord::window size; SDL_Window *window; std::unique_ptr context; From 5ed8d717d1b7bdd8b087b6d791b152441ac81246 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Mon, 29 Jun 2015 17:04:32 +0200 Subject: [PATCH 06/32] renderer: initial generic texture implementation --- libopenage/renderer/CMakeLists.txt | 1 + libopenage/renderer/texture.cpp | 43 ++++++++++++++ libopenage/renderer/texture.h | 93 ++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 libopenage/renderer/texture.cpp create mode 100644 libopenage/renderer/texture.h diff --git a/libopenage/renderer/CMakeLists.txt b/libopenage/renderer/CMakeLists.txt index cd727608a2..d670d58799 100644 --- a/libopenage/renderer/CMakeLists.txt +++ b/libopenage/renderer/CMakeLists.txt @@ -3,6 +3,7 @@ add_sources(libopenage context.cpp tests.cpp text.cpp + texture.cpp window.cpp ) diff --git a/libopenage/renderer/texture.cpp b/libopenage/renderer/texture.cpp new file mode 100644 index 0000000000..cb63b494e3 --- /dev/null +++ b/libopenage/renderer/texture.cpp @@ -0,0 +1,43 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#include "texture.h" + +#include "../util/error.h" + +namespace openage { +namespace renderer { + +std::tuple Texture::get_size() const { + return std::make_tuple(this->w, this->h); +} + + +const gamedata::subtexture *Texture::get_subtexture(size_t subid) const { + if (subid < this->subtextures.size()) { + return &this->subtextures[subid]; + } + else { + throw util::Error(MSG(err) << "Unknown subtexture requested: " << subid); + } +} + +const std::tuple Texture::get_subtexture_coordinates(size_t subid) const { + auto tx = this->get_subtexture(subid); + return std::make_tuple( + ((float)tx->x) / this->w, + ((float)(tx->x + tx->w)) / this->w, + ((float)tx->y) / this->h, + ((float)(tx->y + tx->h)) / this->h + ); +} + +int Texture::get_subtexture_count() const { + return this->subtextures.size(); +} + +const std::tuple Texture::get_subtexture_size(size_t subid) const { + auto subtex = this->get_subtexture(subid); + return std::make_tuple(subtex->w, subtex->h); +} + +}} // namespace openage::renderer diff --git a/libopenage/renderer/texture.h b/libopenage/renderer/texture.h new file mode 100644 index 0000000000..170d078b28 --- /dev/null +++ b/libopenage/renderer/texture.h @@ -0,0 +1,93 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_TEXTURE_H_ +#define OPENAGE_RENDERER_TEXTURE_H_ + +#include +#include +#include + +#include "../gamedata/texture.gen.h" + +namespace openage { +namespace renderer { + +/** + * A texture for rendering graphically. + * + * You may believe it or not, but this class represents a single texture, + * which can be drawn on the screen. + * + * The class supports subtextures, so that one big texture ("texture atlas") + * can contain several smaller images. These are the ones actually to be + * rendered. + */ +class Texture { +public: + virtual ~Texture(); + + /** + * Return the dimensions of the whole texture bitmap + * @returns tuple(width, height) + */ + std::tuple get_size() const; + + /** + * Get the subtexture coordinates by its idea. + */ + const gamedata::subtexture *get_subtexture(size_t subid) const; + + /** + * @return the number of available subtextures + */ + int get_subtexture_count() const; + + /** + * Fetch the size of the given subtexture. + * @param subid: index of the requested subtexture + */ + const std::tuple get_subtexture_size(size_t subid) const; + + /** + * get atlas subtexture coordinates. + * + * @returns left, right, top and bottom bounds as coordinates these pick + * the requested area out of the big texture. returned as floats in + * range 0.0 to 1.0, relative to the whole bitmap size. + */ + const std::tuple get_subtexture_coordinates(size_t subid) const; + +protected: + std::vectorsubtextures; + size_t w, h; +}; + + +class RawTexture : public Texture { +public: + /** + * Create a texture from a rgba8 array. + * It will have w * h * 4byte storage. + * Each pixel has one byte, and r g b and alpha values. + */ + RawTexture(int width, int height, std::unique_ptr data); +}; + + +class FileTexture : public Texture { +public: + /** + * Create a texture from a existing image file. + * + * For supported image file types, see the SDL_Image initialization in + * the engine. + */ + FileTexture(const std::string &filename, bool use_metafile=false); +private: + bool use_metafile; + std::string filename; +}; + +}} // namespace openage::renderer + +#endif From 46049a85ba2c20a6e49de48b6450c2bfc8ffe80c Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Mon, 29 Jun 2015 17:04:49 +0200 Subject: [PATCH 07/32] renderer: rendering quad with opengl 3.3 --- README.md | 2 +- libopenage/renderer/context.cpp | 13 +- libopenage/renderer/material.h | 16 ++ libopenage/renderer/object.h | 24 +++ libopenage/renderer/opengl/CMakeLists.txt | 2 + libopenage/renderer/opengl/context.cpp | 20 +-- libopenage/renderer/opengl/program.cpp | 177 ++++++++++++++++++++++ libopenage/renderer/opengl/program.h | 79 ++++++++++ libopenage/renderer/opengl/shader.cpp | 72 +++++++++ libopenage/renderer/opengl/shader.h | 68 +++++++++ libopenage/renderer/quad.h | 21 +++ libopenage/renderer/renderer.cpp | 35 +++++ libopenage/renderer/renderer.h | 44 ++++++ libopenage/renderer/shader.h | 29 ++++ libopenage/renderer/target.h | 23 +++ libopenage/renderer/task.h | 33 ++++ libopenage/renderer/tests.cpp | 101 +++++++++--- libopenage/renderer/texture.cpp | 4 +- libopenage/renderer/texture.h | 2 +- libopenage/renderer/window.cpp | 8 +- libopenage/util/opengl.cpp | 1 + openage/CMakeLists.txt | 1 + openage/renderer/CMakeLists.txt | 8 + openage/renderer/__init__.py | 5 + openage/renderer/renderer_cpp.pyx | 1 + openage/renderer/tests.py | 5 + 26 files changed, 747 insertions(+), 47 deletions(-) create mode 100644 libopenage/renderer/material.h create mode 100644 libopenage/renderer/object.h create mode 100644 libopenage/renderer/opengl/program.cpp create mode 100644 libopenage/renderer/opengl/program.h create mode 100644 libopenage/renderer/opengl/shader.cpp create mode 100644 libopenage/renderer/opengl/shader.h create mode 100644 libopenage/renderer/quad.h create mode 100644 libopenage/renderer/renderer.cpp create mode 100644 libopenage/renderer/renderer.h create mode 100644 libopenage/renderer/shader.h create mode 100644 libopenage/renderer/target.h create mode 100644 libopenage/renderer/task.h create mode 100644 openage/renderer/CMakeLists.txt create mode 100644 openage/renderer/__init__.py create mode 100644 openage/renderer/renderer_cpp.pyx create mode 100644 openage/renderer/tests.py diff --git a/README.md b/README.md index f41566a946..1be1cfaacf 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Technology | Component **Python3** | Scripting, media conversion, in-game console, code generation **Cython** | Glue code **CMake** | Build system -**OpenGL2.1** | Rendering, shaders +**OpenGL3.3** | Rendering, shaders **SDL2** | Cross-platform Audio/Input/Window handling **Opus** | Audio codec **Humans** | Mixing together all of the above diff --git a/libopenage/renderer/context.cpp b/libopenage/renderer/context.cpp index bbd893ccf2..1114994a94 100644 --- a/libopenage/renderer/context.cpp +++ b/libopenage/renderer/context.cpp @@ -4,7 +4,7 @@ #include "../config.h" #include "../log/log.h" -#include "../util/error.h" +#include "../error/error.h" #if WITH_OPENGL #include "opengl/context.h" @@ -33,13 +33,13 @@ std::unique_ptr Context::generate(context_type t) { ctx_requested = context_type::opengl; } else { - throw util::Error(MSG(err) << "No render context available!"); + throw Error{MSG(err) << "No render context available!"}; } } if (ctx_requested == context_type::opengl) { if (not WITH_OPENGL) { - throw util::Error(MSG(err) << "OpenGL support not enabled!"); + throw Error{MSG(err) << "OpenGL support not enabled!"}; } #if WITH_OPENGL log::log(MSG(dbg) << "Using OpenGL context..."); @@ -48,7 +48,7 @@ std::unique_ptr Context::generate(context_type t) { } else if (ctx_requested == context_type::vulkan) { if (not WITH_VULKAN) { - throw util::Error(MSG(err) << "Vulkan support not enabled!"); + throw Error{MSG(err) << "Vulkan support not enabled!"}; } #if WITH_VULKAN log::log(MSG(dbg) << "Using Vulkan context..."); @@ -56,10 +56,11 @@ std::unique_ptr Context::generate(context_type t) { #endif } else { - throw util::Error(MSG(err) << "Unknown context type requested!"); + throw Error{MSG(err) << "Unknown context type requested!"}; } - throw util::Error(MSG(err) << "Context creation dead code reached! Veeery bad."); + throw Error{MSG(err) << "Context creation dead code reached! Veeery bad."}; + return nullptr; } }} // namespace openage::renderer diff --git a/libopenage/renderer/material.h b/libopenage/renderer/material.h new file mode 100644 index 0000000000..3ee5eabf90 --- /dev/null +++ b/libopenage/renderer/material.h @@ -0,0 +1,16 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_MATERIAL_H_ +#define OPENAGE_RENDERER_MATERIAL_H_ + +namespace openage { +namespace renderer { + + +class material { + Shader code; +}; + +}} // namespace openage::renderer + +#endif diff --git a/libopenage/renderer/object.h b/libopenage/renderer/object.h new file mode 100644 index 0000000000..1e3587d7e8 --- /dev/null +++ b/libopenage/renderer/object.h @@ -0,0 +1,24 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_OBJECT_H_ +#define OPENAGE_RENDERER_OBJECT_H_ + + +namespace openage { +namespace renderer { + + +class Object { +public: + Object(); + ~Object(); + +private: + std::string name; + + position -> Texture +}; +} +} + +#endif diff --git a/libopenage/renderer/opengl/CMakeLists.txt b/libopenage/renderer/opengl/CMakeLists.txt index 79a04cc645..869bd78260 100644 --- a/libopenage/renderer/opengl/CMakeLists.txt +++ b/libopenage/renderer/opengl/CMakeLists.txt @@ -1,3 +1,5 @@ add_sources(libopenage context.cpp + program.cpp + shader.cpp ) diff --git a/libopenage/renderer/opengl/context.cpp b/libopenage/renderer/opengl/context.cpp index 0da6fc1178..5a593afb84 100644 --- a/libopenage/renderer/opengl/context.cpp +++ b/libopenage/renderer/opengl/context.cpp @@ -9,15 +9,15 @@ #include #include "../../log/log.h" -#include "../../util/error.h" +#include "../../error/error.h" namespace openage { namespace renderer { namespace opengl { // TODO: get max available gl version -constexpr int opengl_version_major = 2; -constexpr int opengl_version_minor = 1; +constexpr int opengl_version_major = 3; +constexpr int opengl_version_minor = 3; Context::Context() {} Context::~Context() {} @@ -39,16 +39,18 @@ void Context::create(SDL_Window *window) { this->glcontext = SDL_GL_CreateContext(window); if (this->glcontext == nullptr) { - throw util::Error(MSG(err) << "Failed creating OpenGL context: " << SDL_GetError()); + throw Error(MSG(err) << "Failed creating OpenGL context: " << SDL_GetError()); } // check the OpenGL version, for shaders n stuff int epoxy_glv = opengl_version_major * 10 + opengl_version_minor; if (not epoxy_is_desktop_gl() or epoxy_gl_version() < epoxy_glv) { - throw util::Error(MSG(err) << "OpenGL " - << opengl_version_major << "." << opengl_version_minor - << " not available"); + throw Error(MSG(err) << "OpenGL " + << opengl_version_major << "." << opengl_version_minor + << " not available"); } + + log::log(MSG(info) << "Using OpenGL " << opengl_version_major << "." << opengl_version_minor); } void Context::setup() { @@ -59,14 +61,14 @@ void Context::setup() { glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); log::log(MSG(dbg) << "Maximum supported texture size: " << max_texture_size); if (max_texture_size < 1024) { - throw util::Error(MSG(err) << "Maximum supported texture size too small: " << max_texture_size); + throw Error(MSG(err) << "Maximum supported texture size too small: " << max_texture_size); } int max_texture_units; glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_texture_units); log::log(MSG(dbg) << "Maximum supported texture units: " << max_texture_units); if (max_texture_units < 2) { - throw util::Error(MSG(err) << "Your GPU has not enough texture units: " << max_texture_units); + throw Error(MSG(err) << "Your GPU has not enough texture units: " << max_texture_units); } // vsync on diff --git a/libopenage/renderer/opengl/program.cpp b/libopenage/renderer/opengl/program.cpp new file mode 100644 index 0000000000..0efd2a936a --- /dev/null +++ b/libopenage/renderer/opengl/program.cpp @@ -0,0 +1,177 @@ +// Copyright 2013-2015 the openage authors. See copying.md for legal info. + +#include "program.h" + +#include +#include +#include +#include + +#include "../../log/log.h" +#include "../../error/error.h" +#include "../../util/compiler.h" +#include "../../util/file.h" +#include "../../util/strings.h" + +namespace openage { +namespace renderer { +namespace opengl { + +Program::Program() + : + is_linked{false} { + + this->id = glCreateProgram(); +} + +Program::~Program() { + glDeleteProgram(this->id); +} + +void Program::attach_shader(const Shader &shader) { + if (unlikely(this->is_linked)) { + throw Error{MSG(err) << "can't attach shader to linked program", true}; + } + + this->shader_ids.push_back(shader.id); + glAttachShader(this->id, shader.id); +} + +void Program::link() { + if (unlikely(this->is_linked)) { + throw Error{MSG(err) << "can't relink a program", true}; + } + + // link shaders to a program + glLinkProgram(this->id); + this->check(GL_LINK_STATUS); + + // check if program is usable + glValidateProgram(this->id); + this->check(GL_VALIDATE_STATUS); + + this->is_linked = true; + this->post_link_hook(); + + for (auto &id : this->shader_ids) { + glDetachShader(this->id, id); + } +} + +void Program::check(GLenum what_to_check) { + GLint status; + glGetProgramiv(this->id, what_to_check, &status); + + if (status != GL_TRUE) { + GLint loglen; + glGetProgramiv(this->id, GL_INFO_LOG_LENGTH, &loglen); + + auto infolog = std::make_unique(loglen); + glGetProgramInfoLog(this->id, loglen, NULL, infolog.get()); + + const char *what_str; + switch(what_to_check) { + case GL_LINK_STATUS: + what_str = "linking"; + break; + case GL_VALIDATE_STATUS: + what_str = "validation"; + break; + case GL_COMPILE_STATUS: + what_str = "compiliation"; + break; + default: + what_str = ""; + break; + } + + throw Error{MSG(err) << "Program " << what_str << " failed:\n" << infolog, true}; + } +} + +void Program::use() { + if (unlikely(not this->is_linked)) { + throw Error{MSG(err) << "using none-linked program!", true}; + } + glUseProgram(this->id); +} + +void Program::stopusing() { + glUseProgram((GLuint) 0); +} + +void Program::check_is_linked(const char *info) { + // just throw up when we're not linked yet. + + if (unlikely(not this->is_linked)) { + throw Error{MSG(err) << info << " before program was linked!", true}; + } +} + +GLint Program::get_uniform_id(const char *name) { + this->check_is_linked("Uniform id requested"); + return glGetUniformLocation(this->id, name); +} + +GLint Program::get_uniformbuffer_id(const char *name) { + this->check_is_linked("Uniform buffer requested"); + return glGetUniformBlockIndex(this->id, name); +} + +GLint Program::get_attribute_id(const char *name) { + this->check_is_linked("Vertex attribute requested"); + + GLint aid = glGetAttribLocation(this->id, name); + + if (unlikely(aid == -1)) { + this->dump_active_attributes(); + throw Error{MSG(err) << "Attribute " << name + << " queried but not found or active" + << " (optimized out by the compiler?).", true}; + } + + return aid; +} + +void Program::set_attribute_id(const char *name, GLuint id) { + if (unlikely(this->is_linked)) { + // TODO: maybe enable overwriting, but after that relink the program + throw Error{MSG(err) + << "assigned attribute " << name << " = " + << id << " after program was linked!", true}; + } + else { + glBindAttribLocation(this->id, id, name); + } +} + +void Program::dump_active_attributes() { + auto msg = MSG(info); + msg << "Dumping shader program " << this->id << " active attribute list:"; + + GLint num_attribs; + glGetProgramiv(this->id, GL_ACTIVE_ATTRIBUTES, &num_attribs); + + GLint attrib_max_length; + glGetProgramiv(this->id, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &attrib_max_length); + + for (int i = 0; i < num_attribs; i++) { + GLsizei attrib_length; + GLint attrib_size; + GLenum attrib_type; + auto attrib_name = std::make_unique(attrib_max_length); + + glGetActiveAttrib(this->id, i, attrib_max_length, &attrib_length, &attrib_size, &attrib_type, attrib_name.get()); + + msg << "\n -> attribute " << attrib_name + << ": type=" << attrib_type << ", size=" << attrib_size + << ", id=" << this->get_attribute_id(attrib_name.get()); + } + + log::log(msg); +} + + +void Program::post_link_hook() {} + +}}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/program.h b/libopenage/renderer/opengl/program.h new file mode 100644 index 0000000000..7daecec8fc --- /dev/null +++ b/libopenage/renderer/opengl/program.h @@ -0,0 +1,79 @@ +// Copyright 2013-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_OPENGL_PROGRAM_H_ +#define OPENAGE_RENDERER_OPENGL_PROGRAM_H_ + +#include +#include + +#include "shader.h" + +namespace openage { +namespace renderer { +namespace opengl { + +class Program { +public: + GLuint id; + + Program(); + virtual ~Program(); + + /** + * add a shader to be included in this program. + */ + void attach_shader(const opengl::Shader &s); + + /** + * combine all attached shaders and link them + * to a usable program. + */ + void link(); + + /** + * activate the program on the gpu. + */ + void use(); + void stopusing(); + + GLint get_uniform_id(const char *name); + GLint get_uniformbuffer_id(const char *name); + GLint get_attribute_id(const char *name); + + void set_attribute_id(const char *name, GLuint id); + + void dump_active_attributes(); + +private: + /** + * True when this program was linked. + */ + bool is_linked; + + /** + * Shaders attached to this program. + */ + std::vector shader_ids; + + /** + * checks a given status for this program. + * + * @param what_to_check GL_LINK_STATUS GL_VALIDATE_STATUS GL_COMPILE_STATUS + */ + void check(GLenum what_to_check); + + /** + * check if this program was linked already. + */ + void check_is_linked(const char *info=""); + + + GLint get_info(GLenum pname); + char *get_log(); + void post_link_hook(); +}; + + +}}} // openage::renderer::opengl + +#endif diff --git a/libopenage/renderer/opengl/shader.cpp b/libopenage/renderer/opengl/shader.cpp new file mode 100644 index 0000000000..061927cef6 --- /dev/null +++ b/libopenage/renderer/opengl/shader.cpp @@ -0,0 +1,72 @@ +// Copyright 2013-2015 the openage authors. See copying.md for legal info. + +#include "shader.h" + +#include +#include +#include + +#include "../../error/error.h" +#include "../../log/log.h" +#include "../../util/file.h" +#include "../../util/strings.h" + +namespace openage { +namespace renderer { +namespace opengl { + +Shader::Shader(GLenum type, const char *source) { + // create shader + this->id = glCreateShader(type); + + // store type + this->type = type; + + // load shader source + glShaderSource(this->id, 1, &source, NULL); + + // compile shader source + glCompileShader(this->id); + + // check compiliation result + GLint status; + glGetShaderiv(this->id, GL_COMPILE_STATUS, &status); + + if (status != GL_TRUE) { + GLint loglen; + glGetShaderiv(this->id, GL_INFO_LOG_LENGTH, &loglen); + + auto infolog = std::make_unique(loglen); + glGetShaderInfoLog(this->id, loglen, NULL, infolog.get()); + + auto errmsg = MSG(err); + errmsg << "Failed to compile shader:\n" << infolog; + + glDeleteShader(this->id); + + throw Error{errmsg, true}; + } +} + + +Shader::~Shader() { + glDeleteShader(this->id); +} + + +VertexShader::VertexShader(const char *source) + : + Shader{GL_VERTEX_SHADER, source} {} + + +FragmentShader::FragmentShader(const char *source) + : + Shader{GL_FRAGMENT_SHADER, source} {} + + +GeometryShader::GeometryShader(const char *source) + : + Shader{GL_GEOMETRY_SHADER, source} {} + + +}}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/shader.h b/libopenage/renderer/opengl/shader.h new file mode 100644 index 0000000000..aebd33a399 --- /dev/null +++ b/libopenage/renderer/opengl/shader.h @@ -0,0 +1,68 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_OPENGL_SHADER_H_ +#define OPENAGE_RENDERER_OPENGL_SHADER_H_ + +#include + +namespace openage { +namespace renderer { +namespace opengl { + +class Shader { +public: + Shader(GLenum type, const char *source); + + Shader(const Shader ©) = delete; + Shader(Shader &&other) = delete; + Shader &operator=(const Shader ©) = delete; + Shader &operator=(Shader &&other) = delete; + + virtual ~Shader(); + + /** + * Return the shader type name. + */ + virtual const char *typestring() = 0; + + GLuint id; + GLenum type; +}; + + +class VertexShader : public Shader { +public: + VertexShader(const char *source); + virtual ~VertexShader() {}; + + virtual const char *typestring() { + return "vertex shader"; + } +}; + + +class FragmentShader : public Shader { +public: + FragmentShader(const char *source); + virtual ~FragmentShader() {}; + + virtual const char *typestring() { + return "fragment shader"; + } +}; + + +class GeometryShader : public Shader { +public: + GeometryShader(const char *source); + virtual ~GeometryShader() {}; + + virtual const char *typestring() { + return "geometry shader"; + } +}; + + +}}} // openage::renderer::opengl + +#endif diff --git a/libopenage/renderer/quad.h b/libopenage/renderer/quad.h new file mode 100644 index 0000000000..c4bb2f839c --- /dev/null +++ b/libopenage/renderer/quad.h @@ -0,0 +1,21 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_QUAD_H_ +#define OPENAGE_RENDERER_QUAD_H_ + +#include "../coord/window.h" + +namespace openage { +namespace renderer { + +/** + * struct to submit to the renderer + */ +struct Quad { + coord::window vertices[4]; +}; + + +}} // namespace openage::renderer + +#endif diff --git a/libopenage/renderer/renderer.cpp b/libopenage/renderer/renderer.cpp new file mode 100644 index 0000000000..3103bbb6f8 --- /dev/null +++ b/libopenage/renderer/renderer.cpp @@ -0,0 +1,35 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#include "renderer.h" + + +Renderer::Renderer() { + +} + +Renderer::~Renderer() { + +} + +void Renderer::render() { + sort instructions; + for (each instruction) { + apply necessary shader/texture changes; + render instruction; + } +} + +void add_instruction(task t) { + // insertion sort? + // place the new task according to its order? + this->tasks.push_back(t); +} + +void render() { + +} + +std::vector instructions_sorted() { + // stably sort by: + // layer, texture, shader +} diff --git a/libopenage/renderer/renderer.h b/libopenage/renderer/renderer.h new file mode 100644 index 0000000000..2d887110eb --- /dev/null +++ b/libopenage/renderer/renderer.h @@ -0,0 +1,44 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_RENDERER_H_ +#define OPENAGE_RENDERER_RENDERER_H_ + +#include "context.h" +#include "task.h" + +namespace openage { +namespace renderer { + +/** + * Main for collecting and rendering renderable things. + * + * All tasks are added and aggregated, + * when the render is invoked, the tasks are sorted and executed. + */ +class Renderer { +public: + Renderer(const Renderer &other) = delete; + Renderer(Renderer &&other) = delete; + Renderer &operator =(const Renderer &other) = delete; + Renderer &operator =(Renderer &&other) = delete; + + Renderer(); + ~Renderer(); + + void add_task(task task); + void render(); + + void register_shader(const Shader &shader); + +private: + std::vector instructions_sorted(); + + /** + * All tasks the renderer has to to display on the next drawout + */ + std::vector tasks; +}; + +}} // namespace openage::renderer + +#endif diff --git a/libopenage/renderer/shader.h b/libopenage/renderer/shader.h new file mode 100644 index 0000000000..f361bf9f02 --- /dev/null +++ b/libopenage/renderer/shader.h @@ -0,0 +1,29 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_SHADER_H_ +#define OPENAGE_RENDERER_SHADER_H_ + + +namespace openage { +namespace renderer { + +enum class shadertype { + fragment, + vertex, + geometry, +}; + + +class Shader { +public: + Shader(const std::string &path, shadertype type); + ~Shader(); + +private: + std::string path; + shadertype type; +}; + +}} // openage::renderer + +#endif diff --git a/libopenage/renderer/target.h b/libopenage/renderer/target.h new file mode 100644 index 0000000000..15553df732 --- /dev/null +++ b/libopenage/renderer/target.h @@ -0,0 +1,23 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_TARGET_H_ +#define OPENAGE_RENDERER_TARGET_H_ + +#include + +#include "renderer.h" + +namespace openage { +namespace renderer { + +/** + * Inherit from this to have methods that the renderer + * uses to fetch render tasks. + */ +class Target { + std::vector get_render_tasks(); +}; + +}} // namespace openage::renderer + +#endif diff --git a/libopenage/renderer/task.h b/libopenage/renderer/task.h new file mode 100644 index 0000000000..890163d197 --- /dev/null +++ b/libopenage/renderer/task.h @@ -0,0 +1,33 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_TASK_H_ +#define OPENAGE_RENDERER_TASK_H_ + +#include "material.h" + +namespace openage { +namespace renderer { + +/** + * render layer, their order is important. + * layers from bottom to top: later in enum = drawn later + */ +enum class layer { + terrain, + unit, + sky, + hud, +}; + +/** + * struct to submit to the renderer + */ +struct Task { + layer layer; + std::vector materials; +}; + + +}} // namespace openage::renderer + +#endif diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index d905ea9cde..0dafc17e89 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -6,6 +6,9 @@ #include #include "../log/log.h" +#include "../util/opengl.h" +#include "opengl/shader.h" +#include "opengl/program.h" #include "window.h" namespace openage { @@ -20,8 +23,7 @@ struct render_demo { }; -void create_window(const render_demo *actions) { - Window window{"openage renderer testing"}; +void render_test(Window &window, const render_demo *actions) { SDL_Event event; actions->setup(&window); @@ -68,33 +70,84 @@ void create_window(const render_demo *actions) { } -void renderer_demo(int argc, char **argv) { - int demo_id ; - if (argc == 2) { - demo_id = atoi(argv[1]); - log::log(MSG(info) << "running renderer demo " << demo_id << "..."); - } else { - demo_id = 0; - log::log(MSG(info) << "executing default renderer demo " << demo_id << "..."); - } +void renderer_demo() { + int demo_id = 0; + + Window window{"openage renderer testing"}; + + const char *vshader = "#version 330\n" + "layout(location = 0) in vec4 position;" + "smooth out vec4 fragpos;" + "void main() {" + "fragpos = position;" + "gl_Position = position;" + "}"; + + const char *fshader = "#version 330\n" + "out vec4 color;\n" + "smooth in vec4 fragpos;" + "void main() {" + "color = vec4(1.0f, fragpos.y, fragpos.x, 1.0f);" + "}"; + + + const float vpos[] = { + .0f, .0f, .0f, 1.0f, + 1.0f, .0f, .0f, 1.0f, + .0f, 1.0f, .0f, 1.0f, + + 1.0f, .0f, .0f, 1.0f, + .0f, 1.0f, .0f, 1.0f, + 1.0f, 1.0f, .0f, 1.0f, + }; + + GLuint vpos_buf, posattr_id; + + opengl::VertexShader vert{vshader}; + opengl::FragmentShader frag{fshader}; + + opengl::Program simplequad; + simplequad.attach_shader(vert); + simplequad.attach_shader(frag); + simplequad.link(); + + simplequad.dump_active_attributes(); + + posattr_id = simplequad.get_attribute_id("position"); + + GLuint vao; render_demo test0{ // init - [=](Window */*window*/) { + [&](Window */*window*/) { glEnable(GL_BLEND); + + glGenBuffers(1, &vpos_buf); + glBindBuffer(GL_ARRAY_BUFFER, vpos_buf); + // save vertex attributes to GPU: + glBufferData(GL_ARRAY_BUFFER, sizeof(vpos), vpos, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); // stores all the vertex attrib state. }, // frame - [] { - glClearColor(0.0, 0.0, 1.0, 1.0); - glClear(GL_COLOR_BUFFER_BIT); - - glColor3f(1, 0, 0); - glBegin(GL_QUADS); { - glVertex2f(.0f, .0f); - glVertex2f(1.0f, .0f); - glVertex2f(1.0f, 1.0f); - glVertex2f(.0f, 1.0f); - } glEnd(); + [&]() { + glClearColor(0.0, 0.0, 0.2, 1.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + simplequad.use(); + + glBindBuffer(GL_ARRAY_BUFFER, vpos_buf); + glEnableVertexAttribArray(posattr_id); + glVertexAttribPointer(posattr_id, 4, GL_FLOAT, GL_FALSE, 0, (void *)0); + glDrawArrays(GL_TRIANGLES, 0, 6); + + glDisableVertexAttribArray(posattr_id); + + simplequad.stopusing(); + + util::gl_check_error(); }, // resize [](const coord::window &new_size) { @@ -106,7 +159,7 @@ void renderer_demo(int argc, char **argv) { demos[0] = &test0; if (demos.find(demo_id) != demos.end()) { - create_window(demos[demo_id]); + render_test(window, demos[demo_id]); } else { log::log(MSG(err) << "unknown renderer demo " << demo_id << " requested."); diff --git a/libopenage/renderer/texture.cpp b/libopenage/renderer/texture.cpp index cb63b494e3..00d15b7788 100644 --- a/libopenage/renderer/texture.cpp +++ b/libopenage/renderer/texture.cpp @@ -2,7 +2,7 @@ #include "texture.h" -#include "../util/error.h" +#include "../error/error.h" namespace openage { namespace renderer { @@ -17,7 +17,7 @@ const gamedata::subtexture *Texture::get_subtexture(size_t subid) const { return &this->subtextures[subid]; } else { - throw util::Error(MSG(err) << "Unknown subtexture requested: " << subid); + throw Error(MSG(err) << "Unknown subtexture requested: " << subid); } } diff --git a/libopenage/renderer/texture.h b/libopenage/renderer/texture.h index 170d078b28..c5a8a35ec1 100644 --- a/libopenage/renderer/texture.h +++ b/libopenage/renderer/texture.h @@ -24,7 +24,7 @@ namespace renderer { */ class Texture { public: - virtual ~Texture(); + virtual ~Texture() {}; /** * Return the dimensions of the whole texture bitmap diff --git a/libopenage/renderer/window.cpp b/libopenage/renderer/window.cpp index 1fc7a8a367..25f994dc2e 100644 --- a/libopenage/renderer/window.cpp +++ b/libopenage/renderer/window.cpp @@ -7,7 +7,7 @@ #include #include "../log/log.h" -#include "../util/error.h" +#include "../error/error.h" namespace openage { @@ -20,7 +20,7 @@ Window::Window(const char *title) this->context = Context::generate(context_type::autodetect); if (SDL_Init(SDL_INIT_VIDEO) < 0) { - throw util::Error(MSG(err) << "SDL video initialization: " << SDL_GetError()); + throw Error{MSG(err) << "SDL video initialization: " << SDL_GetError()}; } else { log::log(MSG(info) << "Initialized SDL video subsystems."); } @@ -38,7 +38,7 @@ Window::Window(const char *title) ); if (this->window == nullptr) { - throw util::Error(MSG(err) << "Failed to create SDL window: " << SDL_GetError()); + throw Error{MSG(err) << "Failed to create SDL window: " << SDL_GetError()}; } else { log::log(MSG(info) << "Created SDL window."); } @@ -49,7 +49,7 @@ Window::Window(const char *title) int wanted_image_formats = IMG_INIT_PNG; int sdlimg_inited = IMG_Init(wanted_image_formats); if ((sdlimg_inited & wanted_image_formats) != wanted_image_formats) { - throw util::Error(MSG(err) << "Failed to init PNG support: " << IMG_GetError()); + throw Error{MSG(err) << "Failed to init PNG support: " << IMG_GetError()}; } this->context->setup(); diff --git a/libopenage/util/opengl.cpp b/libopenage/util/opengl.cpp index 1cc8b31ee3..eb8e2e6178 100644 --- a/libopenage/util/opengl.cpp +++ b/libopenage/util/opengl.cpp @@ -63,6 +63,7 @@ void gl_check_error() { // unknown error state errormsg = "unknown error"; } + throw Error(MSG(err) << "OpenGL error state after running draw method: " << glerrorstate << "\n" "\t" << errormsg << "\n" diff --git a/openage/CMakeLists.txt b/openage/CMakeLists.txt index 939fa7c297..5a31ccc943 100644 --- a/openage/CMakeLists.txt +++ b/openage/CMakeLists.txt @@ -21,4 +21,5 @@ add_subdirectory(game) add_subdirectory(log) add_subdirectory(nyan) add_subdirectory(util) +add_subdirectory(renderer) add_subdirectory(testing) diff --git a/openage/renderer/CMakeLists.txt b/openage/renderer/CMakeLists.txt new file mode 100644 index 0000000000..909a214c08 --- /dev/null +++ b/openage/renderer/CMakeLists.txt @@ -0,0 +1,8 @@ +add_cython_modules( + renderer_cpp.pyx +) + +add_py_modules( + __init__.py + tests.py +) diff --git a/openage/renderer/__init__.py b/openage/renderer/__init__.py new file mode 100644 index 0000000000..ce8a6e3ce2 --- /dev/null +++ b/openage/renderer/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2015-2015 the openage authors. See copying.md for legal info. + +""" +openage graphics renderer +""" diff --git a/openage/renderer/renderer_cpp.pyx b/openage/renderer/renderer_cpp.pyx new file mode 100644 index 0000000000..32ac9f55e3 --- /dev/null +++ b/openage/renderer/renderer_cpp.pyx @@ -0,0 +1 @@ +# Copyright 2015-2015 the openage authors. See copying.md for legal info. diff --git a/openage/renderer/tests.py b/openage/renderer/tests.py new file mode 100644 index 0000000000..dea70b3f44 --- /dev/null +++ b/openage/renderer/tests.py @@ -0,0 +1,5 @@ +# Copyright 2015-2015 the openage authors. See copying.md for legal info. + +""" +tests for the graphics renderer. +""" From d1aafcfddd21b52e7e48149532a4148db5b022d7 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Mon, 10 Aug 2015 06:06:38 +0200 Subject: [PATCH 08/32] renderer: texture and shader creation abstractions textures and shader can now be created context independently. --- libopenage/renderer/CMakeLists.txt | 3 + libopenage/renderer/context.cpp | 9 +- libopenage/renderer/context.h | 75 +++++++++- libopenage/renderer/material.h | 12 +- libopenage/renderer/opengl/CMakeLists.txt | 1 + libopenage/renderer/opengl/context.cpp | 49 ++++++- libopenage/renderer/opengl/context.h | 60 +++++++- libopenage/renderer/opengl/program.cpp | 37 +++-- libopenage/renderer/opengl/program.h | 64 +++++--- libopenage/renderer/opengl/shader.cpp | 78 ++++++++-- libopenage/renderer/opengl/shader.h | 42 ++++-- libopenage/renderer/opengl/texture.cpp | 60 ++++++++ libopenage/renderer/opengl/texture.h | 32 ++++ libopenage/renderer/renderer.cpp | 43 +++--- libopenage/renderer/renderer.h | 63 +++++++- libopenage/renderer/shader.cpp | 73 ++++++++++ libopenage/renderer/shader.h | 137 +++++++++++++++++- libopenage/renderer/shaders/simpletexture.cpp | 3 + libopenage/renderer/shaders/simpletexture.h | 7 + libopenage/renderer/task.cpp | 24 +++ libopenage/renderer/task.h | 26 +++- libopenage/renderer/tests.cpp | 68 +++++---- libopenage/renderer/texture.cpp | 91 +++++++++++- libopenage/renderer/texture.h | 97 ++++++++++--- libopenage/renderer/vulkan/context.h | 11 ++ libopenage/renderer/window.cpp | 8 +- libopenage/renderer/window.h | 28 +++- 27 files changed, 1040 insertions(+), 161 deletions(-) create mode 100644 libopenage/renderer/opengl/texture.cpp create mode 100644 libopenage/renderer/opengl/texture.h create mode 100644 libopenage/renderer/shader.cpp create mode 100644 libopenage/renderer/shaders/simpletexture.cpp create mode 100644 libopenage/renderer/shaders/simpletexture.h create mode 100644 libopenage/renderer/task.cpp diff --git a/libopenage/renderer/CMakeLists.txt b/libopenage/renderer/CMakeLists.txt index d670d58799..6fa32c4b6e 100644 --- a/libopenage/renderer/CMakeLists.txt +++ b/libopenage/renderer/CMakeLists.txt @@ -1,6 +1,9 @@ add_sources(libopenage color.cpp context.cpp + renderer.cpp + shader.cpp + task.cpp tests.cpp text.cpp texture.cpp diff --git a/libopenage/renderer/context.cpp b/libopenage/renderer/context.cpp index 1114994a94..a2c26f9295 100644 --- a/libopenage/renderer/context.cpp +++ b/libopenage/renderer/context.cpp @@ -18,14 +18,17 @@ namespace openage { namespace renderer { -Context::Context() {} -Context::~Context() {} +Context::Context(context_type type) + : + type{type} {} std::unique_ptr Context::generate(context_type t) { context_type ctx_requested = t; if (t == context_type::autodetect) { // priority: vulkan > opengl + // TODO: could use some boosting, that autodetection is kinda lame. + if (WITH_VULKAN) { ctx_requested = context_type::vulkan; } @@ -59,7 +62,7 @@ std::unique_ptr Context::generate(context_type t) { throw Error{MSG(err) << "Unknown context type requested!"}; } - throw Error{MSG(err) << "Context creation dead code reached! Veeery bad."}; + throw Error{MSG(err) << "No context was created! Veeery bad."}; return nullptr; } diff --git a/libopenage/renderer/context.h b/libopenage/renderer/context.h index 9c7e479dd1..9e97322df4 100644 --- a/libopenage/renderer/context.h +++ b/libopenage/renderer/context.h @@ -7,27 +7,94 @@ #include +#include "shader.h" +#include "texture.h" + namespace openage { namespace renderer { +/** + * Available context types. + * Specifies all available backends. + * autodetect: use vulkan > opengl. could use some tuning :D + */ enum class context_type { autodetect, - opengl, vulkan, + opengl, +}; + +/** + * Context features that can be enabled or disabled. + * Used in Context::set_feature + */ +enum class context_feature { + blending, //!< Alpha blending + depth_test, //!< z-buffering for fragment depth tests }; class Context { public: - Context(); - virtual ~Context(); + Context(context_type type); + virtual ~Context() = default; + /** + * Create a context according to the requested type. + */ static std::unique_ptr generate(context_type t); + /** + * Called before the drawing window is created. + */ virtual void prepare() = 0; - virtual uint32_t get_window_flags() = 0; + + /** + * Returns the SDL2 window flags for the requested context type. + */ + virtual uint32_t get_window_flags() const = 0; + + /** + * Actually creates the requested context for the given SDL window. + */ virtual void create(SDL_Window *window) = 0; + + /** + * Setup calls for the newly created context. + * After creation, the context may want to have some setup + * calls that configure functionality. + */ virtual void setup() = 0; + + /** + * Destroy the context to unregister it. + */ virtual void destroy() = 0; + + /** + * Enable or disable a given context feature. + * @param feature the feature to modify + * @param on whether to set this feature on or off. + */ + virtual void set_feature(context_feature feature, bool on) = 0; + + /** + * Register some texture data to the context. + * @returns the newly created Texture handle. + */ + virtual std::shared_ptr register_texture(const TextureData &data) = 0; + + /** + * Register some shader pipeline program to the context. + * @returns the newly created Program handle. + */ + virtual std::shared_ptr register_program(const ProgramSource &data) = 0; + + +protected: + /** + * Type of this context, namely the backend variant. + */ + context_type type; }; }} // namespace openage::renderer diff --git a/libopenage/renderer/material.h b/libopenage/renderer/material.h index 3ee5eabf90..ac7352b063 100644 --- a/libopenage/renderer/material.h +++ b/libopenage/renderer/material.h @@ -3,12 +3,18 @@ #ifndef OPENAGE_RENDERER_MATERIAL_H_ #define OPENAGE_RENDERER_MATERIAL_H_ +#include + +#include "texture.h" +#include "shader.h" + namespace openage { namespace renderer { - -class material { - Shader code; +class Material { +protected: + std::shared_ptr txt; + std::shared_ptr code; }; }} // namespace openage::renderer diff --git a/libopenage/renderer/opengl/CMakeLists.txt b/libopenage/renderer/opengl/CMakeLists.txt index 869bd78260..54291ddf27 100644 --- a/libopenage/renderer/opengl/CMakeLists.txt +++ b/libopenage/renderer/opengl/CMakeLists.txt @@ -2,4 +2,5 @@ add_sources(libopenage context.cpp program.cpp shader.cpp + texture.cpp ) diff --git a/libopenage/renderer/opengl/context.cpp b/libopenage/renderer/opengl/context.cpp index 5a593afb84..b0d2ba2bee 100644 --- a/libopenage/renderer/opengl/context.cpp +++ b/libopenage/renderer/opengl/context.cpp @@ -8,6 +8,8 @@ #include #include +#include "program.h" +#include "texture.h" #include "../../log/log.h" #include "../../error/error.h" @@ -19,10 +21,13 @@ namespace opengl { constexpr int opengl_version_major = 3; constexpr int opengl_version_minor = 3; -Context::Context() {} +Context::Context() + : + renderer::Context{context_type::opengl} {} + Context::~Context() {} -uint32_t Context::get_window_flags() { +uint32_t Context::get_window_flags() const { return SDL_WINDOW_OPENGL; } @@ -74,7 +79,7 @@ void Context::setup() { // vsync on SDL_GL_SetSwapInterval(1); - // TODO: move the following statements to some other place. + // TODO: transform the following to this::set_feature // enable alpha blending glEnable(GL_BLEND); @@ -90,6 +95,44 @@ void Context::destroy() { SDL_GL_DeleteContext(this->glcontext); } + +void Context::set_feature(context_feature feature, bool on) { + // what feature to change? this is the argument to glEnable and glDisable. + GLenum what; + + switch (feature) { + case context_feature::blending: + what = GL_BLEND; + break; + + case context_feature::depth_test: + what = GL_DEPTH_TEST; + break; + + default: + throw Error(MSG(err) << "unknown opengl context feature to set"); + } + + if (on) { + glEnable(what); + } else { + glDisable(what); + } +} + + +std::shared_ptr Context::register_texture(const TextureData &data) { + std::shared_ptr txt = std::make_shared(data); + return txt; +} + +std::shared_ptr Context::register_program(const ProgramSource &data) { + std::shared_ptr txt = std::make_shared(data); + return txt; +} + + + }}} // namespace openage::renderer::opengl #endif // if WITH_OPENGL diff --git a/libopenage/renderer/opengl/context.h b/libopenage/renderer/opengl/context.h index 416b426558..6dbec35238 100644 --- a/libopenage/renderer/opengl/context.h +++ b/libopenage/renderer/opengl/context.h @@ -11,6 +11,11 @@ namespace openage { namespace renderer { + +/** + * OpenGL specific renderer code. + * Is selected if the requested backend is OpenGL. + */ namespace opengl { class Context : public renderer::Context { @@ -18,13 +23,56 @@ class Context : public renderer::Context { Context(); ~Context(); - SDL_GLContext glcontext; + /** + * Called before the drawing window is created. + */ + virtual void prepare() override; + + /** + * Returns the SDL window flags for the opengl window. + */ + virtual uint32_t get_window_flags() const override; + + /** + * Actually creates the OpenGL context for the given SDL window. + */ + virtual void create(SDL_Window *window) override; + + /** + * Setup calls for the newly created context. + * + * Enables opengl functions like blending etc. + */ + virtual void setup() override; + + /** + * Deinitializes and unregisters the gl context from SDL2. + */ + virtual void destroy() override; - virtual void prepare(); - virtual uint32_t get_window_flags(); - virtual void create(SDL_Window *window); - virtual void setup(); - virtual void destroy(); + /** + * use glEnable and glDisable to toggle a given feature. + */ + virtual void set_feature(context_feature feature, bool on) override; + + /** + * Creates the opengl texture in this context. + * @returns a handle to it. + */ + virtual std::shared_ptr register_texture(const TextureData &data) override; + + /** + * Register a glsl shader pipeline program to the context. + * @returns a handle to the new program. + */ + virtual std::shared_ptr register_program(const ProgramSource &data) override; + + +protected: + /** + * SDL opengl context state. + */ + SDL_GLContext glcontext; }; }}} // namespace openage::renderer diff --git a/libopenage/renderer/opengl/program.cpp b/libopenage/renderer/opengl/program.cpp index 0efd2a936a..a20dabbbde 100644 --- a/libopenage/renderer/opengl/program.cpp +++ b/libopenage/renderer/opengl/program.cpp @@ -1,11 +1,9 @@ // Copyright 2013-2015 the openage authors. See copying.md for legal info. -#include "program.h" +#include "../../config.h" +#if WITH_OPENGL -#include -#include -#include -#include +#include "program.h" #include "../../log/log.h" #include "../../error/error.h" @@ -17,11 +15,28 @@ namespace openage { namespace renderer { namespace opengl { -Program::Program() +Program::Program(const ProgramSource &source) : is_linked{false} { + // tell OpenGL we wanna have a new pipeline program this->id = glCreateProgram(); + + if (unlikely(this->id == 0)) { + throw Error{MSG(err) << "no gl program created! wtf?", true}; + } + + // add all shader sources + for (auto &shadersource : source.get_shaders()) { + // create the shader from the source code + Shader shader{*shadersource}; + + // attach the shader to this program + this->attach_shader(shader); + } + + // link the sources together. + this->link(); } Program::~Program() { @@ -51,7 +66,6 @@ void Program::link() { this->check(GL_VALIDATE_STATUS); this->is_linked = true; - this->post_link_hook(); for (auto &id : this->shader_ids) { glDetachShader(this->id, id); @@ -124,7 +138,7 @@ GLint Program::get_attribute_id(const char *name) { GLint aid = glGetAttribLocation(this->id, name); if (unlikely(aid == -1)) { - this->dump_active_attributes(); + this->dump_attributes(); throw Error{MSG(err) << "Attribute " << name << " queried but not found or active" << " (optimized out by the compiler?).", true}; @@ -145,7 +159,7 @@ void Program::set_attribute_id(const char *name, GLuint id) { } } -void Program::dump_active_attributes() { +void Program::dump_attributes() { auto msg = MSG(info); msg << "Dumping shader program " << this->id << " active attribute list:"; @@ -171,7 +185,6 @@ void Program::dump_active_attributes() { log::log(msg); } - -void Program::post_link_hook() {} - }}} // openage::renderer::opengl + +#endif // WITH_OPENGL diff --git a/libopenage/renderer/opengl/program.h b/libopenage/renderer/opengl/program.h index 7daecec8fc..6ab6a711da 100644 --- a/libopenage/renderer/opengl/program.h +++ b/libopenage/renderer/opengl/program.h @@ -12,39 +12,69 @@ namespace openage { namespace renderer { namespace opengl { -class Program { +class Program : public renderer::Program { public: - GLuint id; + /** + * A program is created from shader sources. + */ + Program(const ProgramSource &source); - Program(); virtual ~Program(); + /** + * Activate the program on the GPU. + */ + virtual void use() override; + + /** + * Return the opengl handle id for a given pipeline uniform variable. + */ + GLint get_uniform_id(const char *name); + + /** + * Return the opengl handle id for a given uniform buffer object name. + */ + GLint get_uniformbuffer_id(const char *name); + + /** + * Return the opengl handle id for a given vertex attribute name. + */ + GLint get_attribute_id(const char *name); + + /** + * Set vertex attribute with given name to have a custom id. + */ + void set_attribute_id(const char *name, GLuint id); + + /** + * Query OpenGL which of the vertex attributes are actually active + * and haven't been optimized out by the compiler. + */ + virtual void dump_attributes() override; + +protected: /** * add a shader to be included in this program. */ void attach_shader(const opengl::Shader &s); /** - * combine all attached shaders and link them + * Combine all attached shaders and link them * to a usable program. */ void link(); /** - * activate the program on the gpu. + * Don't use the program any more. + * Return to static pipeline configuration. */ - void use(); void stopusing(); - GLint get_uniform_id(const char *name); - GLint get_uniformbuffer_id(const char *name); - GLint get_attribute_id(const char *name); - - void set_attribute_id(const char *name, GLuint id); - - void dump_active_attributes(); + /** + * The OpenGL handle id for this program. + */ + GLuint id; -private: /** * True when this program was linked. */ @@ -67,10 +97,10 @@ class Program { */ void check_is_linked(const char *info=""); - + /** + * Get information about the program. + */ GLint get_info(GLenum pname); - char *get_log(); - void post_link_hook(); }; diff --git a/libopenage/renderer/opengl/shader.cpp b/libopenage/renderer/opengl/shader.cpp index 061927cef6..446227e5d7 100644 --- a/libopenage/renderer/opengl/shader.cpp +++ b/libopenage/renderer/opengl/shader.cpp @@ -1,13 +1,13 @@ // Copyright 2013-2015 the openage authors. See copying.md for legal info. -#include "shader.h" +#include "../../config.h" +#if WITH_OPENGL -#include -#include -#include +#include "shader.h" #include "../../error/error.h" #include "../../log/log.h" +#include "../../util/compiler.h" #include "../../util/file.h" #include "../../util/strings.h" @@ -15,12 +15,52 @@ namespace openage { namespace renderer { namespace opengl { -Shader::Shader(GLenum type, const char *source) { - // create shader - this->id = glCreateShader(type); +Shader::Shader(const ShaderSource &source) { + // assign gl shader type + switch (source.get_type()) { + case shader_type::vertex: + this->type = GL_VERTEX_SHADER; + break; + + case shader_type::fragment: + this->type = GL_FRAGMENT_SHADER; + break; + + case shader_type::geometry: + this->type = GL_GEOMETRY_SHADER; + break; + + case shader_type::tesselation_control: + this->type = GL_TESS_CONTROL_SHADER; + break; + + case shader_type::tesselation_evaluation: + this->type = GL_TESS_EVALUATION_SHADER; + break; - // store type - this->type = type; + default: + throw Error{MSG(err) << "Unknown shader type requested."}; + } + + // create shader from source code! + this->create_from_source(source.get_source()); +} + +Shader::Shader(GLenum type, const char *source) + : + type{type} { + + // create the shader! + this->create_from_source(source); +} + +void Shader::create_from_source(const char *source) { + // allocate shader in opengl + this->id = glCreateShader(this->type); + + if (unlikely(this->id == 0)) { + throw Error{MSG(err) << "no gl shader created! wtf?", true}; + } // load shader source glShaderSource(this->id, 1, &source, NULL); @@ -48,11 +88,27 @@ Shader::Shader(GLenum type, const char *source) { } } - Shader::~Shader() { glDeleteShader(this->id); } +const char *Shader::typestring() { + switch (this->type) { + case GL_VERTEX_SHADER: + return "vertex shader"; + case GL_FRAGMENT_SHADER: + return "fragment shader"; + case GL_GEOMETRY_SHADER: + return "geometry shader"; + case GL_TESS_CONTROL_SHADER: + return "tesselation control shader"; + case GL_TESS_EVALUATION_SHADER: + return "tesselation evaluation shader"; + default: + return "unknown shader type"; + } +} + VertexShader::VertexShader(const char *source) : @@ -70,3 +126,5 @@ GeometryShader::GeometryShader(const char *source) }}} // openage::renderer::opengl + +#endif // WITH_OPENGL diff --git a/libopenage/renderer/opengl/shader.h b/libopenage/renderer/opengl/shader.h index aebd33a399..4d0195218d 100644 --- a/libopenage/renderer/opengl/shader.h +++ b/libopenage/renderer/opengl/shader.h @@ -5,17 +5,30 @@ #include +#include "../shader.h" + namespace openage { namespace renderer { namespace opengl { class Shader { public: + /** + * Create an OpenGL shader from a source code string. + */ Shader(GLenum type, const char *source); + /** + * Create an OpenGL shader from a ShaderSource object. + */ + Shader(const ShaderSource &source); + + // don't allow copying as the shader has a context-bound opengl handle id Shader(const Shader ©) = delete; - Shader(Shader &&other) = delete; Shader &operator=(const Shader ©) = delete; + + // moving could be enabled i guess, but beware the destructor call. + Shader(Shader &&other) = delete; Shader &operator=(Shader &&other) = delete; virtual ~Shader(); @@ -23,9 +36,22 @@ class Shader { /** * Return the shader type name. */ - virtual const char *typestring() = 0; + const char *typestring(); + /** + * OpenGL handle id. + */ GLuint id; + +protected: + /** + * actually create the shader. + */ + void create_from_source(const char *source); + + /** + * OpenGL shader type enum. + */ GLenum type; }; @@ -34,10 +60,6 @@ class VertexShader : public Shader { public: VertexShader(const char *source); virtual ~VertexShader() {}; - - virtual const char *typestring() { - return "vertex shader"; - } }; @@ -45,10 +67,6 @@ class FragmentShader : public Shader { public: FragmentShader(const char *source); virtual ~FragmentShader() {}; - - virtual const char *typestring() { - return "fragment shader"; - } }; @@ -56,10 +74,6 @@ class GeometryShader : public Shader { public: GeometryShader(const char *source); virtual ~GeometryShader() {}; - - virtual const char *typestring() { - return "geometry shader"; - } }; diff --git a/libopenage/renderer/opengl/texture.cpp b/libopenage/renderer/opengl/texture.cpp new file mode 100644 index 0000000000..14c9d81457 --- /dev/null +++ b/libopenage/renderer/opengl/texture.cpp @@ -0,0 +1,60 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#include "../../config.h" +#if WITH_OPENGL + +#include "texture.h" + +#include + +#include "../../error/error.h" + + +namespace openage { +namespace renderer { +namespace opengl { + + +Texture::Texture(const TextureData &txt) { + // generate opengl texture handle + glGenTextures(1, &this->id); + glBindTexture(GL_TEXTURE_2D, id); + + int input_format, output_format; + + // select pixel format + switch (txt.format) { + case texture_format::rgb: + input_format = GL_RGB8; + output_format = GL_RGB; + break; + case texture_format::rgba: + input_format = GL_RGBA8; + output_format = GL_RGBA; + break; + default: + throw Error{MSG(err) << "invalid texture format passed to OpenGL."}; + } + + // store raw pixels to gpu + glTexImage2D( + GL_TEXTURE_2D, 0, + input_format, txt.w, txt.h, 0, + output_format, GL_UNSIGNED_BYTE, txt.data.get() + ); + + // drawing settings + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glBindTexture(GL_TEXTURE_2D, 0); +} + +Texture::~Texture() { + glDeleteTextures(1, &this->id); +} + + +}}} // openage::renderer::opengl + +#endif diff --git a/libopenage/renderer/opengl/texture.h b/libopenage/renderer/opengl/texture.h new file mode 100644 index 0000000000..55e6eec46b --- /dev/null +++ b/libopenage/renderer/opengl/texture.h @@ -0,0 +1,32 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_OPENGL_TEXTURE_H_ +#define OPENAGE_RENDERER_OPENGL_TEXTURE_H_ + +#include + +#include "../texture.h" + +namespace openage { +namespace renderer { +namespace opengl { + +/** + * An OpenGL texture. + */ +class Texture : public renderer::Texture { +public: + Texture(const TextureData &data); + ~Texture(); + +protected: + /** + * OpenGL handle id. + */ + GLuint id; +}; + + +}}} // openage::renderer::opengl + +#endif diff --git a/libopenage/renderer/renderer.cpp b/libopenage/renderer/renderer.cpp index 3103bbb6f8..bb3d4d4728 100644 --- a/libopenage/renderer/renderer.cpp +++ b/libopenage/renderer/renderer.cpp @@ -2,34 +2,41 @@ #include "renderer.h" +namespace openage { +namespace renderer { -Renderer::Renderer() { +Renderer::Renderer(const std::shared_ptr &ctx) + : + context{ctx} {} -} +Renderer::~Renderer() {} -Renderer::~Renderer() { +TaskState Renderer::add_task(const Task &task) { + // sort by: + // layer, texture, shader + this->tasks.push(task); + TaskState ret; + return ret; } -void Renderer::render() { - sort instructions; - for (each instruction) { - apply necessary shader/texture changes; - render instruction; - } -} -void add_instruction(task t) { - // insertion sort? - // place the new task according to its order? - this->tasks.push_back(t); +std::shared_ptr Renderer::add_program(const ProgramSource &source) { + return this->context->register_program(source); } -void render() { +std::shared_ptr Renderer::add_texture(const TextureData &data) { + return this->context->register_texture(data); } -std::vector instructions_sorted() { - // stably sort by: - // layer, texture, shader + +void Renderer::render() const { + while (not this->tasks.empty()) { + // Task = this->tasks.pop(); + // apply necessary shader/texture changes; + // render instruction; + } } + +}} // openage::renderer diff --git a/libopenage/renderer/renderer.h b/libopenage/renderer/renderer.h index 2d887110eb..52ff5e9534 100644 --- a/libopenage/renderer/renderer.h +++ b/libopenage/renderer/renderer.h @@ -5,8 +5,21 @@ #include "context.h" #include "task.h" +#include "texture.h" + +#include "../datastructure/pairing_heap.h" namespace openage { + +/** + * Contains all components for the graphics renderer. + * Supports multiple backends and provides a uniform interface for + * interacting with the GPU and the render window. + * + * It gathers tasks to be rendered in a heap. + * This orders the instructions in an optimized order, + * so that the GPU performs as few context switches as possible. + */ namespace renderer { /** @@ -17,26 +30,60 @@ namespace renderer { */ class Renderer { public: + /** + * A renderer has to be created for a context. + */ + Renderer(const std::shared_ptr &ctx); + virtual ~Renderer(); + Renderer(const Renderer &other) = delete; Renderer(Renderer &&other) = delete; Renderer &operator =(const Renderer &other) = delete; Renderer &operator =(Renderer &&other) = delete; - Renderer(); - ~Renderer(); + /** + * Add a task to be rendered to the job list. + * This enqueues the task to be drawn in an optimized order. + * + * @returns the state handle for the added task. + */ + TaskState add_task(const Task &task); - void add_task(task task); - void render(); + /** + * Register a pipeline program to this renderer. + * + * Internally, this creates the internal backend-aware instance for + * the program. + * + * @returns a handle to the usable program for the developer. + */ + std::shared_ptr add_program(const ProgramSource &source); - void register_shader(const Shader &shader); + /** + * Register a texture to the renderer. + * Creates the context-aware handling structures. + * + * @returns a texture handle to be used in the code. + */ + std::shared_ptr add_texture(const TextureData &data); -private: - std::vector instructions_sorted(); + /** + * Finally, this is the actual drawing invocation. + * Pushes all the aggregated data to the GPU and renders it. + * When this is done, the front and backbuffer are flipped. + */ + void render() const; +private: /** * All tasks the renderer has to to display on the next drawout */ - std::vector tasks; + datastructure::PairingHeap tasks; + + /** + * The context to which the renderer is attached to. + */ + std::shared_ptr context; }; }} // namespace openage::renderer diff --git a/libopenage/renderer/shader.cpp b/libopenage/renderer/shader.cpp new file mode 100644 index 0000000000..503d7beb6c --- /dev/null +++ b/libopenage/renderer/shader.cpp @@ -0,0 +1,73 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +/** @file + * common code for all shaders. + */ + +#include "shader.h" + +#include "../error/error.h" + +namespace openage { +namespace renderer { + +ShaderSource::ShaderSource(shader_type type) + : + type{type} {} + +shader_type ShaderSource::get_type() const { + return this->type; +} + + +ShaderSourceFile::ShaderSourceFile(shader_type type, const std::string &path) + : + ShaderSource{type}, + path{path} {} + + +const char *ShaderSourceFile::get_source() const { + throw Error{MSG(err) << "not implemented yet"}; +} + + +ShaderSourceCode::ShaderSourceCode(shader_type type, const char *code) + : + ShaderSource{type}, + code{code} {} + + +ShaderSourceCode::ShaderSourceCode(shader_type type, const std::string &code) + : + ShaderSource{type}, + code{code} {} + + +const char *ShaderSourceCode::get_source() const { + return this->code.c_str(); +} + + +ProgramSource::ProgramSource() {} + + +ProgramSource::ProgramSource(const std::vector &shaders) { + for (auto &shader : shaders) { + this->attach_shader(*shader); + } +} + +const std::vector &ProgramSource::get_shaders() const { + return this->shaders; +} + + +void ProgramSource::attach_shader(const ShaderSource &shader) { + this->shaders.push_back(&shader); +} + + +Program::Program() {} + + +}} // openage::renderer diff --git a/libopenage/renderer/shader.h b/libopenage/renderer/shader.h index f361bf9f02..357cb121fe 100644 --- a/libopenage/renderer/shader.h +++ b/libopenage/renderer/shader.h @@ -3,25 +3,150 @@ #ifndef OPENAGE_RENDERER_SHADER_H_ #define OPENAGE_RENDERER_SHADER_H_ +/** @file + * gpu program creation classes reside in here. + */ + +#include +#include namespace openage { namespace renderer { -enum class shadertype { +/** + * Available shader types present in modern graphics pipelines. + */ +enum class shader_type { fragment, vertex, geometry, + tesselation_control, + tesselation_evaluation, +}; + + +/** + * Shader source code storage. + */ +class ShaderSource { +public: + ShaderSource(shader_type type); + virtual ~ShaderSource() {}; + + /** + * Return the shader source code. + */ + virtual const char *get_source() const = 0; + + /** + * Return the shader type. + */ + shader_type get_type() const; + +protected: + /** + * The shader's pipeline stage position. + */ + shader_type type; }; -class Shader { +/** + * Shader source code obtained from a file. + */ +class ShaderSourceFile : public ShaderSource { public: - Shader(const std::string &path, shadertype type); - ~Shader(); + ShaderSourceFile(shader_type type, const std::string &path); + virtual ~ShaderSourceFile() {}; -private: + /** + * Return the file contents: the shader code. + */ + const char *get_source() const override; + +protected: + /** + * The shader's filesystem location. + * TODO: replace by some path magic + */ std::string path; - shadertype type; +}; + + +/** + * Shader source code obtained directly as text. + */ +class ShaderSourceCode : public ShaderSource { +public: + ShaderSourceCode(shader_type type, const char *code); + ShaderSourceCode(shader_type type, const std::string &code); + + virtual ~ShaderSourceCode() {}; + + /** + * Return the stored source code. + */ + const char *get_source() const override; + +protected: + /** + * Stores the shader source code. + */ + std::string code; +}; + + +/** + * Create this to assemble shaders to a usable program. + * Register it at the renderer to use it. + * + * A program is a graphics card kernel, consisting of multiple + * shaders used for the rendering stages. + */ +class ProgramSource { +public: + ProgramSource(); + ProgramSource(const std::vector &shaders); + virtual ~ProgramSource() {}; + + /** + * Attach the given shader to this program. + */ + void attach_shader(const ShaderSource &shader); + + /** + * Return the list of all assigned shaders to this program. + */ + const std::vector &get_shaders() const; + +protected: + /** + * All attached shaders to this program. + */ + std::vector shaders; +}; + + +/** + * A usable handle, aquired by registering the sources of the program + * to the renderer and getting back an object of this class. + */ +class Program { +protected: + Program(); + +public: + virtual ~Program() {}; + + /** + * Use this program now on the GPU. + */ + virtual void use() = 0; + + /** + * Dump vertex attribute variables. + */ + virtual void dump_attributes() = 0; }; }} // openage::renderer diff --git a/libopenage/renderer/shaders/simpletexture.cpp b/libopenage/renderer/shaders/simpletexture.cpp new file mode 100644 index 0000000000..81dd7950d8 --- /dev/null +++ b/libopenage/renderer/shaders/simpletexture.cpp @@ -0,0 +1,3 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#include "simpletexture.h" diff --git a/libopenage/renderer/shaders/simpletexture.h b/libopenage/renderer/shaders/simpletexture.h new file mode 100644 index 0000000000..25429e0861 --- /dev/null +++ b/libopenage/renderer/shaders/simpletexture.h @@ -0,0 +1,7 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_SHADERS_SIMPLETEXTURE_H_ +#define OPENAGE_RENDERER_SHADERS_SIMPLETEXTURE_H_ + + +#endif diff --git a/libopenage/renderer/task.cpp b/libopenage/renderer/task.cpp new file mode 100644 index 0000000000..e9b3281cef --- /dev/null +++ b/libopenage/renderer/task.cpp @@ -0,0 +1,24 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +/** @file + * common code for all render tasks. + */ + +#include "task.h" + + +namespace openage { +namespace renderer { + + +bool Task::operator <(const Task &other) const { + if (this->position < other.position) { + return true; + } else { + return false; + } +} + +TaskState::TaskState() {} + +}} // openage::renderer diff --git a/libopenage/renderer/task.h b/libopenage/renderer/task.h index 890163d197..e142400e84 100644 --- a/libopenage/renderer/task.h +++ b/libopenage/renderer/task.h @@ -22,9 +22,31 @@ enum class layer { /** * struct to submit to the renderer */ -struct Task { - layer layer; +class Task { +public: + layer position; std::vector materials; + + bool operator <(const Task &other) const; +}; + + +/** + * corresponding state storage for a render task. + */ +class TaskState { +public: + TaskState(); + ~TaskState(); + + /** + * true if the assigned task has been rendered on screen. + */ + bool rendered; + +private: + Task *task; + class Renderer *renderer; }; diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index 0dafc17e89..f239558838 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -10,12 +10,16 @@ #include "opengl/shader.h" #include "opengl/program.h" #include "window.h" +#include "renderer.h" +#include "shader.h" namespace openage { namespace renderer { namespace tests { - +/** + * render demo function collection. + */ struct render_demo { std::function setup; std::function frame; @@ -74,22 +78,34 @@ void renderer_demo() { int demo_id = 0; Window window{"openage renderer testing"}; - - const char *vshader = "#version 330\n" - "layout(location = 0) in vec4 position;" - "smooth out vec4 fragpos;" - "void main() {" - "fragpos = position;" - "gl_Position = position;" - "}"; - - const char *fshader = "#version 330\n" - "out vec4 color;\n" - "smooth in vec4 fragpos;" - "void main() {" - "color = vec4(1.0f, fragpos.y, fragpos.x, 1.0f);" - "}"; - + Renderer renderer{window.get_context()}; + + ShaderSourceCode vshader_src( + shader_type::vertex, + "#version 330\n" + "layout(location = 0) in vec4 position;" + "smooth out vec4 fragpos;" + "void main() {" + "fragpos = position;" + "gl_Position = position;" + "}" + ); + + ShaderSourceCode fshader_src( + shader_type::fragment, + "#version 330\n" + "out vec4 color;\n" + "smooth in vec4 fragpos;" + "void main() {" + "color = vec4(1.0f, fragpos.y, fragpos.x, 1.0f);" + "}" + ); + + ProgramSource simplequad_src({&vshader_src, &fshader_src}); + + std::shared_ptr simplequad = renderer.add_program(simplequad_src); + + simplequad->dump_attributes(); const float vpos[] = { .0f, .0f, .0f, 1.0f, @@ -101,19 +117,9 @@ void renderer_demo() { 1.0f, 1.0f, .0f, 1.0f, }; - GLuint vpos_buf, posattr_id; + GLuint vpos_buf, posattr_id = 0; - opengl::VertexShader vert{vshader}; - opengl::FragmentShader frag{fshader}; - - opengl::Program simplequad; - simplequad.attach_shader(vert); - simplequad.attach_shader(frag); - simplequad.link(); - - simplequad.dump_active_attributes(); - - posattr_id = simplequad.get_attribute_id("position"); + //posattr_id = simplequad->get_attribute_id("position"); GLuint vao; @@ -136,7 +142,7 @@ void renderer_demo() { glClearColor(0.0, 0.0, 0.2, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - simplequad.use(); + simplequad->use(); glBindBuffer(GL_ARRAY_BUFFER, vpos_buf); glEnableVertexAttribArray(posattr_id); @@ -145,8 +151,6 @@ void renderer_demo() { glDisableVertexAttribArray(posattr_id); - simplequad.stopusing(); - util::gl_check_error(); }, // resize diff --git a/libopenage/renderer/texture.cpp b/libopenage/renderer/texture.cpp index 00d15b7788..253cd26e99 100644 --- a/libopenage/renderer/texture.cpp +++ b/libopenage/renderer/texture.cpp @@ -1,13 +1,16 @@ // Copyright 2015-2015 the openage authors. See copying.md for legal info. +#include + #include "texture.h" #include "../error/error.h" +#include "../util/file.h" namespace openage { namespace renderer { -std::tuple Texture::get_size() const { +const std::tuple Texture::get_size() const { return std::make_tuple(this->w, this->h); } @@ -40,4 +43,90 @@ const std::tuple Texture::get_subtexture_size(size_t subid) const { return std::make_tuple(subtex->w, subtex->h); } + +TextureData::TextureData(int width, int height, uint8_t *data) + : + format{texture_format::rgba}, + w{width}, + h{height} { + + // rgba has 4 components + size_t pixel_size = 4 * this->w * this->h; + + // copy pixel data from surface + this->data = std::make_unique(pixel_size); + memcpy(this->data.get(), data, pixel_size); +} + + +FileTextureData::FileTextureData(const std::string &filename, + bool use_metafile) + : + use_metafile{use_metafile}, + filename{filename} { + + SDL_Surface *surface = IMG_Load(this->filename.c_str()); + + if (!surface) { + throw Error(MSG(err) << + "Could not load texture from " << + filename << ": " << IMG_GetError()); + } else { + log::log(MSG(dbg) << "Texture has been loaded from " << filename); + } + + switch (surface->format->BytesPerPixel) { + case 3: // RGB 24 bit + this->format = texture_format::rgb; + break; + case 4: // RGBA 32 bit + this->format = texture_format::rgba; + break; + default: + throw Error( + MSG(err) << "Unknown texture bit depth for " + << filename << ": " << surface->format->BytesPerPixel + << " bytes per pixel"); + } + + this->w = surface->w; + this->h = surface->h; + + size_t pixel_size = surface->format->BytesPerPixel * surface->w * surface->h; + + // copy pixel data from surface + this->data = std::make_unique(pixel_size); + memcpy(this->data.get(), surface->pixels, pixel_size); + SDL_FreeSurface(surface); + + if (use_metafile) { + // change the suffix to .docx (lol) + std::string meta_filename = this->filename; + + size_t start_pos = meta_filename.find_last_of("."); + if (start_pos == std::string::npos) { + throw Error( + MSG(err) << "No filename extension found in: " + << meta_filename + ); + } + + // TODO: will probably bug if previous extension is longer than 5 + meta_filename.replace(start_pos, 5, ".docx"); + + log::log(MSG(info) << "Loading meta file: " << meta_filename); + + // get subtexture information by meta file exported by script + util::read_csv_file(meta_filename, this->subtextures); + } + else { + // we don't have a texture description file. + // use the whole image as one texture then. + gamedata::subtexture s{0, 0, this->w, this->h, this->w/2, this->h/2}; + + this->subtextures.push_back(s); + } +} + + }} // namespace openage::renderer diff --git a/libopenage/renderer/texture.h b/libopenage/renderer/texture.h index c5a8a35ec1..b617113323 100644 --- a/libopenage/renderer/texture.h +++ b/libopenage/renderer/texture.h @@ -3,6 +3,8 @@ #ifndef OPENAGE_RENDERER_TEXTURE_H_ #define OPENAGE_RENDERER_TEXTURE_H_ +#include + #include #include #include @@ -12,17 +14,27 @@ namespace openage { namespace renderer { + /** - * A texture for rendering graphically. - * - * You may believe it or not, but this class represents a single texture, - * which can be drawn on the screen. + * Texture format, used for setting pixel data size. + */ +enum class texture_format { + rgb, + rgba, +}; + + +/** + * A texture for rendering graphically in 3d space. + * Obtained by registering some texture data to the renderer. * * The class supports subtextures, so that one big texture ("texture atlas") * can contain several smaller images. These are the ones actually to be * rendered. */ class Texture { +protected: + Texture() = default; public: virtual ~Texture() {}; @@ -30,51 +42,92 @@ class Texture { * Return the dimensions of the whole texture bitmap * @returns tuple(width, height) */ - std::tuple get_size() const; + virtual const std::tuple get_size() const; /** * Get the subtexture coordinates by its idea. */ - const gamedata::subtexture *get_subtexture(size_t subid) const; + virtual const gamedata::subtexture *get_subtexture(size_t subid) const; /** * @return the number of available subtextures */ - int get_subtexture_count() const; + virtual int get_subtexture_count() const; /** * Fetch the size of the given subtexture. * @param subid: index of the requested subtexture */ - const std::tuple get_subtexture_size(size_t subid) const; + virtual const std::tuple get_subtexture_size(size_t subid) const; /** * get atlas subtexture coordinates. * * @returns left, right, top and bottom bounds as coordinates these pick * the requested area out of the big texture. returned as floats in - * range 0.0 to 1.0, relative to the whole bitmap size. + * range 0.0 to 1.0, relative to the whole surface size. */ - const std::tuple get_subtexture_coordinates(size_t subid) const; + virtual const std::tuple get_subtexture_coordinates(size_t subid) const; protected: - std::vectorsubtextures; + /** + * Atlas texture positions. + */ + std::vector subtextures; + + /** + * Width and height of this texture. + */ size_t w, h; }; -class RawTexture : public Texture { +/** + * Data for textures. Just used for transmitting the data to the GPU. + */ +class TextureData { +protected: + TextureData() = default; + public: /** * Create a texture from a rgba8 array. * It will have w * h * 4byte storage. * Each pixel has one byte, and r g b and alpha values. */ - RawTexture(int width, int height, std::unique_ptr data); + TextureData(int width, int height, uint8_t *data); + + virtual ~TextureData() = default; + + /** + * The data format of the texture. + */ + texture_format format; + + /** + * Width and height of this texture. + */ + int w, h; + + /** + * Raw texture pixel data. + * r g b a values, each 8 bit. + */ + std::unique_ptr data; + + /** + * The atlas texture positions. + */ + std::vector subtextures; }; -class FileTexture : public Texture { +/** + * Create a texture from an image file. + * + * Uses SDL Image internally. + */ +class FileTextureData : public TextureData { public: /** * Create a texture from a existing image file. @@ -82,12 +135,22 @@ class FileTexture : public Texture { * For supported image file types, see the SDL_Image initialization in * the engine. */ - FileTexture(const std::string &filename, bool use_metafile=false); -private: + FileTextureData(const std::string &filename, bool use_metafile=false); + ~FileTextureData() = default; + +protected: + /** + * Use the meta information file providing info about + * texture atlas positions. + */ bool use_metafile; + + /** + * File system path name of + */ std::string filename; }; -}} // namespace openage::renderer +}} // openage::renderer #endif diff --git a/libopenage/renderer/vulkan/context.h b/libopenage/renderer/vulkan/context.h index c2f6a16926..bf8c0cc4bd 100644 --- a/libopenage/renderer/vulkan/context.h +++ b/libopenage/renderer/vulkan/context.h @@ -11,8 +11,19 @@ namespace openage { namespace renderer { + +/** + * Vulkan specific renderer code. + * Is selected if the requested backend is Vulkan. + */ namespace vulkan { +/** + * Vulkan render context. + * + * No API has been published yet, but it's likely to be very similar + * to OpenGL and Mantle. + */ class Context : public renderer::Context { public: SDL_VulkanContext vkcontext; diff --git a/libopenage/renderer/window.cpp b/libopenage/renderer/window.cpp index 25f994dc2e..6afd1107a9 100644 --- a/libopenage/renderer/window.cpp +++ b/libopenage/renderer/window.cpp @@ -16,8 +16,10 @@ namespace renderer { Window::Window(const char *title) : size{800, 600} { + // TODO: ^ detect screen resolution and determine window size from it. - this->context = Context::generate(context_type::autodetect); + // TODO: make the type user-selectable + this->context = std::move(Context::generate(context_type::autodetect)); if (SDL_Init(SDL_INIT_VIDEO) < 0) { throw Error{MSG(err) << "SDL video initialization: " << SDL_GetError()}; @@ -84,4 +86,8 @@ void Window::set_size(const coord::window &new_size, bool update) { } } +std::shared_ptr Window::get_context() { + return this->context; +} + }} // namespace openage::renderer diff --git a/libopenage/renderer/window.h b/libopenage/renderer/window.h index 1c06987207..f7efa6d8ca 100644 --- a/libopenage/renderer/window.h +++ b/libopenage/renderer/window.h @@ -21,19 +21,39 @@ class Window { Window &operator =(const Window &other) = delete; Window &operator =(Window &&other) = delete; - coord::window get_size(); - void set_size(const coord::window &, bool update=false); - + /** + * Create a shiny window with the given title. + */ Window(const char *title); ~Window(); + /** + * @returns the window dimensions + */ + coord::window get_size(); + + /** + * Resize the drawing window. + */ + void set_size(const coord::window &new_size, bool update=false); + + /** + * Swaps the back and front framebuffers. + * Used to actually display the newly rendered frame. + */ void swap(); + /** + * Return the context created for this window. + */ + std::shared_ptr get_context(); + + private: coord::window size; SDL_Window *window; - std::unique_ptr context; + std::shared_ptr context; }; }} // namespace openage::renderer From cb9ff4670c004a3a628458d082d48c3dd08252ef Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Tue, 11 Aug 2015 18:48:03 +0200 Subject: [PATCH 09/32] util: modernized whole file reading --- libopenage/game_renderer.cpp | 77 +++++++++++++----------------------- libopenage/util/file.cpp | 70 +++++++------------------------- libopenage/util/file.h | 17 +++++++- 3 files changed, 57 insertions(+), 107 deletions(-) diff --git a/libopenage/game_renderer.cpp b/libopenage/game_renderer.cpp index e80870104e..681dbbc30a 100644 --- a/libopenage/game_renderer.cpp +++ b/libopenage/game_renderer.cpp @@ -38,7 +38,6 @@ RenderOptions::RenderOptions() GameRenderer::GameRenderer(openage::Engine *e) : - engine{e} { // set options structure @@ -56,7 +55,7 @@ GameRenderer::GameRenderer(openage::Engine *e) std::vector player_color_lines; util::read_csv_file(asset_dir.join("player_palette.docx"), player_color_lines); - GLfloat *playercolors = new GLfloat[player_color_lines.size() * 4]; + std::unique_ptr playercolors = std::make_unique(player_color_lines.size() * 4); for (size_t i = 0; i < player_color_lines.size(); i++) { auto line = &player_color_lines[i]; playercolors[i*4] = line->r / 255.0; @@ -68,50 +67,39 @@ GameRenderer::GameRenderer(openage::Engine *e) // shader initialisation // read shader source codes and create shader objects for wrapping them. const char *shader_header_code = "#version 120\n"; - char *equalsEpsilon_code; - util::read_whole_file(&equalsEpsilon_code, data_dir->join("shaders/equalsEpsilon.glsl")); + std::string equals_epsilon_code = util::read_whole_file(data_dir->join("shaders/equalsEpsilon.glsl")); - char *texture_vert_code; - util::read_whole_file(&texture_vert_code, data_dir->join("shaders/maptexture.vert.glsl")); - auto plaintexture_vert = new shader::Shader(GL_VERTEX_SHADER, { shader_header_code, texture_vert_code }); - delete[] texture_vert_code; + std::string texture_vert_code = util::read_whole_file(data_dir->join("shaders/maptexture.vert.glsl")); + shader::Shader plaintexture_vert{GL_VERTEX_SHADER, {shader_header_code, texture_vert_code.c_str()}}; - char *texture_frag_code; - util::read_whole_file(&texture_frag_code, data_dir->join("shaders/maptexture.frag.glsl")); - auto plaintexture_frag = new shader::Shader(GL_FRAGMENT_SHADER, { shader_header_code, texture_frag_code }); - delete[] texture_frag_code; + std::string texture_frag_code = util::read_whole_file(data_dir->join("shaders/maptexture.frag.glsl")); + shader::Shader plaintexture_frag{GL_FRAGMENT_SHADER, {shader_header_code, texture_frag_code.c_str()}}; - char *teamcolor_frag_code; - util::read_whole_file(&teamcolor_frag_code, data_dir->join("shaders/teamcolors.frag.glsl")); + std::string teamcolor_frag_code = util::read_whole_file(data_dir->join("shaders/teamcolors.frag.glsl")); std::stringstream ss; ss << player_color_lines.size(); - auto teamcolor_frag = new shader::Shader(GL_FRAGMENT_SHADER, { shader_header_code, ("#define NUM_OF_PLAYER_COLORS " + ss.str() + "\n").c_str(), equalsEpsilon_code, teamcolor_frag_code }); - delete[] teamcolor_frag_code; - - char *alphamask_vert_code; - util::read_whole_file(&alphamask_vert_code, data_dir->join("shaders/alphamask.vert.glsl")); - auto alphamask_vert = new shader::Shader(GL_VERTEX_SHADER, { shader_header_code, alphamask_vert_code }); - delete[] alphamask_vert_code; + shader::Shader teamcolor_frag{GL_FRAGMENT_SHADER, { + shader_header_code, + ("#define NUM_OF_PLAYER_COLORS " + ss.str() + "\n").c_str(), + equals_epsilon_code.c_str(), + teamcolor_frag_code.c_str() + } + }; - char *alphamask_frag_code; - util::read_whole_file(&alphamask_frag_code, data_dir->join("shaders/alphamask.frag.glsl")); - auto alphamask_frag = new shader::Shader(GL_FRAGMENT_SHADER, { shader_header_code, alphamask_frag_code }); - delete[] alphamask_frag_code; + std::string alphamask_vert_code = util::read_whole_file(data_dir->join("shaders/alphamask.vert.glsl")); + shader::Shader alphamask_vert{GL_VERTEX_SHADER, {shader_header_code, alphamask_vert_code.c_str()}}; - char *texturefont_vert_code; - util::read_whole_file(&texturefont_vert_code, data_dir->join("shaders/texturefont.vert.glsl")); - auto texturefont_vert = new shader::Shader(GL_VERTEX_SHADER, { shader_header_code, texturefont_vert_code }); - delete[] texturefont_vert_code; + std::string alphamask_frag_code = util::read_whole_file(data_dir->join("shaders/alphamask.frag.glsl")); + shader::Shader alphamask_frag{GL_FRAGMENT_SHADER, {shader_header_code, alphamask_frag_code.c_str()}}; - char *texturefont_frag_code; - util::read_whole_file(&texturefont_frag_code, data_dir->join("shaders/texturefont.frag.glsl")); - auto texturefont_frag = new shader::Shader(GL_FRAGMENT_SHADER, { shader_header_code, texturefont_frag_code }); - delete[] texturefont_frag_code; + std::string texturefont_vert_code = util::read_whole_file(data_dir->join("shaders/texturefont.vert.glsl")); + shader::Shader texturefont_vert{GL_VERTEX_SHADER, {shader_header_code, texturefont_vert_code.c_str()}}; - delete[] equalsEpsilon_code; + std::string texturefont_frag_code = util::read_whole_file(data_dir->join("shaders/texturefont.frag.glsl")); + shader::Shader texturefont_frag{GL_FRAGMENT_SHADER, {shader_header_code, texturefont_frag_code.c_str()}}; // create program for rendering simple textures - texture_shader::program = new shader::Program(plaintexture_vert, plaintexture_frag); + texture_shader::program = new shader::Program(&plaintexture_vert, &plaintexture_frag); texture_shader::program->link(); texture_shader::texture = texture_shader::program->get_uniform_id("texture"); texture_shader::tex_coord = texture_shader::program->get_attribute_id("tex_coordinates"); @@ -122,7 +110,7 @@ GameRenderer::GameRenderer(openage::Engine *e) // create program for tinting textures at alpha-marked pixels // with team colors - teamcolor_shader::program = new shader::Program(plaintexture_vert, teamcolor_frag); + teamcolor_shader::program = new shader::Program(&plaintexture_vert, &teamcolor_frag); teamcolor_shader::program->link(); teamcolor_shader::texture = teamcolor_shader::program->get_uniform_id("texture"); teamcolor_shader::tex_coord = teamcolor_shader::program->get_attribute_id("tex_coordinates"); @@ -133,13 +121,12 @@ GameRenderer::GameRenderer(openage::Engine *e) glUniform1i(teamcolor_shader::texture, 0); glUniform1f(teamcolor_shader::alpha_marker_var, 254.0/255.0); // fill the teamcolor shader's player color table: - glUniform4fv(teamcolor_shader::player_color_var, 64, playercolors); + glUniform4fv(teamcolor_shader::player_color_var, 64, playercolors.get()); teamcolor_shader::program->stopusing(); - delete[] playercolors; // create program for drawing textures that are alpha-masked before - alphamask_shader::program = new shader::Program(alphamask_vert, alphamask_frag); + alphamask_shader::program = new shader::Program(&alphamask_vert, &alphamask_frag); alphamask_shader::program->link(); alphamask_shader::base_coord = alphamask_shader::program->get_attribute_id("base_tex_coordinates"); alphamask_shader::mask_coord = alphamask_shader::program->get_attribute_id("mask_tex_coordinates"); @@ -151,9 +138,8 @@ GameRenderer::GameRenderer(openage::Engine *e) glUniform1i(alphamask_shader::mask_texture, 1); alphamask_shader::program->stopusing(); - // Create program for texture based font rendering - texturefont_shader::program = new shader::Program(texturefont_vert, texturefont_frag); + texturefont_shader::program = new shader::Program(&texturefont_vert, &texturefont_frag); texturefont_shader::program->link(); texturefont_shader::texture = texturefont_shader::program->get_uniform_id("texture"); texturefont_shader::color = texturefont_shader::program->get_uniform_id("color"); @@ -162,15 +148,6 @@ GameRenderer::GameRenderer(openage::Engine *e) glUniform1i(texturefont_shader::texture, 0); texturefont_shader::program->stopusing(); - // after linking, the shaders are no longer necessary - delete plaintexture_vert; - delete plaintexture_frag; - delete teamcolor_frag; - delete alphamask_vert; - delete alphamask_frag; - delete texturefont_vert; - delete texturefont_frag; - // Renderer keybinds // TODO: a renderer settings struct // would allow these to be put somewher better diff --git a/libopenage/util/file.cpp b/libopenage/util/file.cpp index 2e5f2a78bc..00f81dd6e7 100644 --- a/libopenage/util/file.cpp +++ b/libopenage/util/file.cpp @@ -2,12 +2,12 @@ #include "file.h" +#include +#include +#include #include #include -#include #include -#include -#include #include "../error/error.h" @@ -29,66 +29,26 @@ ssize_t file_size(Dir basedir, const std::string &fname) { } -ssize_t read_whole_file(char **result, const std::string &filename) { - return read_whole_file(result, filename.c_str()); -} - -ssize_t read_whole_file(char **result, const char *filename) { - - //get the file size - ssize_t content_length = file_size(filename); - - if (content_length < 0) { - throw Error(MSG(err) << "File nonexistant: " << filename); - } - - //open the file - FILE *filehandle = fopen(filename, "r"); - if (filehandle == NULL) { - throw Error(MSG(err) << "Failed to open file: " << filename); - } +std::string read_whole_file(const std::string &filename) { + std::ifstream file{filename}; - //allocate filesize + nullbyte - *result = new char[content_length + 1]; + std::string str{ + (std::istreambuf_iterator(file)), + std::istreambuf_iterator() + }; - //read the whole content - if (content_length != (ssize_t)fread(*result, 1, content_length, filehandle)) { - fclose(filehandle); - throw Error(MSG(err) << "Failed to read file: " << filename); - } else { - fclose(filehandle); - } - - //make sure 0-byte is at the end - (*result)[content_length] = '\0'; - - //return the file size - return content_length; + return str; } std::vector file_get_lines(const std::string &file_name) { - char *file_content; - ssize_t fsize = util::read_whole_file(&file_content, file_name); - - char *file_seeker = file_content; - char *current_line = file_content; - - auto result = std::vector{}; - - while ((size_t)file_seeker <= ((size_t)file_content + fsize) - && *file_seeker != '\0') { - - if (*file_seeker == '\n') { - *file_seeker = '\0'; - - result.push_back(std::string{current_line}); + std::string line; + std::vector result{}; + std::ifstream file{file_name}; - current_line = file_seeker + 1; - } - file_seeker += 1; + while (std::getline(file, line)) { + result.push_back(line); } - delete[] file_content; return result; } diff --git a/libopenage/util/file.h b/libopenage/util/file.h index e73927ab9d..2a1d10756f 100644 --- a/libopenage/util/file.h +++ b/libopenage/util/file.h @@ -16,11 +16,24 @@ namespace openage { namespace util { + +/** + * Return the size in bytes of a given file name. + */ ssize_t file_size(const std::string &filename); + + +/** + * Return the size in bytes of a filename relative to a directory. + */ ssize_t file_size(Dir basedir, const std::string &fname); -ssize_t read_whole_file(char **result, const char *filename); -ssize_t read_whole_file(char **result, const std::string &filename); + +/** + * Read the contents of a given filename. + */ +std::string read_whole_file(const std::string &filename); + /** * get the lines of a file. From 912580aa0893482b489dce9424a7b368b2cdbaea Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Tue, 11 Aug 2015 20:52:32 +0200 Subject: [PATCH 10/32] renderer: use the full framebuffer for the test quad --- libopenage/renderer/tests.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index f239558838..f830903a78 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -108,12 +108,12 @@ void renderer_demo() { simplequad->dump_attributes(); const float vpos[] = { - .0f, .0f, .0f, 1.0f, - 1.0f, .0f, .0f, 1.0f, - .0f, 1.0f, .0f, 1.0f, + -1.0f, -1.0f, .0f, 1.0f, + 1.0f, -1.0f, .0f, 1.0f, + -1.0f, 1.0f, .0f, 1.0f, - 1.0f, .0f, .0f, 1.0f, - .0f, 1.0f, .0f, 1.0f, + 1.0f, -1.0f, .0f, 1.0f, + -1.0f, 1.0f, .0f, 1.0f, 1.0f, 1.0f, .0f, 1.0f, }; From d94e1b348878d44e94a304725acaa80c18bd265b Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Tue, 11 Aug 2015 20:55:43 +0200 Subject: [PATCH 11/32] renderer: implemented loading shaders from file --- libopenage/renderer/shader.cpp | 27 ++++++++++++++++----------- libopenage/renderer/shader.h | 23 ++++++----------------- 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/libopenage/renderer/shader.cpp b/libopenage/renderer/shader.cpp index 503d7beb6c..d8fc8f67b7 100644 --- a/libopenage/renderer/shader.cpp +++ b/libopenage/renderer/shader.cpp @@ -7,6 +7,7 @@ #include "shader.h" #include "../error/error.h" +#include "../util/file.h" namespace openage { namespace renderer { @@ -15,36 +16,39 @@ ShaderSource::ShaderSource(shader_type type) : type{type} {} + shader_type ShaderSource::get_type() const { return this->type; } +const char *ShaderSource::get_source() const { + return this->code.c_str(); +} + + ShaderSourceFile::ShaderSourceFile(shader_type type, const std::string &path) : ShaderSource{type}, - path{path} {} - + path{path} { -const char *ShaderSourceFile::get_source() const { - throw Error{MSG(err) << "not implemented yet"}; + this->code = util::read_whole_file(this->path); } ShaderSourceCode::ShaderSourceCode(shader_type type, const char *code) : - ShaderSource{type}, - code{code} {} + ShaderSource{type} { + + this->code = code; +} ShaderSourceCode::ShaderSourceCode(shader_type type, const std::string &code) : - ShaderSource{type}, - code{code} {} + ShaderSource{type} { - -const char *ShaderSourceCode::get_source() const { - return this->code.c_str(); + this->code = code; } @@ -57,6 +61,7 @@ ProgramSource::ProgramSource(const std::vector &shaders) { } } + const std::vector &ProgramSource::get_shaders() const { return this->shaders; } diff --git a/libopenage/renderer/shader.h b/libopenage/renderer/shader.h index 357cb121fe..33d8e937ee 100644 --- a/libopenage/renderer/shader.h +++ b/libopenage/renderer/shader.h @@ -36,7 +36,7 @@ class ShaderSource { /** * Return the shader source code. */ - virtual const char *get_source() const = 0; + const char *get_source() const; /** * Return the shader type. @@ -48,6 +48,11 @@ class ShaderSource { * The shader's pipeline stage position. */ shader_type type; + + /** + * Stores the shader source code. + */ + std::string code; }; @@ -59,11 +64,6 @@ class ShaderSourceFile : public ShaderSource { ShaderSourceFile(shader_type type, const std::string &path); virtual ~ShaderSourceFile() {}; - /** - * Return the file contents: the shader code. - */ - const char *get_source() const override; - protected: /** * The shader's filesystem location. @@ -82,17 +82,6 @@ class ShaderSourceCode : public ShaderSource { ShaderSourceCode(shader_type type, const std::string &code); virtual ~ShaderSourceCode() {}; - - /** - * Return the stored source code. - */ - const char *get_source() const override; - -protected: - /** - * Stores the shader source code. - */ - std::string code; }; From 006f9fd54a205f46d1f459c622aff5fa85d0f1dd Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Wed, 12 Aug 2015 06:41:29 +0200 Subject: [PATCH 12/32] watch: created new file watching subsystem --- libopenage/CMakeLists.txt | 1 + libopenage/watch/CMakeLists.txt | 5 ++ libopenage/watch/linux.cpp | 123 ++++++++++++++++++++++++++++++++ libopenage/watch/linux.h | 86 ++++++++++++++++++++++ libopenage/watch/tests.cpp | 19 +++++ libopenage/watch/watch.cpp | 24 +++++++ libopenage/watch/watch.h | 94 ++++++++++++++++++++++++ openage/testing/testlist.py | 1 + 8 files changed, 353 insertions(+) create mode 100644 libopenage/watch/CMakeLists.txt create mode 100644 libopenage/watch/linux.cpp create mode 100644 libopenage/watch/linux.h create mode 100644 libopenage/watch/tests.cpp create mode 100644 libopenage/watch/watch.cpp create mode 100644 libopenage/watch/watch.h diff --git a/libopenage/CMakeLists.txt b/libopenage/CMakeLists.txt index 8569b10fca..887e5cfa3a 100644 --- a/libopenage/CMakeLists.txt +++ b/libopenage/CMakeLists.txt @@ -51,6 +51,7 @@ add_subdirectory("terrain") add_subdirectory("testing") add_subdirectory("unit") add_subdirectory("util") +add_subdirectory("watch") # run codegen, add files to executable codegen_run() diff --git a/libopenage/watch/CMakeLists.txt b/libopenage/watch/CMakeLists.txt new file mode 100644 index 0000000000..5dc8f99bf3 --- /dev/null +++ b/libopenage/watch/CMakeLists.txt @@ -0,0 +1,5 @@ +add_sources(libopenage + linux.cpp + tests.cpp + watch.cpp +) diff --git a/libopenage/watch/linux.cpp b/libopenage/watch/linux.cpp new file mode 100644 index 0000000000..7e2e9672f2 --- /dev/null +++ b/libopenage/watch/linux.cpp @@ -0,0 +1,123 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#include "linux.h" + +#if WITH_INOTIFY + +#include + +// inotify headers +#include +#include +#include + +#include "../util/file.h" + +namespace openage { +namespace watch { +namespace linux { + +size_t WatchManager::fd_count = 0; +size_t WatchManager::max_user_watches = 8192; +size_t WatchManager::max_user_instances = 128; + + +WatchManager::WatchManager() { + // initialize the first inotify instance + this->create_inotify_control(); +} + +int WatchManager::create_inotify_control() { + // TODO: figure out the actual number. + // other programs will be using inotify as well. + if (this->fd_count == this->max_user_instances) { + throw Error{MSG(err) << "number of inotify instances exhausted"}; + } + + int control_fd = inotify_init1(IN_NONBLOCK); + if (control_fd < 0) { + throw Error{MSG(err) << "Failed to initialize inotify!"}; + } + + this->watch_fds[control_fd]; + + this->fd_count += 1; + + return control_fd; +} + +// TODO: event type argument, don't hardcode CLOSE_WRITE. +void WatchManager::watch_file(const std::string &filename, const callback_t &callback) { + + // find the next free control fd slot. + int cfd = -1; + for (auto &it : this->watch_fds) { + if (this->watch_fds[it.first].size() < this->max_user_watches) { + cfd = it.first; + break; + } + } + + if (cfd < 0) { + cfd = this->create_inotify_control(); + } + + // create inotify update trigger for the requested file + int wd = inotify_add_watch(cfd, filename.c_str(), IN_CLOSE_WRITE); + if (wd < 0) { + throw Error{MSG(warn) << "Failed to add inotify watch for " << filename}; + } + this->watch_fds[cfd].emplace(std::make_pair(wd, file_watcher{callback, filename})); +} + + +void WatchManager::check_changes() { + char buf[event_queue_size * (sizeof(struct inotify_event) + NAME_MAX + 1)]; + ssize_t len; + + for (auto &wfd : this->watch_fds) { + int control_fd = wfd.first; + + while (true) { + // fetch all events, the kernel won't write "half" structs. + len = read(control_fd, buf, sizeof(buf)); + + if (len == -1 and errno == EAGAIN) { + // no events on this fd, nothing to do. + break; + } + else if (len == -1) { + throw Error{MSG(err) << "Failed to read inotify events!"}; + } + + // process fetched events. + // the kernel guarantees complete events in the buffer. + char *ptr = buf; + while (ptr < buf + len) { + auto *event = reinterpret_cast(ptr); + + // TODO: support more watch events + if (event->mask & IN_CLOSE_WRITE) { + // trigger the callback + file_watcher &w = this->watch_fds[control_fd][event->wd]; + w.callback(event_type::modify, w.filename); + } + + // move the buffer ptr to the next event. + ptr += sizeof(struct inotify_event) + event->len; + } + } + } +} + + +void WatchManager::fetch_limits() { + WatchManager::max_user_watches = std::stoi(util::read_whole_file("/proc/sys/fs/inotify/max_user_watches")); + + WatchManager::max_user_instances = std::stoi(util::read_whole_file("/proc/sys/fs/inotify/max_user_instances")); +} + + +}}} // openage::watch::linux + +#endif // WITH_INOTIFY diff --git a/libopenage/watch/linux.h b/libopenage/watch/linux.h new file mode 100644 index 0000000000..64ddd2216e --- /dev/null +++ b/libopenage/watch/linux.h @@ -0,0 +1,86 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_WATCH_LINUX_H_ +#define OPENAGE_WATCH_LINUX_H_ + +#include "../config.h" +#if WITH_INOTIFY + +#include +#include + +#include "watch.h" + + +namespace openage { +namespace watch { +namespace linux { + +/** + * Number of events to fetch at once at maximum. + */ +constexpr size_t event_queue_size = 4; + +/** + * Linux file watcher powered by inotify. + */ +class WatchManager : public watch::WatchManager { +public: + WatchManager(); + virtual ~WatchManager() = default; + + /** + * Query the inotify fds for events. + */ + void check_changes() override; + + /** + * Create a new watch in this manager. + */ + void watch_file(const std::string &filename, const callback_t &callback) override; + + +protected: + /** + * Create an inotify control fd. + */ + int create_inotify_control(); + + /** + * inotify control fd => watch handle fd => file watcher. + * We may have multiple fds open. + * The kernel returns the handle fd when events are triggered. + */ + std::unordered_map> watch_fds; + + /** + * The number of currently opened inotify fds + * + * Global for the program! + */ + static size_t fd_count; + + /** + * Number of watches per control fd. + * "/proc/sys/fs/inotify/max_user_watches" + */ + static size_t max_user_watches; + + /** + * Number of opened inotify control fds. + * "/proc/sys/fs/inotify/max_user_instances" + */ + static size_t max_user_instances; + + /** + * Update the maximum inotify event counters from the /proc filesystem. + */ + static void fetch_limits(); +}; + + + +}}} // namespace openage::watch::linux + +#endif // WITH_INOTIFY +#endif diff --git a/libopenage/watch/tests.cpp b/libopenage/watch/tests.cpp new file mode 100644 index 0000000000..b5b8dfb7cd --- /dev/null +++ b/libopenage/watch/tests.cpp @@ -0,0 +1,19 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#include "watch.h" + +#include + +namespace openage { +namespace watch { +namespace tests { + +void run() { + std::unique_ptr manager = WatchManager::create(); + + manager->watch_file("/etc/passwd", [](event_type, const std::string&) { } ); + + manager->check_changes(); +} + +}}} // openage::watch::tests diff --git a/libopenage/watch/watch.cpp b/libopenage/watch/watch.cpp new file mode 100644 index 0000000000..21172dfab3 --- /dev/null +++ b/libopenage/watch/watch.cpp @@ -0,0 +1,24 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#include "watch.h" + +#include "linux.h" + +namespace openage { +namespace watch { + +std::unique_ptr WatchManager::create() { +#if WITH_INOTIFY + return std::make_unique(); +#else + return std::make_unique(); +#endif +} + +void DummyWatchManager::check_changes() {} + +void DummyWatchManager::watch_file(const std::string &, const callback_t &) {} + + + +}} // openage::watch diff --git a/libopenage/watch/watch.h b/libopenage/watch/watch.h new file mode 100644 index 0000000000..86fafe4350 --- /dev/null +++ b/libopenage/watch/watch.h @@ -0,0 +1,94 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_WATCH_WATCH_H_ +#define OPENAGE_WATCH_WATCH_H_ + +#include +#include +#include + +namespace openage { + +/** + * Filesystem watching functions. + * Provides functionality for event based file monitoring. + */ +namespace watch { + +/** + * File watching event types. + */ +enum class event_type { + create, //!< new creation + modify, //!< content modification + remove, //!< removal of entry +}; + + +/** + * Function type for event callbacks. + * has to be a function accepting (eventtype, filename) + */ +using callback_t = std::function; + + +/** + * Handle for watching a single file. + */ +struct file_watcher { + const callback_t callback; + const std::string filename; +}; + + +/** + * File change and creation monitoring. + */ +class WatchManager { +protected: + WatchManager() = default; + +public: + virtual ~WatchManager() = default; + + /** + * Check if any of the watched files/directories + * have any events queued and trigger the appropriate callback. + */ + virtual void check_changes() = 0; + + /** + * Create a new watch in this manager. + */ + virtual void watch_file(const std::string &filename, const callback_t &callback) = 0; + + /** + * Create a watch manager. Selects the plattform-compatible backend. + */ + static std::unique_ptr create(); +}; + +/** + * dummy watch manager that is used when no watch functionality + * has been compiled in. + */ +class DummyWatchManager : public WatchManager { +public: + DummyWatchManager() = default; + virtual ~DummyWatchManager() = default; + + /** + * Hehe, does just nothing. + */ + void check_changes() override; + + /** + * Huehue, does nothing as well. + */ + void watch_file(const std::string &, const callback_t &) override; +}; + + +}} // namespace openage::watch + +#endif diff --git a/openage/testing/testlist.py b/openage/testing/testlist.py index 17b38ba03c..851b959511 100644 --- a/openage/testing/testlist.py +++ b/openage/testing/testlist.py @@ -67,6 +67,7 @@ def tests_cpp(): yield "openage::util::tests::matrix" yield "openage::util::tests::vector" yield "openage::input::tests::parse_event_string", "keybinds parsing" + yield "openage::watch::tests::run" def demos_cpp(): From f3b1edbef0b523d8f7c3c60408a24eb7b0c4b1a9 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Wed, 12 Aug 2015 07:23:49 +0200 Subject: [PATCH 13/32] assetmanager: use new file watching subsystem --- libopenage/assetmanager.cpp | 68 ++++++------------------------------- libopenage/assetmanager.h | 8 +++-- 2 files changed, 17 insertions(+), 59 deletions(-) diff --git a/libopenage/assetmanager.cpp b/libopenage/assetmanager.cpp index adc99e44cf..726392beb6 100644 --- a/libopenage/assetmanager.cpp +++ b/libopenage/assetmanager.cpp @@ -2,18 +2,12 @@ #include "assetmanager.h" -#if WITH_INOTIFY -#include -#include -#include /* for NAME_MAX */ -#endif - -#include "util/compiler.h" -#include "util/file.h" #include "error/error.h" #include "log/log.h" - #include "texture.h" +#include "util/compiler.h" +#include "util/file.h" +#include "watch/watch.h" namespace openage { @@ -23,13 +17,7 @@ AssetManager::AssetManager(qtsdl::GuiItemLink *gui_link) missing_tex{nullptr}, gui_link{gui_link} { -#if WITH_INOTIFY - // initialize the inotify instance - this->inotify_fd = inotify_init1(IN_NONBLOCK); - if (this->inotify_fd < 0) { - throw Error{MSG(err) << "Failed to initialize inotify!"}; - } -#endif + this->watch_manager = watch::WatchManager::create(); } util::Dir *AssetManager::get_data_dir() { @@ -70,14 +58,12 @@ std::shared_ptr AssetManager::load_texture(const std::string &name, boo // create the texture! tex = std::make_shared(filename, use_metafile); -#if WITH_INOTIFY - // create inotify update trigger for the requested file - int wd = inotify_add_watch(this->inotify_fd, filename.c_str(), IN_CLOSE_WRITE); - if (wd < 0) { - throw Error{MSG(warn) << "Failed to add inotify watch for " << filename}; - } - this->watch_fds[wd] = tex; -#endif + this->watch_manager->watch_file( + filename, + [=](watch::event_type, std::string) { + tex->reload(); + } + ); } // insert the texture into the map and return the texture. @@ -100,39 +86,7 @@ Texture *AssetManager::get_texture(const std::string &name, bool use_metafile) { } void AssetManager::check_updates() { -#if WITH_INOTIFY - // buffer for at least 4 inotify events - char buf[4 * (sizeof(struct inotify_event) + NAME_MAX + 1)]; - ssize_t len; - - while (true) { - // fetch all events, the kernel won't write "half" structs. - len = read(this->inotify_fd, buf, sizeof(buf)); - - if (len == -1 and errno == EAGAIN) { - // no events, nothing to do. - break; - } - else if (len == -1) { - throw Error{MSG(err) << "Failed to read inotify events!"}; - } - - // process fetched events, - // the kernel guarantees complete events in the buffer. - char *ptr = buf; - while (ptr < buf + len) { - struct inotify_event *event = (struct inotify_event *)ptr; - - if (event->mask & IN_CLOSE_WRITE) { - // TODO: this should invoke callback functions - this->watch_fds[event->wd]->reload(); - } - - // move the buffer ptr to the next event. - ptr += sizeof(struct inotify_event) + event->len; - } - } -#endif + this->watch_manager->check_changes(); } std::shared_ptr AssetManager::get_missing_tex() { diff --git a/libopenage/assetmanager.h b/libopenage/assetmanager.h index e72ebbe08c..ffe2b74f49 100644 --- a/libopenage/assetmanager.h +++ b/libopenage/assetmanager.h @@ -2,13 +2,12 @@ #pragma once -#include "config.h" - #include #include #include #include "util/dir.h" +#include "watch/watch.h" namespace qtsdl { class GuiItemLink; @@ -53,6 +52,11 @@ class AssetManager final { void check_updates(); protected: + /** + * File change monitoring and automatic reloading. + */ + std::unique_ptr watch_manager; + /** * Create an internal texture handle. */ From e30b151db89eaf95b3abe97136cdb26f249b3d32 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Thu, 13 Aug 2015 01:13:43 +0200 Subject: [PATCH 14/32] renderer: initially integrated into the engine used for screenshots and resizing currently --- libopenage/audio/dynamic_resource.cpp | 1 + libopenage/engine.cpp | 36 +++++++------- libopenage/engine.h | 47 ++++++++++++------ libopenage/gamestate/game_spec.cpp | 9 ++-- libopenage/handlers.h | 2 - libopenage/renderer/context.cpp | 7 +++ libopenage/renderer/context.h | 23 ++++++++- libopenage/renderer/opengl/context.cpp | 53 ++++++++++++++++++++ libopenage/renderer/opengl/context.h | 26 +++++++--- libopenage/renderer/renderer.cpp | 12 +++++ libopenage/renderer/renderer.h | 19 ++++++-- libopenage/renderer/tests.cpp | 4 +- libopenage/screenshot.cpp | 67 +++++--------------------- libopenage/screenshot.h | 31 +++++++----- 14 files changed, 219 insertions(+), 118 deletions(-) diff --git a/libopenage/audio/dynamic_resource.cpp b/libopenage/audio/dynamic_resource.cpp index f20db246f5..0e9ba3f781 100644 --- a/libopenage/audio/dynamic_resource.cpp +++ b/libopenage/audio/dynamic_resource.cpp @@ -4,6 +4,7 @@ #include "../engine.h" #include "../log/log.h" +#include "../job/job_manager.h" namespace openage { namespace audio { diff --git a/libopenage/engine.cpp b/libopenage/engine.cpp index 972660842f..7cc3194dea 100644 --- a/libopenage/engine.cpp +++ b/libopenage/engine.cpp @@ -18,16 +18,21 @@ #include "gamestate/game_main.h" #include "gamestate/generator.h" - +#include "job/job_manager.h" +#include "log/log.h" +#include "renderer/font/font.h" +#include "renderer/font/font_manager.h" +#include "renderer/renderer.h" +#include "renderer/text.h" +#include "renderer/window.h" +#include "screenshot.h" +#include "texture.h" #include "util/color.h" #include "util/fps.h" #include "util/opengl.h" #include "util/strings.h" #include "util/timer.h" -#include "renderer/text.h" -#include "renderer/font/font.h" -#include "renderer/font/font_manager.h" /** * stores all things that have to do with the game. @@ -106,7 +111,12 @@ Engine::Engine(util::Dir *data_dir, int32_t fps_limit, bool gl_debug, const char // register the engines input manager this->register_input_action(&this->input_manager); + // create the graphical display this->window = std::make_unique(windowtitle); + this->renderer = std::make_unique(this->window->get_context()); + + // renderer has to be notified of window size changes + this->register_resize_action(this->renderer.get()); // qml sources will be installed to the asset dir // otherwise assume that launched from the source dir @@ -126,7 +136,7 @@ Engine::Engine(util::Dir *data_dir, int32_t fps_limit, bool gl_debug, const char if (number_of_worker_threads <= 0) { number_of_worker_threads = 1; } - this->job_manager = new job::JobManager{number_of_worker_threads}; + this->job_manager = std::make_unique(number_of_worker_threads); // initialize audio auto devices = audio::AudioManager::get_devices(); @@ -144,7 +154,7 @@ Engine::Engine(util::Dir *data_dir, int32_t fps_limit, bool gl_debug, const char this->drawing_huds.value = !this->drawing_huds.value; }); global_input_context.bind(action.get("SCREENSHOT"), [this](const input::action_arg_t &) { - this->get_screenshot_manager().save_screenshot(); + this->get_screenshot_manager()->save_screenshot(); }); global_input_context.bind(action.get("TOGGLE_DEBUG_OVERLAY"), [this](const input::action_arg_t &) { this->drawing_debug_overlay.value = !this->drawing_debug_overlay.value; @@ -174,8 +184,6 @@ Engine::~Engine() { this->profiler.unregister_all(); this->gui.reset(); - - delete this->job_manager; } bool Engine::on_resize(coord::window new_size) { @@ -184,9 +192,6 @@ bool Engine::on_resize(coord::window new_size) { // update engine window size this->engine_coord_data->window_size = new_size; - // tell the screenshot manager about the new size - this->screenshot_manager.window_size = new_size; - // update camgame window position, set it to center. this->engine_coord_data->camgame_window = this->engine_coord_data->window_size / 2; @@ -197,9 +202,6 @@ bool Engine::on_resize(coord::window new_size) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); - // update OpenGL viewport: the renderin area - glViewport(0, 0, this->engine_coord_data->window_size.x, this->engine_coord_data->window_size.y); - // set orthographic projection: left, right, bottom, top, near_val, far_val glOrtho(0, this->engine_coord_data->window_size.x, 0, this->engine_coord_data->window_size.y, 9001, -1); @@ -432,15 +434,15 @@ GameMain *Engine::get_game() { } job::JobManager *Engine::get_job_manager() { - return this->job_manager; + return this->job_manager.get(); } audio::AudioManager &Engine::get_audio_manager() { return this->audio_manager; } -ScreenshotManager &Engine::get_screenshot_manager() { - return this->screenshot_manager; +ScreenshotManager *Engine::get_screenshot_manager() { + return this->screenshot_manager.get(); } input::ActionManager &Engine::get_action_manager() { diff --git a/libopenage/engine.h b/libopenage/engine.h index 5d5ac3c391..15f843dd0b 100644 --- a/libopenage/engine.h +++ b/libopenage/engine.h @@ -20,44 +20,53 @@ #include "cvar/cvar.h" #include "game_singletons_info.h" #include "handlers.h" -#include "options.h" +#include "input/action.h" // pxd: from libopenage.input.input_manager cimport InputManager #include "input/input_manager.h" -#include "input/action.h" #include "job/job_manager.h" +#include "options.h" +#include "renderer/text.h" #include "renderer/window.h" -#include "util/externalprofiler.h" +#include "screenshot.h" #include "util/dir.h" +#include "util/externalprofiler.h" #include "util/fps.h" #include "util/profiler.h" #include "unit/selection.h" #include "screenshot.h" + namespace openage { namespace gui { class GuiBasic; -} +class GuiItemLink; +} // openage::gui namespace renderer { - class Font; class FontManager; class TextRenderer; - +class Renderer; +class Window; } // openage::renderer +namespace job { +class JobManager; +} // openage::job + class DrawHandler; class TickHandler; class ResizeHandler; -class Generator; -class GameSpec; +class Font; class GameMain; -namespace gui { -class GuiItemLink; -} // openage::gui +class GameSpec; +class Generator; +class Player; +class ScreenshotManager; + struct coord_data { coord::window window_size{800, 600}; @@ -240,7 +249,7 @@ class Engine : public ResizeHandler, public options::OptionNode { /** * return this engine's screenshot manager. */ - ScreenshotManager &get_screenshot_manager(); + ScreenshotManager *get_screenshot_manager(); /** * return this engine's action manager. @@ -383,7 +392,7 @@ class Engine : public ResizeHandler, public options::OptionNode { /** * the engine's screenshot manager. */ - ScreenshotManager screenshot_manager; + std::unique_ptr screenshot_manager; /** * the engine's cvar manager. @@ -403,7 +412,7 @@ class Engine : public ResizeHandler, public options::OptionNode { /** * the engine's job manager, for asynchronous background task queuing. */ - job::JobManager *job_manager; + std::unique_ptr job_manager; /** * the engine's keybind manager. @@ -433,9 +442,9 @@ class Engine : public ResizeHandler, public options::OptionNode { std::unique_ptr gui; /* - * the engines profiler + * The renderer. Accepts all tasks to be drawn on screen. */ - util::Profiler profiler; + std::unique_ptr renderer; /** * The font manager to provide different sized and styled fonts. @@ -449,7 +458,13 @@ class Engine : public ResizeHandler, public options::OptionNode { public: EngineSignals gui_signals; + gui::GuiItemLink *gui_link; + + /** + * the engines profiler + */ + util::Profiler profiler; }; } // namespace openage diff --git a/libopenage/gamestate/game_spec.cpp b/libopenage/gamestate/game_spec.cpp index f4814b0203..07242a1dc5 100644 --- a/libopenage/gamestate/game_spec.cpp +++ b/libopenage/gamestate/game_spec.cpp @@ -1,15 +1,18 @@ // Copyright 2015-2016 the openage authors. See copying.md for legal info. +#include "game_spec.h" + +#include "../assetmanager.h" +#include "../engine.h" #include "../gamedata/blending_mode.gen.h" #include "../gamedata/string_resource.gen.h" #include "../gamedata/terrain.gen.h" +#include "../job/job_manager.h" #include "../unit/producer.h" #include "../util/strings.h" #include "../rng/global_rng.h" -#include "../assetmanager.h" -#include "../engine.h" #include "civilisation.h" -#include "game_spec.h" + #include diff --git a/libopenage/handlers.h b/libopenage/handlers.h index a6b481b4bf..876fe16acf 100644 --- a/libopenage/handlers.h +++ b/libopenage/handlers.h @@ -9,8 +9,6 @@ namespace openage { -class Engine; - /** * superclass for all possible drawing operations in the game. */ diff --git a/libopenage/renderer/context.cpp b/libopenage/renderer/context.cpp index a2c26f9295..56e1fea602 100644 --- a/libopenage/renderer/context.cpp +++ b/libopenage/renderer/context.cpp @@ -66,4 +66,11 @@ std::unique_ptr Context::generate(context_type t) { return nullptr; } + +void Context::resize(const coord::window &new_size) { + this->canvas_size = new_size; + this->resize_canvas(this->canvas_size); +} + + }} // namespace openage::renderer diff --git a/libopenage/renderer/context.h b/libopenage/renderer/context.h index 9e97322df4..8ec7e524b5 100644 --- a/libopenage/renderer/context.h +++ b/libopenage/renderer/context.h @@ -3,10 +3,10 @@ #ifndef OPENAGE_RENDERER_CONTEXT_H_ #define OPENAGE_RENDERER_CONTEXT_H_ -#include - #include +#include +#include "../coord/window.h" #include "shader.h" #include "texture.h" @@ -77,6 +77,11 @@ class Context { */ virtual void set_feature(context_feature feature, bool on) = 0; + /** + * Save this context's framebuffer as a png screenshot. + */ + virtual void screenshot(const std::string &filename) = 0; + /** * Register some texture data to the context. * @returns the newly created Texture handle. @@ -89,12 +94,26 @@ class Context { */ virtual std::shared_ptr register_program(const ProgramSource &data) = 0; + /** + * Resize the context because the surrounding window size was updated. + */ + void resize(const coord::window &new_size); protected: + /** + * Perform context-specific calls to resize the drawing canvas. + */ + virtual void resize_canvas(const coord::window &new_size) = 0; + /** * Type of this context, namely the backend variant. */ context_type type; + + /** + * Render surface size. + */ + coord::window canvas_size; }; }} // namespace openage::renderer diff --git a/libopenage/renderer/opengl/context.cpp b/libopenage/renderer/opengl/context.cpp index b0d2ba2bee..bf984d4946 100644 --- a/libopenage/renderer/opengl/context.cpp +++ b/libopenage/renderer/opengl/context.cpp @@ -7,6 +7,7 @@ #include #include +#include #include "program.h" #include "texture.h" @@ -121,6 +122,53 @@ void Context::set_feature(context_feature feature, bool on) { } +void Context::screenshot(const std::string &filename) { + log::log(MSG(info) << "Saving screenshot to " << filename); + + // surface color masks. + int32_t rmask, gmask, bmask, amask; + rmask = 0x000000FF; + gmask = 0x0000FF00; + bmask = 0x00FF0000; + amask = 0xFF000000; + + // create output surface which will be stored later. + SDL_Surface *screen = SDL_CreateRGBSurface( + SDL_SWSURFACE, + this->canvas_size.x, this->canvas_size.y, + 32, rmask, gmask, bmask, amask + ); + + size_t pxcount = screen->w * screen->h; + + auto pxdata = std::make_unique(pxcount); + + // copy the whole framebuffer to our local buffer. + glReadPixels(0, 0, + this->canvas_size.x, this->canvas_size.y, + GL_RGBA, GL_UNSIGNED_BYTE, pxdata.get()); + + uint32_t *surface_pxls = reinterpret_cast(screen->pixels); + + // now copy the raw data to the sdl surface. + // we need to invert all pixel rows, but leave column order the same. + for (ssize_t row = 0; row < screen->h; row++) { + ssize_t irow = screen->h - 1 - row; + for (ssize_t col = 0; col < screen->w; col++) { + uint32_t pxl = pxdata[irow * screen->w + col]; + + // TODO: store the alpha channels in the screenshot, + // is buggy at the moment.. + surface_pxls[row * screen->w + col] = pxl | 0xFF000000; + } + } + + // call sdl_image for saving the screenshot to png + IMG_SavePNG(screen, filename.c_str()); + SDL_FreeSurface(screen); +} + + std::shared_ptr Context::register_texture(const TextureData &data) { std::shared_ptr txt = std::make_shared(data); return txt; @@ -131,6 +179,11 @@ std::shared_ptr Context::register_program(const ProgramSource return txt; } +void Context::resize_canvas(const coord::window &new_size) { + log::log(MSG(dbg) << "opengl viewport resize to " << new_size.x << "x" << new_size.y); + + glViewport(0, 0, new_size.x, new_size.y); +} }}} // namespace openage::renderer::opengl diff --git a/libopenage/renderer/opengl/context.h b/libopenage/renderer/opengl/context.h index 6dbec35238..77b37099a0 100644 --- a/libopenage/renderer/opengl/context.h +++ b/libopenage/renderer/opengl/context.h @@ -26,49 +26,59 @@ class Context : public renderer::Context { /** * Called before the drawing window is created. */ - virtual void prepare() override; + void prepare() override; /** * Returns the SDL window flags for the opengl window. */ - virtual uint32_t get_window_flags() const override; + uint32_t get_window_flags() const override; /** * Actually creates the OpenGL context for the given SDL window. */ - virtual void create(SDL_Window *window) override; + void create(SDL_Window *window) override; /** * Setup calls for the newly created context. * * Enables opengl functions like blending etc. */ - virtual void setup() override; + void setup() override; /** * Deinitializes and unregisters the gl context from SDL2. */ - virtual void destroy() override; + void destroy() override; /** * use glEnable and glDisable to toggle a given feature. */ - virtual void set_feature(context_feature feature, bool on) override; + void set_feature(context_feature feature, bool on) override; + + /** + * Read the opengl framebuffer and dump it to a png file. + */ + void screenshot(const std::string &filename) override; /** * Creates the opengl texture in this context. * @returns a handle to it. */ - virtual std::shared_ptr register_texture(const TextureData &data) override; + std::shared_ptr register_texture(const TextureData &data) override; /** * Register a glsl shader pipeline program to the context. * @returns a handle to the new program. */ - virtual std::shared_ptr register_program(const ProgramSource &data) override; + std::shared_ptr register_program(const ProgramSource &data) override; protected: + /** + * Resize the opengl viewport. + */ + void resize_canvas(const coord::window &new_size) override; + /** * SDL opengl context state. */ diff --git a/libopenage/renderer/renderer.cpp b/libopenage/renderer/renderer.cpp index bb3d4d4728..0c602d345d 100644 --- a/libopenage/renderer/renderer.cpp +++ b/libopenage/renderer/renderer.cpp @@ -2,6 +2,8 @@ #include "renderer.h" +#include "context.h" + namespace openage { namespace renderer { @@ -31,6 +33,16 @@ std::shared_ptr Renderer::add_texture(const TextureData &data) { } +void Renderer::screenshot(const std::string &filename) { + this->context->screenshot(filename); +} + +bool Renderer::on_resize(coord::window new_size) { + this->context->resize(new_size); + return true; +} + + void Renderer::render() const { while (not this->tasks.empty()) { // Task = this->tasks.pop(); diff --git a/libopenage/renderer/renderer.h b/libopenage/renderer/renderer.h index 52ff5e9534..c4f5027e0a 100644 --- a/libopenage/renderer/renderer.h +++ b/libopenage/renderer/renderer.h @@ -3,11 +3,11 @@ #ifndef OPENAGE_RENDERER_RENDERER_H_ #define OPENAGE_RENDERER_RENDERER_H_ -#include "context.h" #include "task.h" #include "texture.h" #include "../datastructure/pairing_heap.h" +#include "../handlers.h" namespace openage { @@ -22,13 +22,15 @@ namespace openage { */ namespace renderer { +class Context; + /** * Main for collecting and rendering renderable things. * * All tasks are added and aggregated, * when the render is invoked, the tasks are sorted and executed. */ -class Renderer { +class Renderer : public ResizeHandler { public: /** * A renderer has to be created for a context. @@ -67,6 +69,17 @@ class Renderer { */ std::shared_ptr add_texture(const TextureData &data); + /** + * Take a screenshot of the current framebuffer. + * Save it as png to the given filename. + */ + void screenshot(const std::string &filename); + + /** + * Resize the renderer because the surrounding window size was updated. + */ + bool on_resize(coord::window new_size) override; + /** * Finally, this is the actual drawing invocation. * Pushes all the aggregated data to the GPU and renders it. @@ -74,7 +87,7 @@ class Renderer { */ void render() const; -private: +protected: /** * All tasks the renderer has to to display on the next drawout */ diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index f830903a78..97f95ea5f3 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -154,8 +154,8 @@ void renderer_demo() { util::gl_check_error(); }, // resize - [](const coord::window &new_size) { - glViewport(0, 0, new_size.x, new_size.y); + [&](const coord::window &new_size) { + renderer.on_resize(new_size); } }; diff --git a/libopenage/screenshot.cpp b/libopenage/screenshot.cpp index e24a229db0..207bcd9834 100644 --- a/libopenage/screenshot.cpp +++ b/libopenage/screenshot.cpp @@ -1,34 +1,29 @@ // Copyright 2014-2016 the openage authors. See copying.md for legal info. -#include "screenshot.h" -#include "util/strings.h" - -#include -#include #include -#include - -#include +#include #include "coord/window.h" #include "log/log.h" #include "log/message.h" #include +#include "screenshot.h" +#include "util/strings.h" +#include "renderer/renderer.h" namespace openage { -ScreenshotManager::ScreenshotManager() +ScreenshotManager::ScreenshotManager(renderer::Renderer *renderer) : - count{0} { -} + count{0}, + renderer{renderer} {} ScreenshotManager::~ScreenshotManager() {} std::string ScreenshotManager::gen_next_filename() { - std::time_t t = std::time(NULL); if (t == this->last_time) { @@ -38,9 +33,11 @@ std::string ScreenshotManager::gen_next_filename() { this->last_time = t; } - // these two values (32) *must* be the same for safety reasons - char timestamp[32]; - std::strftime(timestamp, 32, "%Y-%m-%d_%H-%M-%S", std::localtime(&t)); + constexpr const size_t timestamp_size = 32; + + char timestamp[timestamp_size]; + std::strftime(timestamp, timestamp_size, + "%Y-%m-%d_%H-%M-%S", std::localtime(&t)); return util::sformat("/tmp/openage_%s_%02d.png", timestamp, this->count); } @@ -49,45 +46,7 @@ std::string ScreenshotManager::gen_next_filename() { void ScreenshotManager::save_screenshot() { std::string filename = this->gen_next_filename(); - log::log(MSG(info) << "Saving screenshot to " << filename); - - int32_t rmask, gmask, bmask, amask; - rmask = 0x000000FF; - gmask = 0x0000FF00; - bmask = 0x00FF0000; - amask = 0xFF000000; - - SDL_Surface *screen = SDL_CreateRGBSurface( - SDL_SWSURFACE, - this->window_size.x, - this->window_size.y, - 32, - rmask, gmask, bmask, amask); - - size_t pxcount = screen->w * screen->h; - - auto pxdata = std::make_unique(pxcount); - - glReadPixels(0, 0, - this->window_size.x, this->window_size.y, - GL_RGBA, GL_UNSIGNED_BYTE, pxdata.get()); - - uint32_t *surface_pxls = (uint32_t *)screen->pixels; - - // we need to invert all pixel rows, but leave column order the same. - for (ssize_t row = 0; row < screen->h; row++) { - ssize_t irow = screen->h - 1 - row; - for (ssize_t col = 0; col < screen->w; col++) { - uint32_t pxl = pxdata[irow * screen->w + col]; - - // TODO: store the alpha channels in the screenshot, is buggy at the moment.. - surface_pxls[row * screen->w + col] = pxl | 0xFF000000; - } - } - - // call sdl_image for saving the screenshot to png - IMG_SavePNG(screen, filename.c_str()); - SDL_FreeSurface(screen); + this->renderer->screenshot(filename); } diff --git a/libopenage/screenshot.h b/libopenage/screenshot.h index 5fc1f6faf2..e177ef0731 100644 --- a/libopenage/screenshot.h +++ b/libopenage/screenshot.h @@ -5,34 +5,43 @@ #include #include -#include #include "coord/window.h" namespace openage { +namespace renderer { +class Renderer; +} class ScreenshotManager { public: - ScreenshotManager(); + ScreenshotManager(renderer::Renderer *renderer); ~ScreenshotManager(); - /** to be called to save a screenshot */ + /** + * To be called to save a screenshot. + */ void save_screenshot(); - /** size of the game window, in coord_sdl */ - coord::window window_size; - - private: - - /** to be called to get the next screenshot filename into the array */ + /** + * To be called to get the next screenshot filename into the array. + */ std::string gen_next_filename(); - /** contains the number to be in the next screenshot filename */ + /** + * Contains the number to be in the next screenshot filename. + */ unsigned count; - /** contains the last time when a screenshot was taken */ + /** + * Contains the last time when a screenshot was taken. + */ std::time_t last_time; + /** + * The renderer where to take screenshots from. + */ + class renderer::Renderer *renderer; }; } // openage From bc19ac924c5a1cc78061bb021089930286e07c02 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Thu, 13 Aug 2015 03:13:44 +0200 Subject: [PATCH 15/32] renderer: renamed pipeline Program to RawProgram the name will soon be used for wrapping shader variables --- libopenage/renderer/context.h | 4 ++-- libopenage/renderer/material.h | 2 +- libopenage/renderer/opengl/context.cpp | 8 ++++---- libopenage/renderer/opengl/context.h | 4 ++-- libopenage/renderer/opengl/program.h | 2 +- libopenage/renderer/renderer.cpp | 4 ++-- libopenage/renderer/renderer.h | 4 ++-- libopenage/renderer/shader.cpp | 2 +- libopenage/renderer/shader.h | 6 +++--- libopenage/renderer/tests.cpp | 2 +- 10 files changed, 19 insertions(+), 19 deletions(-) diff --git a/libopenage/renderer/context.h b/libopenage/renderer/context.h index 8ec7e524b5..afbbc18c5e 100644 --- a/libopenage/renderer/context.h +++ b/libopenage/renderer/context.h @@ -86,13 +86,13 @@ class Context { * Register some texture data to the context. * @returns the newly created Texture handle. */ - virtual std::shared_ptr register_texture(const TextureData &data) = 0; + virtual std::unique_ptr register_texture(const TextureData &data) = 0; /** * Register some shader pipeline program to the context. * @returns the newly created Program handle. */ - virtual std::shared_ptr register_program(const ProgramSource &data) = 0; + virtual std::unique_ptr register_program(const ProgramSource &data) = 0; /** * Resize the context because the surrounding window size was updated. diff --git a/libopenage/renderer/material.h b/libopenage/renderer/material.h index ac7352b063..f85eb9296f 100644 --- a/libopenage/renderer/material.h +++ b/libopenage/renderer/material.h @@ -14,7 +14,7 @@ namespace renderer { class Material { protected: std::shared_ptr txt; - std::shared_ptr code; + std::shared_ptr code; }; }} // namespace openage::renderer diff --git a/libopenage/renderer/opengl/context.cpp b/libopenage/renderer/opengl/context.cpp index bf984d4946..c2e176a99d 100644 --- a/libopenage/renderer/opengl/context.cpp +++ b/libopenage/renderer/opengl/context.cpp @@ -169,13 +169,13 @@ void Context::screenshot(const std::string &filename) { } -std::shared_ptr Context::register_texture(const TextureData &data) { - std::shared_ptr txt = std::make_shared(data); +std::unique_ptr Context::register_texture(const TextureData &data) { + std::unique_ptr txt = std::make_unique(data); return txt; } -std::shared_ptr Context::register_program(const ProgramSource &data) { - std::shared_ptr txt = std::make_shared(data); +std::unique_ptr Context::register_program(const ProgramSource &data) { + std::unique_ptr txt = std::make_unique(data); return txt; } diff --git a/libopenage/renderer/opengl/context.h b/libopenage/renderer/opengl/context.h index 77b37099a0..c34455c581 100644 --- a/libopenage/renderer/opengl/context.h +++ b/libopenage/renderer/opengl/context.h @@ -64,13 +64,13 @@ class Context : public renderer::Context { * Creates the opengl texture in this context. * @returns a handle to it. */ - std::shared_ptr register_texture(const TextureData &data) override; + std::unique_ptr register_texture(const TextureData &data) override; /** * Register a glsl shader pipeline program to the context. * @returns a handle to the new program. */ - std::shared_ptr register_program(const ProgramSource &data) override; + std::unique_ptr register_program(const ProgramSource &data) override; protected: diff --git a/libopenage/renderer/opengl/program.h b/libopenage/renderer/opengl/program.h index 6ab6a711da..64848176fa 100644 --- a/libopenage/renderer/opengl/program.h +++ b/libopenage/renderer/opengl/program.h @@ -12,7 +12,7 @@ namespace openage { namespace renderer { namespace opengl { -class Program : public renderer::Program { +class Program : public RawProgram { public: /** * A program is created from shader sources. diff --git a/libopenage/renderer/renderer.cpp b/libopenage/renderer/renderer.cpp index 0c602d345d..623175a3e8 100644 --- a/libopenage/renderer/renderer.cpp +++ b/libopenage/renderer/renderer.cpp @@ -23,12 +23,12 @@ TaskState Renderer::add_task(const Task &task) { } -std::shared_ptr Renderer::add_program(const ProgramSource &source) { +std::unique_ptr Renderer::add_program(const ProgramSource &source) { return this->context->register_program(source); } -std::shared_ptr Renderer::add_texture(const TextureData &data) { +std::unique_ptr Renderer::add_texture(const TextureData &data) { return this->context->register_texture(data); } diff --git a/libopenage/renderer/renderer.h b/libopenage/renderer/renderer.h index c4f5027e0a..1d0543be9d 100644 --- a/libopenage/renderer/renderer.h +++ b/libopenage/renderer/renderer.h @@ -59,7 +59,7 @@ class Renderer : public ResizeHandler { * * @returns a handle to the usable program for the developer. */ - std::shared_ptr add_program(const ProgramSource &source); + std::unique_ptr add_program(const ProgramSource &source); /** * Register a texture to the renderer. @@ -67,7 +67,7 @@ class Renderer : public ResizeHandler { * * @returns a texture handle to be used in the code. */ - std::shared_ptr add_texture(const TextureData &data); + std::unique_ptr add_texture(const TextureData &data); /** * Take a screenshot of the current framebuffer. diff --git a/libopenage/renderer/shader.cpp b/libopenage/renderer/shader.cpp index d8fc8f67b7..4c72a79997 100644 --- a/libopenage/renderer/shader.cpp +++ b/libopenage/renderer/shader.cpp @@ -72,7 +72,7 @@ void ProgramSource::attach_shader(const ShaderSource &shader) { } -Program::Program() {} +RawProgram::RawProgram() {} }} // openage::renderer diff --git a/libopenage/renderer/shader.h b/libopenage/renderer/shader.h index 33d8e937ee..cfa8be6a08 100644 --- a/libopenage/renderer/shader.h +++ b/libopenage/renderer/shader.h @@ -120,12 +120,12 @@ class ProgramSource { * A usable handle, aquired by registering the sources of the program * to the renderer and getting back an object of this class. */ -class Program { +class RawProgram { protected: - Program(); + RawProgram(); public: - virtual ~Program() {}; + virtual ~RawProgram() {}; /** * Use this program now on the GPU. diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index 97f95ea5f3..b9c9fe62de 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -103,7 +103,7 @@ void renderer_demo() { ProgramSource simplequad_src({&vshader_src, &fshader_src}); - std::shared_ptr simplequad = renderer.add_program(simplequad_src); + std::unique_ptr simplequad = renderer.add_program(simplequad_src); simplequad->dump_attributes(); From d34034851006cc83e48fc9ec071ae4ba40aede95 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Thu, 13 Aug 2015 20:48:09 +0200 Subject: [PATCH 16/32] renderer: moved program api contents to separate files --- libopenage/renderer/CMakeLists.txt | 1 + libopenage/renderer/context.h | 8 ++- libopenage/renderer/material.h | 2 +- libopenage/renderer/opengl/program.h | 1 + libopenage/renderer/program.cpp | 35 ++++++++++ libopenage/renderer/program.h | 95 ++++++++++++++++++++++++++++ libopenage/renderer/renderer.h | 9 ++- libopenage/renderer/shader.cpp | 24 ------- libopenage/renderer/shader.h | 54 ---------------- 9 files changed, 145 insertions(+), 84 deletions(-) create mode 100644 libopenage/renderer/program.cpp create mode 100644 libopenage/renderer/program.h diff --git a/libopenage/renderer/CMakeLists.txt b/libopenage/renderer/CMakeLists.txt index 6fa32c4b6e..f67123100e 100644 --- a/libopenage/renderer/CMakeLists.txt +++ b/libopenage/renderer/CMakeLists.txt @@ -1,6 +1,7 @@ add_sources(libopenage color.cpp context.cpp + program.cpp renderer.cpp shader.cpp task.cpp diff --git a/libopenage/renderer/context.h b/libopenage/renderer/context.h index afbbc18c5e..d165b627d3 100644 --- a/libopenage/renderer/context.h +++ b/libopenage/renderer/context.h @@ -7,12 +7,16 @@ #include #include "../coord/window.h" -#include "shader.h" -#include "texture.h" namespace openage { namespace renderer { +class ProgramSource; +class RawProgram; +class Texture; +class TextureData; + + /** * Available context types. * Specifies all available backends. diff --git a/libopenage/renderer/material.h b/libopenage/renderer/material.h index f85eb9296f..ad50787816 100644 --- a/libopenage/renderer/material.h +++ b/libopenage/renderer/material.h @@ -6,7 +6,7 @@ #include #include "texture.h" -#include "shader.h" +#include "program.h" namespace openage { namespace renderer { diff --git a/libopenage/renderer/opengl/program.h b/libopenage/renderer/opengl/program.h index 64848176fa..e5572648ab 100644 --- a/libopenage/renderer/opengl/program.h +++ b/libopenage/renderer/opengl/program.h @@ -6,6 +6,7 @@ #include #include +#include "../program.h" #include "shader.h" namespace openage { diff --git a/libopenage/renderer/program.cpp b/libopenage/renderer/program.cpp new file mode 100644 index 0000000000..eee1830627 --- /dev/null +++ b/libopenage/renderer/program.cpp @@ -0,0 +1,35 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +/** @file + * common code for all pipeline programs. + */ + +#include "program.h" + +namespace openage { +namespace renderer { + +ProgramSource::ProgramSource() {} + + +ProgramSource::ProgramSource(const std::vector &shaders) { + for (auto &shader : shaders) { + this->attach_shader(*shader); + } +} + + +const std::vector &ProgramSource::get_shaders() const { + return this->shaders; +} + + +void ProgramSource::attach_shader(const ShaderSource &shader) { + this->shaders.push_back(&shader); +} + + +RawProgram::RawProgram() {} + + +}} // openage::renderer diff --git a/libopenage/renderer/program.h b/libopenage/renderer/program.h new file mode 100644 index 0000000000..4921860e3b --- /dev/null +++ b/libopenage/renderer/program.h @@ -0,0 +1,95 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_PROGRAM_H_ +#define OPENAGE_RENDERER_PROGRAM_H_ + +#include +#include + +namespace openage { +namespace renderer { + +class ShaderSource; + +/** + * Create this to assemble shaders to a usable program. + * Register it at the renderer to use it. + * + * A program is a graphics card kernel, consisting of multiple + * shaders used for the rendering stages. + */ +class ProgramSource { +public: + ProgramSource(); + ProgramSource(const std::vector &shaders); + virtual ~ProgramSource() {}; + + /** + * Attach the given shader to this program. + */ + void attach_shader(const ShaderSource &shader); + + /** + * Return the list of all assigned shaders to this program. + */ + const std::vector &get_shaders() const; + +protected: + /** + * All attached shaders to this program. + */ + std::vector shaders; +}; + + +/** + * A usable handle, aquired by registering the sources of the program + * to the renderer and getting back an object of this class. + */ +class RawProgram { +protected: + RawProgram(); + +public: + virtual ~RawProgram() {}; + + /** + * Use this program now on the GPU. + */ + virtual void use() = 0; + + /** + * Dump vertex attribute variables. + */ + virtual void dump_attributes() = 0; +}; + + +/** + * Inherit from this class to create statically known program properties. + * + * Add members of ProgramVariable to the inherited class + * to make pipeline variables available to the outside. + */ +class Program { +public: + Program(std::unique_ptr prg); + virtual ~Program(); + +protected: + /** + * Add the given program variable to the list of maintained + * pipeline attributes. + */ + void add_var(const ProgramVariable &var); + + /** + * The pipeline program associated with this property definition class. + */ + std::unique_ptr raw_program; +}; + + +}} // openage::renderer + +#endif diff --git a/libopenage/renderer/renderer.h b/libopenage/renderer/renderer.h index 1d0543be9d..72ab04a383 100644 --- a/libopenage/renderer/renderer.h +++ b/libopenage/renderer/renderer.h @@ -3,11 +3,9 @@ #ifndef OPENAGE_RENDERER_RENDERER_H_ #define OPENAGE_RENDERER_RENDERER_H_ -#include "task.h" -#include "texture.h" - #include "../datastructure/pairing_heap.h" #include "../handlers.h" +#include "task.h" namespace openage { @@ -23,6 +21,11 @@ namespace openage { namespace renderer { class Context; +class ProgramSource; +class RawProgram; +class Texture; +class TextureData; + /** * Main for collecting and rendering renderable things. diff --git a/libopenage/renderer/shader.cpp b/libopenage/renderer/shader.cpp index 4c72a79997..385b159be0 100644 --- a/libopenage/renderer/shader.cpp +++ b/libopenage/renderer/shader.cpp @@ -51,28 +51,4 @@ ShaderSourceCode::ShaderSourceCode(shader_type type, const std::string &code) this->code = code; } - -ProgramSource::ProgramSource() {} - - -ProgramSource::ProgramSource(const std::vector &shaders) { - for (auto &shader : shaders) { - this->attach_shader(*shader); - } -} - - -const std::vector &ProgramSource::get_shaders() const { - return this->shaders; -} - - -void ProgramSource::attach_shader(const ShaderSource &shader) { - this->shaders.push_back(&shader); -} - - -RawProgram::RawProgram() {} - - }} // openage::renderer diff --git a/libopenage/renderer/shader.h b/libopenage/renderer/shader.h index cfa8be6a08..6a9bfaf9bc 100644 --- a/libopenage/renderer/shader.h +++ b/libopenage/renderer/shader.h @@ -84,60 +84,6 @@ class ShaderSourceCode : public ShaderSource { virtual ~ShaderSourceCode() {}; }; - -/** - * Create this to assemble shaders to a usable program. - * Register it at the renderer to use it. - * - * A program is a graphics card kernel, consisting of multiple - * shaders used for the rendering stages. - */ -class ProgramSource { -public: - ProgramSource(); - ProgramSource(const std::vector &shaders); - virtual ~ProgramSource() {}; - - /** - * Attach the given shader to this program. - */ - void attach_shader(const ShaderSource &shader); - - /** - * Return the list of all assigned shaders to this program. - */ - const std::vector &get_shaders() const; - -protected: - /** - * All attached shaders to this program. - */ - std::vector shaders; -}; - - -/** - * A usable handle, aquired by registering the sources of the program - * to the renderer and getting back an object of this class. - */ -class RawProgram { -protected: - RawProgram(); - -public: - virtual ~RawProgram() {}; - - /** - * Use this program now on the GPU. - */ - virtual void use() = 0; - - /** - * Dump vertex attribute variables. - */ - virtual void dump_attributes() = 0; -}; - }} // openage::renderer #endif From b783458650fbacd711f98fcf769ca81f8e12a40b Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Wed, 26 Aug 2015 00:47:19 +0200 Subject: [PATCH 17/32] renderer: initial pipeline variable abstraction --- libopenage/renderer/CMakeLists.txt | 1 + libopenage/renderer/context.h | 4 +- libopenage/renderer/material.h | 4 +- libopenage/renderer/opengl/context.cpp | 6 +- libopenage/renderer/opengl/context.h | 2 +- libopenage/renderer/opengl/pipeline.h | 18 +++ libopenage/renderer/opengl/program.cpp | 13 ++- libopenage/renderer/opengl/program.h | 29 ++++- libopenage/renderer/opengl/texture.cpp | 5 +- libopenage/renderer/opengl/texture.h | 2 +- libopenage/renderer/pipeline.cpp | 26 +++++ libopenage/renderer/pipeline.h | 108 ++++++++++++++++++ libopenage/renderer/program.cpp | 8 +- libopenage/renderer/program.h | 41 ++++--- libopenage/renderer/renderer.cpp | 2 +- libopenage/renderer/renderer.h | 4 +- libopenage/renderer/shaders/simpletexture.cpp | 8 ++ libopenage/renderer/shaders/simpletexture.h | 44 +++++++ libopenage/renderer/tests.cpp | 2 +- libopenage/renderer/texture.cpp | 10 ++ libopenage/renderer/texture.h | 11 +- 21 files changed, 301 insertions(+), 47 deletions(-) create mode 100644 libopenage/renderer/opengl/pipeline.h create mode 100644 libopenage/renderer/pipeline.cpp create mode 100644 libopenage/renderer/pipeline.h diff --git a/libopenage/renderer/CMakeLists.txt b/libopenage/renderer/CMakeLists.txt index f67123100e..67f451f1df 100644 --- a/libopenage/renderer/CMakeLists.txt +++ b/libopenage/renderer/CMakeLists.txt @@ -1,6 +1,7 @@ add_sources(libopenage color.cpp context.cpp + pipeline.cpp program.cpp renderer.cpp shader.cpp diff --git a/libopenage/renderer/context.h b/libopenage/renderer/context.h index d165b627d3..93ea5a7ae8 100644 --- a/libopenage/renderer/context.h +++ b/libopenage/renderer/context.h @@ -12,7 +12,7 @@ namespace openage { namespace renderer { class ProgramSource; -class RawProgram; +class Program; class Texture; class TextureData; @@ -96,7 +96,7 @@ class Context { * Register some shader pipeline program to the context. * @returns the newly created Program handle. */ - virtual std::unique_ptr register_program(const ProgramSource &data) = 0; + virtual std::unique_ptr register_program(const ProgramSource &data) = 0; /** * Resize the context because the surrounding window size was updated. diff --git a/libopenage/renderer/material.h b/libopenage/renderer/material.h index ad50787816..05ef164613 100644 --- a/libopenage/renderer/material.h +++ b/libopenage/renderer/material.h @@ -13,8 +13,8 @@ namespace renderer { class Material { protected: - std::shared_ptr txt; - std::shared_ptr code; + Texture* txt; + Program* code; }; }} // namespace openage::renderer diff --git a/libopenage/renderer/opengl/context.cpp b/libopenage/renderer/opengl/context.cpp index c2e176a99d..9059695204 100644 --- a/libopenage/renderer/opengl/context.cpp +++ b/libopenage/renderer/opengl/context.cpp @@ -170,12 +170,12 @@ void Context::screenshot(const std::string &filename) { std::unique_ptr Context::register_texture(const TextureData &data) { - std::unique_ptr txt = std::make_unique(data); + std::unique_ptr txt = std::make_unique(this, data); return txt; } -std::unique_ptr Context::register_program(const ProgramSource &data) { - std::unique_ptr txt = std::make_unique(data); +std::unique_ptr Context::register_program(const ProgramSource &data) { + std::unique_ptr txt = std::make_unique(this, data); return txt; } diff --git a/libopenage/renderer/opengl/context.h b/libopenage/renderer/opengl/context.h index c34455c581..c198334682 100644 --- a/libopenage/renderer/opengl/context.h +++ b/libopenage/renderer/opengl/context.h @@ -70,7 +70,7 @@ class Context : public renderer::Context { * Register a glsl shader pipeline program to the context. * @returns a handle to the new program. */ - std::unique_ptr register_program(const ProgramSource &data) override; + std::unique_ptr register_program(const ProgramSource &data) override; protected: diff --git a/libopenage/renderer/opengl/pipeline.h b/libopenage/renderer/opengl/pipeline.h new file mode 100644 index 0000000000..b84d495f20 --- /dev/null +++ b/libopenage/renderer/opengl/pipeline.h @@ -0,0 +1,18 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_OPENGL_PIPELINE_H_ +#define OPENAGE_RENDERER_OPENGL_PIPELINE_H_ + + +#include "../pipeline.h" + + +namespace openage { +namespace renderer { +namespace opengl { + +// TODO + +}}} // openage::renderer::opengl + +#endif diff --git a/libopenage/renderer/opengl/program.cpp b/libopenage/renderer/opengl/program.cpp index a20dabbbde..73e3b978dc 100644 --- a/libopenage/renderer/opengl/program.cpp +++ b/libopenage/renderer/opengl/program.cpp @@ -15,8 +15,9 @@ namespace openage { namespace renderer { namespace opengl { -Program::Program(const ProgramSource &source) +Program::Program(renderer::Context *context, const ProgramSource &source) : + renderer::Program{context}, is_linked{false} { // tell OpenGL we wanna have a new pipeline program @@ -185,6 +186,16 @@ void Program::dump_attributes() { log::log(msg); } + +void Program::set_uniform_3f(const char *name, const std::array &value) { + // TODO +} + + +void Program::set_uniform_2dtexture(const char *name, const Texture &value) { + // TODO +} + }}} // openage::renderer::opengl #endif // WITH_OPENGL diff --git a/libopenage/renderer/opengl/program.h b/libopenage/renderer/opengl/program.h index e5572648ab..0f30dcbfc8 100644 --- a/libopenage/renderer/opengl/program.h +++ b/libopenage/renderer/opengl/program.h @@ -11,21 +11,23 @@ namespace openage { namespace renderer { + +class Context; + namespace opengl { -class Program : public RawProgram { +class Program : public renderer::Program { public: /** * A program is created from shader sources. */ - Program(const ProgramSource &source); - + Program(renderer::Context *context, const ProgramSource &source); virtual ~Program(); /** * Activate the program on the GPU. */ - virtual void use() override; + void use() override; /** * Return the opengl handle id for a given pipeline uniform variable. @@ -51,7 +53,21 @@ class Program : public RawProgram { * Query OpenGL which of the vertex attributes are actually active * and haven't been optimized out by the compiler. */ - virtual void dump_attributes() override; + void dump_attributes() override; + + /* ====== */ + // shader variables + + /** + * Set a 3 dimensional float vector + */ + void set_uniform_3f(const char *name, const std::array &value) override; + + /** + * Set 2d texture data. + */ + void set_uniform_2dtexture(const char *name, const Texture &value) override; + protected: /** @@ -89,7 +105,8 @@ class Program : public RawProgram { /** * checks a given status for this program. * - * @param what_to_check GL_LINK_STATUS GL_VALIDATE_STATUS GL_COMPILE_STATUS + * @param what_to_check can be one of GL_LINK_STATUS + * GL_VALIDATE_STATUS GL_COMPILE_STATUS */ void check(GLenum what_to_check); diff --git a/libopenage/renderer/opengl/texture.cpp b/libopenage/renderer/opengl/texture.cpp index 14c9d81457..40258d944c 100644 --- a/libopenage/renderer/opengl/texture.cpp +++ b/libopenage/renderer/opengl/texture.cpp @@ -15,7 +15,10 @@ namespace renderer { namespace opengl { -Texture::Texture(const TextureData &txt) { +Texture::Texture(renderer::Context *context, const TextureData &txt) + : + renderer::Texture{context} { + // generate opengl texture handle glGenTextures(1, &this->id); glBindTexture(GL_TEXTURE_2D, id); diff --git a/libopenage/renderer/opengl/texture.h b/libopenage/renderer/opengl/texture.h index 55e6eec46b..b4daf9d237 100644 --- a/libopenage/renderer/opengl/texture.h +++ b/libopenage/renderer/opengl/texture.h @@ -16,7 +16,7 @@ namespace opengl { */ class Texture : public renderer::Texture { public: - Texture(const TextureData &data); + Texture(renderer::Context *context, const TextureData &data); ~Texture(); protected: diff --git a/libopenage/renderer/pipeline.cpp b/libopenage/renderer/pipeline.cpp new file mode 100644 index 0000000000..425d9c1a0c --- /dev/null +++ b/libopenage/renderer/pipeline.cpp @@ -0,0 +1,26 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +/** @file + * common code for all pipeline programs. + */ + +#include "pipeline.h" + +namespace openage { +namespace renderer { + +Pipeline::Pipeline(Program *prg) + : + program{prg} { +} + +Pipeline::~Pipeline() {} + + +void Pipeline::add_var(const std::string &name, PipelineVariable &var) { + // just add the variable to the known list + this->variables[name] = &var; +} + + +}} // openage::renderer diff --git a/libopenage/renderer/pipeline.h b/libopenage/renderer/pipeline.h new file mode 100644 index 0000000000..4e406f99cc --- /dev/null +++ b/libopenage/renderer/pipeline.h @@ -0,0 +1,108 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_PIPELINE_H_ +#define OPENAGE_RENDERER_PIPELINE_H_ + + +#include "program.h" + + +namespace openage { +namespace renderer { + +/** + * A pipeline property. Wraps GPU state to be set. + */ +class PipelineVariable { +}; + + +/** + * Pipeline uniform variable, which is a global value for all shader stages. + */ +template +class Uniform : public PipelineVariable { +public: + Uniform(const std::string &name, Program *program=nullptr); + virtual ~Uniform(); + + void set(const T &value); + void set_program(Program *program); + + void apply(); + +protected: + std::string name; + T value; + Program *program; +}; + + +/** + * Vertex attribute property. + * + * All vertex attributes of a shader have the same number of entries! + * All of those attribtes are merged into a single buffer on request. + * The buffer merging is done in the respective context. + * This buffer is then transmitted to the GPU. + */ +template +class Attribute : public PipelineVariable { +public: + // TODO + +protected: + /** + * The vertex attribute values + */ + std::vector values; +}; + + +/** + * Inherit from this class to create statically known pipeline properties. + * + * Add members of PipelineVariable to the inherited class + * to make pipeline variables available to the outside. + * This buffer is then transmitted to the GPU when the time has come. + */ +class Pipeline { +public: + Pipeline(Program *prg); + virtual ~Pipeline(); + +protected: + /** + * Add the given program variable to the list of maintained + * pipeline attributes. + */ + void add_var(const std::string &name, PipelineVariable &var); + + /** + * Set the name of a uniform to a given value. + * e.g. set_uniform("color", {0.0, 0.0, 1.0}); + */ + template + void set_uniform(const std::string &name, const T &value) { + Uniform u{name, this->program}; + u.set(value); + u.apply(); + } + + /** + * The pipeline program associated with this property definition class. + */ + Program *program; + + /** + * Syncs attribute entry lengths. + * Each attribute has to be supplied for each vertex once. + * e.g. vec3 1337 color entries require 1337 vec4 positions. + * These have different per-attribute sizes but the same lengths. + */ + std::unordered_map variables; +}; + +}} // openage::renderer + +#endif diff --git a/libopenage/renderer/program.cpp b/libopenage/renderer/program.cpp index eee1830627..22a3adccce 100644 --- a/libopenage/renderer/program.cpp +++ b/libopenage/renderer/program.cpp @@ -1,7 +1,7 @@ // Copyright 2015-2015 the openage authors. See copying.md for legal info. /** @file - * common code for all pipeline programs. + * common code for all shader programs. */ #include "program.h" @@ -29,7 +29,9 @@ void ProgramSource::attach_shader(const ShaderSource &shader) { } -RawProgram::RawProgram() {} - +Program::Program(Context *context) + : + context{context} { +} }} // openage::renderer diff --git a/libopenage/renderer/program.h b/libopenage/renderer/program.h index 4921860e3b..ff07d011d5 100644 --- a/libopenage/renderer/program.h +++ b/libopenage/renderer/program.h @@ -4,12 +4,16 @@ #define OPENAGE_RENDERER_PROGRAM_H_ #include +#include #include namespace openage { namespace renderer { +class Context; class ShaderSource; +class Texture; + /** * Create this to assemble shaders to a usable program. @@ -46,12 +50,12 @@ class ProgramSource { * A usable handle, aquired by registering the sources of the program * to the renderer and getting back an object of this class. */ -class RawProgram { +class Program { protected: - RawProgram(); + Program(Context *context); public: - virtual ~RawProgram() {}; + virtual ~Program() {}; /** * Use this program now on the GPU. @@ -62,34 +66,29 @@ class RawProgram { * Dump vertex attribute variables. */ virtual void dump_attributes() = 0; -}; + /* ========================================== */ + // available pipeline properties -/** - * Inherit from this class to create statically known program properties. - * - * Add members of ProgramVariable to the inherited class - * to make pipeline variables available to the outside. - */ -class Program { -public: - Program(std::unique_ptr prg); - virtual ~Program(); + /** + * Set a 3 dimensional float vector + */ + virtual void set_uniform_3f(const char *name, const std::array &value) = 0; -protected: /** - * Add the given program variable to the list of maintained - * pipeline attributes. + * Set 2d texture data. */ - void add_var(const ProgramVariable &var); + virtual void set_uniform_2dtexture(const char *name, const Texture &value) = 0; + /* ========================================== */ + +protected: /** - * The pipeline program associated with this property definition class. + * The associated context. */ - std::unique_ptr raw_program; + Context *context; }; - }} // openage::renderer #endif diff --git a/libopenage/renderer/renderer.cpp b/libopenage/renderer/renderer.cpp index 623175a3e8..49e30d32b0 100644 --- a/libopenage/renderer/renderer.cpp +++ b/libopenage/renderer/renderer.cpp @@ -23,7 +23,7 @@ TaskState Renderer::add_task(const Task &task) { } -std::unique_ptr Renderer::add_program(const ProgramSource &source) { +std::unique_ptr Renderer::add_program(const ProgramSource &source) { return this->context->register_program(source); } diff --git a/libopenage/renderer/renderer.h b/libopenage/renderer/renderer.h index 72ab04a383..f4a0244c11 100644 --- a/libopenage/renderer/renderer.h +++ b/libopenage/renderer/renderer.h @@ -22,7 +22,7 @@ namespace renderer { class Context; class ProgramSource; -class RawProgram; +class Program; class Texture; class TextureData; @@ -62,7 +62,7 @@ class Renderer : public ResizeHandler { * * @returns a handle to the usable program for the developer. */ - std::unique_ptr add_program(const ProgramSource &source); + std::unique_ptr add_program(const ProgramSource &source); /** * Register a texture to the renderer. diff --git a/libopenage/renderer/shaders/simpletexture.cpp b/libopenage/renderer/shaders/simpletexture.cpp index 81dd7950d8..7f899b2528 100644 --- a/libopenage/renderer/shaders/simpletexture.cpp +++ b/libopenage/renderer/shaders/simpletexture.cpp @@ -1,3 +1,11 @@ // Copyright 2015-2015 the openage authors. See copying.md for legal info. #include "simpletexture.h" + +SimpleTextureProgram::SimpleTextureProgram(RawProgram *prg) + : + Program{prg} { + + this->add_var(this->position); + this->add_var(this->tex); +} diff --git a/libopenage/renderer/shaders/simpletexture.h b/libopenage/renderer/shaders/simpletexture.h index 25429e0861..6a544fdc01 100644 --- a/libopenage/renderer/shaders/simpletexture.h +++ b/libopenage/renderer/shaders/simpletexture.h @@ -3,5 +3,49 @@ #ifndef OPENAGE_RENDERER_SHADERS_SIMPLETEXTURE_H_ #define OPENAGE_RENDERER_SHADERS_SIMPLETEXTURE_H_ +#include + +#include "../shader.h" + + +class SimpleTextureProgram : public Program { +public: + SimpleTextureProgram(RawProgram *prg); + virtual ~SimpleTextureProgram(); + + Uniform color; + Attribute position; + Attribute color_tint; +}; + + +void lol() { + SimpleTextureProgram asdf{program}; + + asdf.color = {1, 0, 0, 1}; + asdf.tex = tentacle_monster; + + DrawHandle draw = asdf.get_handle(); + draw.position = { + {0, 0}, + {0, 1}, + {1, 0}, + {1, 1} + }; + + draw.color_tint = { + {0, 0, 0}, + {1, 0, 0}, + {0, 1, 0}, + {0, 0, 1}, + }; + + // enqueue the call, this tests the constraints internally. + // len(whatever)/pervertex == len(positions)/pervertex + // packs the buffers when needed, + // submits to gpu when needed. + renderer.add_task(draw.get_task()); +} + #endif diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index b9c9fe62de..dc0709cf1c 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -103,7 +103,7 @@ void renderer_demo() { ProgramSource simplequad_src({&vshader_src, &fshader_src}); - std::unique_ptr simplequad = renderer.add_program(simplequad_src); + std::unique_ptr simplequad = renderer.add_program(simplequad_src); simplequad->dump_attributes(); diff --git a/libopenage/renderer/texture.cpp b/libopenage/renderer/texture.cpp index 253cd26e99..384c2360ae 100644 --- a/libopenage/renderer/texture.cpp +++ b/libopenage/renderer/texture.cpp @@ -10,6 +10,13 @@ namespace openage { namespace renderer { +Texture::Texture(Context *ctx) + : + context{ctx} { +} + +Texture::~Texture() {} + const std::tuple Texture::get_size() const { return std::make_tuple(this->w, this->h); } @@ -24,6 +31,7 @@ const gamedata::subtexture *Texture::get_subtexture(size_t subid) const { } } + const std::tuple Texture::get_subtexture_coordinates(size_t subid) const { auto tx = this->get_subtexture(subid); return std::make_tuple( @@ -34,10 +42,12 @@ const std::tuple Texture::get_subtexture_coordinates ); } + int Texture::get_subtexture_count() const { return this->subtextures.size(); } + const std::tuple Texture::get_subtexture_size(size_t subid) const { auto subtex = this->get_subtexture(subid); return std::make_tuple(subtex->w, subtex->h); diff --git a/libopenage/renderer/texture.h b/libopenage/renderer/texture.h index b617113323..9aefbc21ec 100644 --- a/libopenage/renderer/texture.h +++ b/libopenage/renderer/texture.h @@ -14,6 +14,7 @@ namespace openage { namespace renderer { +class Context; /** * Texture format, used for setting pixel data size. @@ -34,9 +35,10 @@ enum class texture_format { */ class Texture { protected: - Texture() = default; + Texture(Context *ctx); + public: - virtual ~Texture() {}; + virtual ~Texture(); /** * Return the dimensions of the whole texture bitmap @@ -69,6 +71,11 @@ class Texture { */ virtual const std::tuple get_subtexture_coordinates(size_t subid) const; + /** + * The associated graphics context. + */ + Context *context; + protected: /** * Atlas texture positions. From 89210b0b5912bb673fd51c7a7b3f94a54e42aba0 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Tue, 15 Sep 2015 16:01:06 +0200 Subject: [PATCH 18/32] error: always collect backtrace information also beautified header and purged namespace redundancy. --- libopenage/error/error.h | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/libopenage/error/error.h b/libopenage/error/error.h index 3c8ed868d1..a758d15f34 100644 --- a/libopenage/error/error.h +++ b/libopenage/error/error.h @@ -14,24 +14,22 @@ // pxd: from libopenage.error.backtrace cimport Backtrace namespace openage { -namespace error { -// forward-declaration to avoid the header include. -class Backtrace; -}} -namespace openage { namespace pyinterface { // forward-declaration for use in the 'friend' declaration below. class PyException; -}} - +} -namespace openage { +/** + * Contains the exception handling and backtracing subsystem. + */ namespace error { +class Backtrace; + /** - * Openage base exception type; the constructor usage is analogous to - * log::log(). + * openage base exception type. + * the constructor usage is analogous to log::log(). * * pxd: * @@ -174,4 +172,4 @@ inline std::string no_ensuring_message() using error::Error; -} // openage::error +} // openage From 4a63a69c64ef1984fdff4d76ada23cfe43843a36 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Tue, 15 Sep 2015 16:02:02 +0200 Subject: [PATCH 19/32] renderer: demo id selection via cython --- libopenage/renderer/CMakeLists.txt | 4 +++ libopenage/renderer/context.cpp | 4 ++- libopenage/renderer/context.h | 5 +++ libopenage/renderer/opengl/program.cpp | 48 ++++++++++++++++++++------ libopenage/renderer/opengl/program.h | 17 +++++++-- libopenage/renderer/opengl/texture.cpp | 5 +++ libopenage/renderer/opengl/texture.h | 5 +++ libopenage/renderer/program.h | 12 +++++-- libopenage/renderer/tests.cpp | 5 +-- libopenage/renderer/tests.h | 10 ++++++ libopenage/renderer/texture.cpp | 11 +++++- libopenage/renderer/texture.h | 28 +++++++++++---- openage/game/main_cpp.pyx | 2 +- openage/renderer/CMakeLists.txt | 2 +- openage/renderer/tests.py | 5 --- openage/renderer/tests.pyx | 26 ++++++++++++++ openage/testing/testlist.py | 4 +-- 17 files changed, 157 insertions(+), 36 deletions(-) create mode 100644 libopenage/renderer/tests.h delete mode 100644 openage/renderer/tests.py create mode 100644 openage/renderer/tests.pyx diff --git a/libopenage/renderer/CMakeLists.txt b/libopenage/renderer/CMakeLists.txt index 67f451f1df..2a0c1d9792 100644 --- a/libopenage/renderer/CMakeLists.txt +++ b/libopenage/renderer/CMakeLists.txt @@ -12,5 +12,9 @@ add_sources(libopenage window.cpp ) +pxdgen( + tests.h +) + add_subdirectory(font/) add_subdirectory(opengl/) diff --git a/libopenage/renderer/context.cpp b/libopenage/renderer/context.cpp index 56e1fea602..4bfc932327 100644 --- a/libopenage/renderer/context.cpp +++ b/libopenage/renderer/context.cpp @@ -20,7 +20,9 @@ namespace renderer { Context::Context(context_type type) : - type{type} {} + type{type}, + pipeline{nullptr} { +} std::unique_ptr Context::generate(context_type t) { context_type ctx_requested = t; diff --git a/libopenage/renderer/context.h b/libopenage/renderer/context.h index 93ea5a7ae8..c44ce75800 100644 --- a/libopenage/renderer/context.h +++ b/libopenage/renderer/context.h @@ -118,6 +118,11 @@ class Context { * Render surface size. */ coord::window canvas_size; + + /** + * Active pipeline program. + */ + Program *pipeline; }; }} // namespace openage::renderer diff --git a/libopenage/renderer/opengl/program.cpp b/libopenage/renderer/opengl/program.cpp index 73e3b978dc..fc8abdd702 100644 --- a/libopenage/renderer/opengl/program.cpp +++ b/libopenage/renderer/opengl/program.cpp @@ -5,6 +5,8 @@ #include "program.h" +#include "texture.h" + #include "../../log/log.h" #include "../../error/error.h" #include "../../util/compiler.h" @@ -106,8 +108,9 @@ void Program::check(GLenum what_to_check) { void Program::use() { if (unlikely(not this->is_linked)) { - throw Error{MSG(err) << "using none-linked program!", true}; + throw Error{MSG(err) << "using program before it was linked!"}; } + glUseProgram(this->id); } @@ -116,16 +119,27 @@ void Program::stopusing() { } void Program::check_is_linked(const char *info) { - // just throw up when we're not linked yet. - if (unlikely(not this->is_linked)) { - throw Error{MSG(err) << info << " before program was linked!", true}; + throw Error{MSG(err) << info << " before program was linked!"}; } } GLint Program::get_uniform_id(const char *name) { - this->check_is_linked("Uniform id requested"); - return glGetUniformLocation(this->id, name); + GLint loc; + + // check if the location is cached. + auto it = this->uniforms.find(name); + if (it == this->uniforms.end()) { + this->check_is_linked("Uniform id requested"); + loc = glGetUniformLocation(this->id, name); + + // save to cache + this->uniforms[name] = loc; + } else { + loc = it->second; + } + + return loc; } GLint Program::get_uniformbuffer_id(const char *name) { @@ -187,13 +201,27 @@ void Program::dump_attributes() { } -void Program::set_uniform_3f(const char *name, const std::array &value) { - // TODO +void Program::set_uniform_3f(const char *name, const util::Vector<3> &value) { + // TODO: is required? this->use(); + GLint location = this->get_uniform_id(name); + glUniform3f(location, value[0], value[1], value[2]); } -void Program::set_uniform_2dtexture(const char *name, const Texture &value) { - // TODO +void Program::set_uniform_1i(const char *name, const int &value) { + GLint location = this->get_uniform_id(name); + glUniform1i(location, value); +} + + +void Program::set_uniform_2dtexture(const char *name, renderer::Texture &texture) { + // set the sampler "value" to the texture slot id. + GLint location = this->get_uniform_id(name); + + // TODO: use multiple slots! + int slot = 0; + glUniform1i(location, slot); + texture.bind_to(slot); } }}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/program.h b/libopenage/renderer/opengl/program.h index 0f30dcbfc8..bd27d09530 100644 --- a/libopenage/renderer/opengl/program.h +++ b/libopenage/renderer/opengl/program.h @@ -4,6 +4,7 @@ #define OPENAGE_RENDERER_OPENGL_PROGRAM_H_ #include +#include #include #include "../program.h" @@ -61,12 +62,17 @@ class Program : public renderer::Program { /** * Set a 3 dimensional float vector */ - void set_uniform_3f(const char *name, const std::array &value) override; + void set_uniform_3f(const char *name, const util::Vector<3> &value) override; + + /** + * Set a single integer value + */ + void set_uniform_1i(const char *name, const int &value) override; /** * Set 2d texture data. */ - void set_uniform_2dtexture(const char *name, const Texture &value) override; + void set_uniform_2dtexture(const char *name, renderer::Texture &value) override; protected: @@ -102,6 +108,13 @@ class Program : public renderer::Program { */ std::vector shader_ids; + /** + * Uniform id cache. + * + * Maps uniform variable name to the handle id. + */ + std::unordered_map uniforms; + /** * checks a given status for this program. * diff --git a/libopenage/renderer/opengl/texture.cpp b/libopenage/renderer/opengl/texture.cpp index 40258d944c..51c9e32518 100644 --- a/libopenage/renderer/opengl/texture.cpp +++ b/libopenage/renderer/opengl/texture.cpp @@ -57,6 +57,11 @@ Texture::~Texture() { glDeleteTextures(1, &this->id); } +void Texture::bind_to(int slot) { + glActiveTexture(GL_TEXTURE0 + slot); + glBindTexture(GL_TEXTURE_2D, this->id); +} + }}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/texture.h b/libopenage/renderer/opengl/texture.h index b4daf9d237..2bc4dcc5b9 100644 --- a/libopenage/renderer/opengl/texture.h +++ b/libopenage/renderer/opengl/texture.h @@ -19,6 +19,11 @@ class Texture : public renderer::Texture { Texture(renderer::Context *context, const TextureData &data); ~Texture(); + /** + * Bind this texture to the given slot id. + */ + void bind_to(int slot) override; + protected: /** * OpenGL handle id. diff --git a/libopenage/renderer/program.h b/libopenage/renderer/program.h index ff07d011d5..202343edd9 100644 --- a/libopenage/renderer/program.h +++ b/libopenage/renderer/program.h @@ -7,6 +7,8 @@ #include #include +#include "../util/vector.h" + namespace openage { namespace renderer { @@ -73,12 +75,18 @@ class Program { /** * Set a 3 dimensional float vector */ - virtual void set_uniform_3f(const char *name, const std::array &value) = 0; + virtual void set_uniform_3f(const char *name, const util::Vector<3> &value) = 0; + + /** + * Set a single integer value + */ + virtual void set_uniform_1i(const char *name, const int &value) = 0; + /** * Set 2d texture data. */ - virtual void set_uniform_2dtexture(const char *name, const Texture &value) = 0; + virtual void set_uniform_2dtexture(const char *name, Texture &value) = 0; /* ========================================== */ diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index dc0709cf1c..184130897b 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -73,10 +73,7 @@ void render_test(Window &window, const render_demo *actions) { } } - -void renderer_demo() { - int demo_id = 0; - +void renderer_demo(int demo_id) { Window window{"openage renderer testing"}; Renderer renderer{window.get_context()}; diff --git a/libopenage/renderer/tests.h b/libopenage/renderer/tests.h new file mode 100644 index 0000000000..54ea3967da --- /dev/null +++ b/libopenage/renderer/tests.h @@ -0,0 +1,10 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +namespace openage { +namespace renderer { +namespace tests { + +// pxd: void renderer_demo(int demo_id) except + +void renderer_demo(int demo_id); + +}}} // openage::renderer::tests diff --git a/libopenage/renderer/texture.cpp b/libopenage/renderer/texture.cpp index 384c2360ae..37a2aab4b8 100644 --- a/libopenage/renderer/texture.cpp +++ b/libopenage/renderer/texture.cpp @@ -54,7 +54,7 @@ const std::tuple Texture::get_subtexture_size(size_t subid) const { } -TextureData::TextureData(int width, int height, uint8_t *data) +TextureData::TextureData(int width, int height, char *data) : format{texture_format::rgba}, w{width}, @@ -68,6 +68,15 @@ TextureData::TextureData(int width, int height, uint8_t *data) memcpy(this->data.get(), data, pixel_size); } +TextureData::TextureData(int width, int height, std::unique_ptr data) + : + format{texture_format::rgba}, + w{width}, + h{height} { + + this->data = std::move(data); +} + FileTextureData::FileTextureData(const std::string &filename, bool use_metafile) diff --git a/libopenage/renderer/texture.h b/libopenage/renderer/texture.h index 9aefbc21ec..5854a99afe 100644 --- a/libopenage/renderer/texture.h +++ b/libopenage/renderer/texture.h @@ -44,23 +44,23 @@ class Texture { * Return the dimensions of the whole texture bitmap * @returns tuple(width, height) */ - virtual const std::tuple get_size() const; + const std::tuple get_size() const; /** * Get the subtexture coordinates by its idea. */ - virtual const gamedata::subtexture *get_subtexture(size_t subid) const; + const gamedata::subtexture *get_subtexture(size_t subid) const; /** * @return the number of available subtextures */ - virtual int get_subtexture_count() const; + int get_subtexture_count() const; /** * Fetch the size of the given subtexture. * @param subid: index of the requested subtexture */ - virtual const std::tuple get_subtexture_size(size_t subid) const; + const std::tuple get_subtexture_size(size_t subid) const; /** * get atlas subtexture coordinates. @@ -69,7 +69,12 @@ class Texture { * the requested area out of the big texture. returned as floats in * range 0.0 to 1.0, relative to the whole surface size. */ - virtual const std::tuple get_subtexture_coordinates(size_t subid) const; + const std::tuple get_subtexture_coordinates(size_t subid) const; + + /** + * Bind the texture to the given texture unit slot id. + */ + virtual void bind_to(int slot) = 0; /** * The associated graphics context. @@ -100,9 +105,18 @@ class TextureData { /** * Create a texture from a rgba8 array. * It will have w * h * 4byte storage. - * Each pixel has one byte, and r g b and alpha values. + * Each pixel has one byte for its r g b and alpha values, + * resulting in 32 bit per pixel. + * + * Copies the data to this texture. + */ + TextureData(int width, int height, char *data); + + /** + * Create a texture from an rgba8 array by moving the data. + * Each pixel consists of 4 chars, for r g b and alpha. */ - TextureData(int width, int height, uint8_t *data); + TextureData(int width, int height, std::unique_ptr data); virtual ~TextureData() = default; diff --git a/openage/game/main_cpp.pyx b/openage/game/main_cpp.pyx index 1eacc0d35f..129c4d75fe 100644 --- a/openage/game/main_cpp.pyx +++ b/openage/game/main_cpp.pyx @@ -10,7 +10,7 @@ def run_game(args, assets): del assets # unused for now. - cdef main_arguments args_cpp; + cdef main_arguments args_cpp args_cpp.data_directory = args.asset_dir.encode() if args.fps is not None: diff --git a/openage/renderer/CMakeLists.txt b/openage/renderer/CMakeLists.txt index 909a214c08..1f32b350a4 100644 --- a/openage/renderer/CMakeLists.txt +++ b/openage/renderer/CMakeLists.txt @@ -1,8 +1,8 @@ add_cython_modules( renderer_cpp.pyx + tests.pyx ) add_py_modules( __init__.py - tests.py ) diff --git a/openage/renderer/tests.py b/openage/renderer/tests.py deleted file mode 100644 index dea70b3f44..0000000000 --- a/openage/renderer/tests.py +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright 2015-2015 the openage authors. See copying.md for legal info. - -""" -tests for the graphics renderer. -""" diff --git a/openage/renderer/tests.pyx b/openage/renderer/tests.pyx new file mode 100644 index 0000000000..532cf56585 --- /dev/null +++ b/openage/renderer/tests.pyx @@ -0,0 +1,26 @@ +# Copyright 2015-2015 the openage authors. See copying.md for legal info. + +""" +tests for the graphics renderer. +""" + +import argparse + +from libopenage.renderer.tests cimport renderer_demo as renderer_demo_c + +def renderer_demo(list argv): + """ + invokes the available render demos. + """ + + cmd = argparse.ArgumentParser( + prog='... renderer_demo', + description='Various renderer demos') + cmd.add_argument("test_id", type=int, help="id of the demo to run.") + + args = cmd.parse_args(argv) + + cdef int renderer_test_id = args.test_id + + with nogil: + renderer_demo_c(renderer_test_id) diff --git a/openage/testing/testlist.py b/openage/testing/testlist.py index 851b959511..7835d456c6 100644 --- a/openage/testing/testlist.py +++ b/openage/testing/testlist.py @@ -42,6 +42,8 @@ def demos_py(): "translates a C++ exception and its causes to python") yield ("openage.log.tests.demo", "demonstrates the translation of Python log messages") + yield ("openage.renderer.tests.renderer_demo", + "showcases the new renderer") def tests_cpp(): @@ -87,5 +89,3 @@ def demos_cpp(): "translates a Python exception to C++") yield ("openage::pyinterface::tests::pyobject_demo", "a tiny interactive interpreter using PyObjectRef") - yield ("openage::renderer::tests::renderer_demo", - "showcases the new renderer") From 22162c00a4e3cf9fe94fc18604a3a9a30358d456 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Tue, 15 Sep 2015 17:49:51 +0200 Subject: [PATCH 20/32] renderer: first successful texture draw --- libopenage/renderer/CMakeLists.txt | 1 + libopenage/renderer/opengl/program.cpp | 5 +- libopenage/renderer/pipeline.h | 22 ++- libopenage/renderer/shaders/CMakeLists.txt | 3 + libopenage/renderer/shaders/simpletexture.cpp | 16 +- libopenage/renderer/shaders/simpletexture.h | 45 ++---- libopenage/renderer/tests.cpp | 147 ++++++++++++++++-- libopenage/renderer/tests.h | 6 + 8 files changed, 186 insertions(+), 59 deletions(-) create mode 100644 libopenage/renderer/shaders/CMakeLists.txt diff --git a/libopenage/renderer/CMakeLists.txt b/libopenage/renderer/CMakeLists.txt index 2a0c1d9792..42d37154af 100644 --- a/libopenage/renderer/CMakeLists.txt +++ b/libopenage/renderer/CMakeLists.txt @@ -18,3 +18,4 @@ pxdgen( add_subdirectory(font/) add_subdirectory(opengl/) +add_subdirectory(shaders/) diff --git a/libopenage/renderer/opengl/program.cpp b/libopenage/renderer/opengl/program.cpp index fc8abdd702..5501081e9c 100644 --- a/libopenage/renderer/opengl/program.cpp +++ b/libopenage/renderer/opengl/program.cpp @@ -202,19 +202,22 @@ void Program::dump_attributes() { void Program::set_uniform_3f(const char *name, const util::Vector<3> &value) { - // TODO: is required? this->use(); + this->use(); GLint location = this->get_uniform_id(name); glUniform3f(location, value[0], value[1], value[2]); } void Program::set_uniform_1i(const char *name, const int &value) { + this->use(); GLint location = this->get_uniform_id(name); glUniform1i(location, value); } void Program::set_uniform_2dtexture(const char *name, renderer::Texture &texture) { + this->use(); + // set the sampler "value" to the texture slot id. GLint location = this->get_uniform_id(name); diff --git a/libopenage/renderer/pipeline.h b/libopenage/renderer/pipeline.h index 4e406f99cc..f60c6045cd 100644 --- a/libopenage/renderer/pipeline.h +++ b/libopenage/renderer/pipeline.h @@ -3,6 +3,7 @@ #ifndef OPENAGE_RENDERER_PIPELINE_H_ #define OPENAGE_RENDERER_PIPELINE_H_ +#include #include "program.h" @@ -23,17 +24,27 @@ class PipelineVariable { template class Uniform : public PipelineVariable { public: - Uniform(const std::string &name, Program *program=nullptr); - virtual ~Uniform(); + Uniform(Program *program=nullptr) + : + program{program} { + } + + virtual ~Uniform() {}; + + void set_name(const std::string &name) { + this->name = name; + } void set(const T &value); - void set_program(Program *program); + + void set_program(Program *program) { + this->program = program; + } void apply(); protected: std::string name; - T value; Program *program; }; @@ -49,7 +60,8 @@ class Uniform : public PipelineVariable { template class Attribute : public PipelineVariable { public: - // TODO + Attribute() {}; + virtual ~Attribute() {}; protected: /** diff --git a/libopenage/renderer/shaders/CMakeLists.txt b/libopenage/renderer/shaders/CMakeLists.txt new file mode 100644 index 0000000000..19aecc1b2b --- /dev/null +++ b/libopenage/renderer/shaders/CMakeLists.txt @@ -0,0 +1,3 @@ +add_sources(libopenage + simpletexture.cpp +) diff --git a/libopenage/renderer/shaders/simpletexture.cpp b/libopenage/renderer/shaders/simpletexture.cpp index 7f899b2528..516d8bc8d5 100644 --- a/libopenage/renderer/shaders/simpletexture.cpp +++ b/libopenage/renderer/shaders/simpletexture.cpp @@ -2,10 +2,18 @@ #include "simpletexture.h" -SimpleTextureProgram::SimpleTextureProgram(RawProgram *prg) +namespace openage { +namespace renderer { + +SimpleTexturePipeline::SimpleTexturePipeline(Program *prg) : - Program{prg} { + Pipeline{prg} { - this->add_var(this->position); - this->add_var(this->tex); + this->add_var("tex", this->tex); + this->add_var("position", this->position); + this->add_var("texcoord", this->texcoord); } + +SimpleTexturePipeline::~SimpleTexturePipeline() {} + +}} // openage::renderer diff --git a/libopenage/renderer/shaders/simpletexture.h b/libopenage/renderer/shaders/simpletexture.h index 6a544fdc01..4ae7341ea3 100644 --- a/libopenage/renderer/shaders/simpletexture.h +++ b/libopenage/renderer/shaders/simpletexture.h @@ -5,47 +5,24 @@ #include -#include "../shader.h" +#include "../pipeline.h" +#include "../../util/vector.h" +namespace openage { +namespace renderer { -class SimpleTextureProgram : public Program { +class SimpleTexturePipeline : public Pipeline { public: - SimpleTextureProgram(RawProgram *prg); - virtual ~SimpleTextureProgram(); + SimpleTexturePipeline(Program *prg); + virtual ~SimpleTexturePipeline(); - Uniform color; - Attribute position; - Attribute color_tint; + Uniform tex; + Attribute position; + Attribute texcoord; }; -void lol() { - SimpleTextureProgram asdf{program}; - - asdf.color = {1, 0, 0, 1}; - asdf.tex = tentacle_monster; - - DrawHandle draw = asdf.get_handle(); - draw.position = { - {0, 0}, - {0, 1}, - {1, 0}, - {1, 1} - }; - - draw.color_tint = { - {0, 0, 0}, - {1, 0, 0}, - {0, 1, 0}, - {0, 0, 1}, - }; - - // enqueue the call, this tests the constraints internally. - // len(whatever)/pervertex == len(positions)/pervertex - // packs the buffers when needed, - // submits to gpu when needed. - renderer.add_task(draw.get_task()); -} +}} // openage::renderer #endif diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index 184130897b..c2e5fef44c 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -12,6 +12,7 @@ #include "window.h" #include "renderer.h" #include "shader.h" +#include "shaders/simpletexture.h" namespace openage { namespace renderer { @@ -73,7 +74,7 @@ void render_test(Window &window, const render_demo *actions) { } } -void renderer_demo(int demo_id) { +void renderer_demo_0() { Window window{"openage renderer testing"}; Renderer renderer{window.get_context()}; @@ -104,20 +105,19 @@ void renderer_demo(int demo_id) { simplequad->dump_attributes(); + float val = 0.9f; const float vpos[] = { - -1.0f, -1.0f, .0f, 1.0f, - 1.0f, -1.0f, .0f, 1.0f, - -1.0f, 1.0f, .0f, 1.0f, + -val, -val, .0f, 1.0f, + val, -val, .0f, 1.0f, + -val, val, .0f, 1.0f, - 1.0f, -1.0f, .0f, 1.0f, - -1.0f, 1.0f, .0f, 1.0f, - 1.0f, 1.0f, .0f, 1.0f, + val, -val, .0f, 1.0f, + -val, val, .0f, 1.0f, + val, val, .0f, 1.0f, }; GLuint vpos_buf, posattr_id = 0; - //posattr_id = simplequad->get_attribute_id("position"); - GLuint vao; render_demo test0{ @@ -156,14 +156,131 @@ void renderer_demo(int demo_id) { } }; - std::unordered_map demos; - demos[0] = &test0; + render_test(window, &test0); +} + - if (demos.find(demo_id) != demos.end()) { - render_test(window, demos[demo_id]); - } - else { +void renderer_demo_1() { + Window window{"openage renderer testing"}; + Renderer renderer{window.get_context()}; + + ShaderSourceCode vshader_src( + shader_type::vertex, + "#version 330\n" + "layout(location = 0) in vec4 position;" + "layout(location = 1) in vec2 texcoord;" + "smooth out vec2 texpos;" + "void main() {" + "texpos = texcoord;" + "gl_Position = position;" + "}" + ); + + ShaderSourceCode fshader_src( + shader_type::fragment, + "#version 330\n" + "out vec4 color;\n" + "smooth in vec2 texpos;" + "uniform sampler2D tex;" + "void main() {" + "color = texture(tex, texpos.xy);" + "}" + ); + + ProgramSource simpletex_src({&vshader_src, &fshader_src}); + + std::unique_ptr simpletex = renderer.add_program(simpletex_src); + + simpletex->dump_attributes(); + + FileTextureData gaben_data{"assets/gaben.png"}; + std::unique_ptr gaben = renderer.add_texture(gaben_data); + + SimpleTexturePipeline tex_pipeline{simpletex.get()}; + + + simpletex->set_uniform_2dtexture("tex", *gaben.get()); + + float val = 1.0f; + const float vpos[] = { + -val, -val, .0f, 1.0f, + val, -val, .0f, 1.0f, + -val, val, .0f, 1.0f, + + val, -val, .0f, 1.0f, + -val, val, .0f, 1.0f, + val, val, .0f, 1.0f, + + 0.0f, 1.0f, + 1.0f, 1.0f, + 0.0f, 0.0f, + + 1.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f, + }; + + // defined in the layout + GLuint vpos_buf, posattr_id = 0, texcoord_id = 1; + + GLuint vao; + + render_demo test1{ + // init + [&](Window */*window*/) { + glEnable(GL_BLEND); + + glGenBuffers(1, &vpos_buf); + glBindBuffer(GL_ARRAY_BUFFER, vpos_buf); + // save vertex attributes to GPU: + glBufferData(GL_ARRAY_BUFFER, sizeof(vpos), vpos, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); // stores all the vertex attrib state. + }, + // frame + [&]() { + glClearColor(0.0, 0.0, 0.2, 1.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + simpletex->use(); + + glBindBuffer(GL_ARRAY_BUFFER, vpos_buf); + glEnableVertexAttribArray(posattr_id); + glEnableVertexAttribArray(texcoord_id); + glVertexAttribPointer(posattr_id, 4, GL_FLOAT, GL_FALSE, 0, (void *)0); + glVertexAttribPointer(texcoord_id, 2, GL_FLOAT, GL_FALSE, 0, (void *)(4 * 4 * 6)); + glDrawArrays(GL_TRIANGLES, 0, 6); + + glDisableVertexAttribArray(posattr_id); + glDisableVertexAttribArray(texcoord_id); + + util::gl_check_error(); + }, + // resize + [&](const coord::window &new_size) { + renderer.on_resize(new_size); + } + }; + + render_test(window, &test1); +} + + +void renderer_demo(int demo_id) { + switch (demo_id) { + case 0: + renderer_demo_0(); + break; + + case 1: + renderer_demo_1(); + break; + + default: log::log(MSG(err) << "unknown renderer demo " << demo_id << " requested."); + break; } } diff --git a/libopenage/renderer/tests.h b/libopenage/renderer/tests.h index 54ea3967da..327f3123dd 100644 --- a/libopenage/renderer/tests.h +++ b/libopenage/renderer/tests.h @@ -1,5 +1,9 @@ // Copyright 2015-2015 the openage authors. See copying.md for legal info. +#ifndef OPENAGE_RENDERER_TESTS_H_ +#define OPENAGE_RENDERER_TESTS_H_ + + namespace openage { namespace renderer { namespace tests { @@ -8,3 +12,5 @@ namespace tests { void renderer_demo(int demo_id); }}} // openage::renderer::tests + +#endif From 3c4e93a776cbbf52249b66a4181c987abdfc03e0 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Wed, 16 Sep 2015 13:44:53 +0200 Subject: [PATCH 21/32] renderer: basic uniform variable abstraction --- libopenage/renderer/opengl/program.cpp | 2 +- libopenage/renderer/opengl/program.h | 2 +- libopenage/renderer/opengl/texture.cpp | 2 +- libopenage/renderer/opengl/texture.h | 2 +- libopenage/renderer/pipeline.cpp | 28 +++++++++ libopenage/renderer/pipeline.h | 69 +++++++++++++-------- libopenage/renderer/program.h | 2 +- libopenage/renderer/shaders/simpletexture.h | 4 +- libopenage/renderer/tests.cpp | 5 +- libopenage/renderer/texture.h | 2 +- 10 files changed, 81 insertions(+), 37 deletions(-) diff --git a/libopenage/renderer/opengl/program.cpp b/libopenage/renderer/opengl/program.cpp index 5501081e9c..cf34cbd1d2 100644 --- a/libopenage/renderer/opengl/program.cpp +++ b/libopenage/renderer/opengl/program.cpp @@ -215,7 +215,7 @@ void Program::set_uniform_1i(const char *name, const int &value) { } -void Program::set_uniform_2dtexture(const char *name, renderer::Texture &texture) { +void Program::set_uniform_2dtexture(const char *name, const renderer::Texture &texture) { this->use(); // set the sampler "value" to the texture slot id. diff --git a/libopenage/renderer/opengl/program.h b/libopenage/renderer/opengl/program.h index bd27d09530..104b9d48d0 100644 --- a/libopenage/renderer/opengl/program.h +++ b/libopenage/renderer/opengl/program.h @@ -72,7 +72,7 @@ class Program : public renderer::Program { /** * Set 2d texture data. */ - void set_uniform_2dtexture(const char *name, renderer::Texture &value) override; + void set_uniform_2dtexture(const char *name, const renderer::Texture &value) override; protected: diff --git a/libopenage/renderer/opengl/texture.cpp b/libopenage/renderer/opengl/texture.cpp index 51c9e32518..f78c08a386 100644 --- a/libopenage/renderer/opengl/texture.cpp +++ b/libopenage/renderer/opengl/texture.cpp @@ -57,7 +57,7 @@ Texture::~Texture() { glDeleteTextures(1, &this->id); } -void Texture::bind_to(int slot) { +void Texture::bind_to(int slot) const { glActiveTexture(GL_TEXTURE0 + slot); glBindTexture(GL_TEXTURE_2D, this->id); } diff --git a/libopenage/renderer/opengl/texture.h b/libopenage/renderer/opengl/texture.h index 2bc4dcc5b9..a84a36131e 100644 --- a/libopenage/renderer/opengl/texture.h +++ b/libopenage/renderer/opengl/texture.h @@ -22,7 +22,7 @@ class Texture : public renderer::Texture { /** * Bind this texture to the given slot id. */ - void bind_to(int slot) override; + void bind_to(int slot) const override; protected: /** diff --git a/libopenage/renderer/pipeline.cpp b/libopenage/renderer/pipeline.cpp index 425d9c1a0c..fa6bcbab3e 100644 --- a/libopenage/renderer/pipeline.cpp +++ b/libopenage/renderer/pipeline.cpp @@ -9,6 +9,33 @@ namespace openage { namespace renderer { +PipelineVariable::PipelineVariable(Program *program) + : + program{program} { +} + +PipelineVariable::~PipelineVariable() {} + +void PipelineVariable::set_program(Program *program) { + this->program = program; +} + +void PipelineVariable::set_name(const std::string &name) { + this->name = name; +} + + +template<> +void Uniform::set_impl(const Texture &value) { + this->program->set_uniform_2dtexture(this->name.c_str(), value); +} + +template<> +void Uniform::set_impl(const int &value) { + this->program->set_uniform_1i(this->name.c_str(), value); +} + + Pipeline::Pipeline(Program *prg) : program{prg} { @@ -20,6 +47,7 @@ Pipeline::~Pipeline() {} void Pipeline::add_var(const std::string &name, PipelineVariable &var) { // just add the variable to the known list this->variables[name] = &var; + var.set_program(this->program); } diff --git a/libopenage/renderer/pipeline.h b/libopenage/renderer/pipeline.h index f60c6045cd..0953035c84 100644 --- a/libopenage/renderer/pipeline.h +++ b/libopenage/renderer/pipeline.h @@ -7,6 +7,8 @@ #include "program.h" +#include "../error/error.h" +#include "../util/compiler.h" namespace openage { namespace renderer { @@ -15,6 +17,30 @@ namespace renderer { * A pipeline property. Wraps GPU state to be set. */ class PipelineVariable { +public: + PipelineVariable(Program *program); + virtual ~PipelineVariable(); + + /** + * Assign this uniform to the given pipeline program. + */ + void set_program(Program *program); + + /** + * Set the shader-defined uniform variable name. + */ + void set_name(const std::string &name); + +protected: + /** + * The associated gpu program. + */ + Program *program; + + /** + * Shader variable name. + */ + std::string name; }; @@ -26,26 +52,25 @@ class Uniform : public PipelineVariable { public: Uniform(Program *program=nullptr) : - program{program} { + PipelineVariable{program} { } virtual ~Uniform() {}; - void set_name(const std::string &name) { - this->name = name; - } - - void set(const T &value); - - void set_program(Program *program) { - this->program = program; + /** + * set the uniform value to the gpu. + */ + void set(const T &value) { + if (unlikely(this->program == nullptr)) { + throw error::Error(MSG(err) << "can't set uniform when its program is unset."); + } + this->set_impl(value); } - void apply(); - -protected: - std::string name; - Program *program; + /** + * Per-type specialization of how to set the uniform value. + */ + void set_impl(const T &value); }; @@ -60,7 +85,10 @@ class Uniform : public PipelineVariable { template class Attribute : public PipelineVariable { public: - Attribute() {}; + Attribute(Program *program=nullptr) + : + PipelineVariable{program} { + } virtual ~Attribute() {}; protected: @@ -90,17 +118,6 @@ class Pipeline { */ void add_var(const std::string &name, PipelineVariable &var); - /** - * Set the name of a uniform to a given value. - * e.g. set_uniform("color", {0.0, 0.0, 1.0}); - */ - template - void set_uniform(const std::string &name, const T &value) { - Uniform u{name, this->program}; - u.set(value); - u.apply(); - } - /** * The pipeline program associated with this property definition class. */ diff --git a/libopenage/renderer/program.h b/libopenage/renderer/program.h index 202343edd9..5bd1576c1c 100644 --- a/libopenage/renderer/program.h +++ b/libopenage/renderer/program.h @@ -86,7 +86,7 @@ class Program { /** * Set 2d texture data. */ - virtual void set_uniform_2dtexture(const char *name, Texture &value) = 0; + virtual void set_uniform_2dtexture(const char *name, const Texture &value) = 0; /* ========================================== */ diff --git a/libopenage/renderer/shaders/simpletexture.h b/libopenage/renderer/shaders/simpletexture.h index 4ae7341ea3..306db1e49a 100644 --- a/libopenage/renderer/shaders/simpletexture.h +++ b/libopenage/renderer/shaders/simpletexture.h @@ -17,8 +17,8 @@ class SimpleTexturePipeline : public Pipeline { virtual ~SimpleTexturePipeline(); Uniform tex; - Attribute position; - Attribute texcoord; + Attribute> position; + Attribute> texcoord; }; diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index c2e5fef44c..ceb238bf96 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -198,10 +198,9 @@ void renderer_demo_1() { SimpleTexturePipeline tex_pipeline{simpletex.get()}; + tex_pipeline.tex.set(*gaben.get()); - simpletex->set_uniform_2dtexture("tex", *gaben.get()); - - float val = 1.0f; + float val = 0.9f; const float vpos[] = { -val, -val, .0f, 1.0f, val, -val, .0f, 1.0f, diff --git a/libopenage/renderer/texture.h b/libopenage/renderer/texture.h index 5854a99afe..a4c82e082c 100644 --- a/libopenage/renderer/texture.h +++ b/libopenage/renderer/texture.h @@ -74,7 +74,7 @@ class Texture { /** * Bind the texture to the given texture unit slot id. */ - virtual void bind_to(int slot) = 0; + virtual void bind_to(int slot) const = 0; /** * The associated graphics context. From e0afef169b2316c7b8de6d9604ccd6e1a825575e Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Fri, 18 Sep 2015 02:03:19 +0200 Subject: [PATCH 22/32] renderer: implemented vertex attribute packing and uploading --- libopenage/renderer/CMakeLists.txt | 2 + libopenage/renderer/buffer.cpp | 31 +++ libopenage/renderer/buffer.h | 70 +++++++ libopenage/renderer/context.h | 7 + libopenage/renderer/opengl/CMakeLists.txt | 2 + libopenage/renderer/opengl/buffer.cpp | 44 +++++ libopenage/renderer/opengl/buffer.h | 43 +++++ libopenage/renderer/opengl/context.cpp | 11 +- libopenage/renderer/opengl/context.h | 5 + libopenage/renderer/opengl/pipeline.cpp | 15 ++ libopenage/renderer/opengl/program.cpp | 7 +- libopenage/renderer/opengl/program.h | 9 +- libopenage/renderer/pipeline.cpp | 136 +++++++++++-- libopenage/renderer/pipeline.h | 181 +++++++++++++++--- libopenage/renderer/program.h | 17 +- libopenage/renderer/shaders/simpletexture.cpp | 9 +- libopenage/renderer/tests.cpp | 73 +++---- libopenage/renderer/vertex_buffer.cpp | 39 ++++ libopenage/renderer/vertex_buffer.h | 77 ++++++++ libopenage/renderer/window.cpp | 2 +- 20 files changed, 676 insertions(+), 104 deletions(-) create mode 100644 libopenage/renderer/buffer.cpp create mode 100644 libopenage/renderer/buffer.h create mode 100644 libopenage/renderer/opengl/buffer.cpp create mode 100644 libopenage/renderer/opengl/buffer.h create mode 100644 libopenage/renderer/opengl/pipeline.cpp create mode 100644 libopenage/renderer/vertex_buffer.cpp create mode 100644 libopenage/renderer/vertex_buffer.h diff --git a/libopenage/renderer/CMakeLists.txt b/libopenage/renderer/CMakeLists.txt index 42d37154af..2848af63eb 100644 --- a/libopenage/renderer/CMakeLists.txt +++ b/libopenage/renderer/CMakeLists.txt @@ -1,4 +1,5 @@ add_sources(libopenage + buffer.cpp color.cpp context.cpp pipeline.cpp @@ -9,6 +10,7 @@ add_sources(libopenage tests.cpp text.cpp texture.cpp + vertex_buffer.cpp window.cpp ) diff --git a/libopenage/renderer/buffer.cpp b/libopenage/renderer/buffer.cpp new file mode 100644 index 0000000000..a12a85eaca --- /dev/null +++ b/libopenage/renderer/buffer.cpp @@ -0,0 +1,31 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#include "buffer.h" + +namespace openage { +namespace renderer { + +Buffer::Buffer(Context *ctx, size_t size) + : + context{ctx}, + allocd{size} { + + if (size > 0) { + this->create(size); + } +} + +size_t Buffer::size() const { + return this->allocd; +} + +char *Buffer::get() const { + return this->buffer.get(); +} + +void Buffer::create(size_t size) { + this->buffer = std::make_unique(size); + this->allocd = size; +} + +}} // openage::renderer diff --git a/libopenage/renderer/buffer.h b/libopenage/renderer/buffer.h new file mode 100644 index 0000000000..e299d0d0a9 --- /dev/null +++ b/libopenage/renderer/buffer.h @@ -0,0 +1,70 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_BUFFER_H_ +#define OPENAGE_RENDERER_BUFFER_H_ + +#include + +namespace openage { +namespace renderer { + +class Context; + + +/** + * Context-specialized graphics system buffer. + */ +class Buffer { +public: + enum class bind_target { + vertex_attributes, + }; + + Buffer(Context *ctx, size_t size=0); + virtual ~Buffer() = default; + + Buffer(const Buffer &other) = delete; + Buffer(Buffer &&other) = delete; + Buffer &operator =(const Buffer &other) = delete; + Buffer &operator =(Buffer &&other) = delete; + + /** + * Returns the raw pointer to the buffer memory area. + */ + char *get() const; + + /** + * Create an empty buffer of the given size. + */ + void create(size_t size); + + /** + * Returns the allocated buffer size. + */ + size_t size() const; + + /** + * Uploads the current state of the buffer to the GPU. + */ + virtual void upload(bind_target target) const = 0; + + /** + * The associated graphics context. + */ + Context *const context; + +protected: + /** + * The raw buffer containing the data. + */ + std::unique_ptr buffer; + + /** + * Stores the allocated buffer size. + */ + size_t allocd; +}; + +}} // openage::renderer + +#endif diff --git a/libopenage/renderer/context.h b/libopenage/renderer/context.h index c44ce75800..e6ec507c00 100644 --- a/libopenage/renderer/context.h +++ b/libopenage/renderer/context.h @@ -11,6 +11,7 @@ namespace openage { namespace renderer { +class Buffer; class ProgramSource; class Program; class Texture; @@ -98,6 +99,12 @@ class Context { */ virtual std::unique_ptr register_program(const ProgramSource &data) = 0; + /** + * Create a buffer handle + * @returns the newly created buffer state. + */ + virtual std::unique_ptr create_buffer(size_t size=0) = 0; + /** * Resize the context because the surrounding window size was updated. */ diff --git a/libopenage/renderer/opengl/CMakeLists.txt b/libopenage/renderer/opengl/CMakeLists.txt index 54291ddf27..7937ef1e86 100644 --- a/libopenage/renderer/opengl/CMakeLists.txt +++ b/libopenage/renderer/opengl/CMakeLists.txt @@ -1,5 +1,7 @@ add_sources(libopenage + buffer.cpp context.cpp + pipeline.cpp program.cpp shader.cpp texture.cpp diff --git a/libopenage/renderer/opengl/buffer.cpp b/libopenage/renderer/opengl/buffer.cpp new file mode 100644 index 0000000000..8c257bc63e --- /dev/null +++ b/libopenage/renderer/opengl/buffer.cpp @@ -0,0 +1,44 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#include "buffer.h" + +#include + +#include "../../error/error.h" + +namespace openage { +namespace renderer { +namespace opengl { + +Buffer::Buffer(renderer::Context *ctx, size_t size) + : + renderer::Buffer{ctx, size} { + + glGenBuffers(1, &this->id); +} + +Buffer::~Buffer() { + glDeleteBuffers(1, &this->id); +} + +void Buffer::upload(bind_target target) const { + GLenum slot = this->get_target(target); + + glBindBuffer(slot, this->id); + // submit whole buffer to the GPU + // TODO: really static draw? probably not. + glBufferData(slot, this->allocd, this->get(), GL_STATIC_DRAW); + glBindBuffer(slot, 0); +} + +GLenum Buffer::get_target(bind_target target) { + switch (target) { + case bind_target::vertex_attributes: + return GL_ARRAY_BUFFER; + default: + throw Error{MSG(err) << "unimplemented buffer binding target!"}; + } +} + + +}}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/buffer.h b/libopenage/renderer/opengl/buffer.h new file mode 100644 index 0000000000..65e6f0101d --- /dev/null +++ b/libopenage/renderer/opengl/buffer.h @@ -0,0 +1,43 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_OPENGL_BUFFER_H_ +#define OPENAGE_RENDERER_OPENGL_BUFFER_H_ + +#include "../buffer.h" + +#include +#include + +namespace openage { +namespace renderer { +namespace opengl { + +/** + * Context-specialized graphics system buffer. + * + * TODO: auto-dirty flagging when modified. + */ +class Buffer : public renderer::Buffer { +public: + Buffer(renderer::Context *ctx, size_t size=0); + virtual ~Buffer(); + + Buffer(const Buffer &other) = delete; + Buffer(Buffer &&other) = delete; + Buffer &operator =(const Buffer &other) = delete; + Buffer &operator =(Buffer &&other) = delete; + + /** + * Uploads the current state of the buffer to the GPU. + */ + void upload(bind_target target) const override; + + static GLenum get_target(bind_target target); + +protected: + GLuint id; +}; + +}}} // openage::renderer::opengl + +#endif diff --git a/libopenage/renderer/opengl/context.cpp b/libopenage/renderer/opengl/context.cpp index 9059695204..bfeea9fb14 100644 --- a/libopenage/renderer/opengl/context.cpp +++ b/libopenage/renderer/opengl/context.cpp @@ -9,6 +9,7 @@ #include #include +#include "buffer.h" #include "program.h" #include "texture.h" #include "../../log/log.h" @@ -175,10 +176,16 @@ std::unique_ptr Context::register_texture(const TextureData & } std::unique_ptr Context::register_program(const ProgramSource &data) { - std::unique_ptr txt = std::make_unique(this, data); - return txt; + std::unique_ptr prg = std::make_unique(this, data); + return prg; +} + +std::unique_ptr Context::create_buffer(size_t size) { + std::unique_ptr buf = std::make_unique(this, size); + return buf; } + void Context::resize_canvas(const coord::window &new_size) { log::log(MSG(dbg) << "opengl viewport resize to " << new_size.x << "x" << new_size.y); diff --git a/libopenage/renderer/opengl/context.h b/libopenage/renderer/opengl/context.h index c198334682..3f2c76fc0b 100644 --- a/libopenage/renderer/opengl/context.h +++ b/libopenage/renderer/opengl/context.h @@ -72,6 +72,11 @@ class Context : public renderer::Context { */ std::unique_ptr register_program(const ProgramSource &data) override; + /** + * Create a opengl buffer handle + * @returns the newly created state object. + */ + std::unique_ptr create_buffer(size_t size=0) override; protected: /** diff --git a/libopenage/renderer/opengl/pipeline.cpp b/libopenage/renderer/opengl/pipeline.cpp new file mode 100644 index 0000000000..a2bcbf5977 --- /dev/null +++ b/libopenage/renderer/opengl/pipeline.cpp @@ -0,0 +1,15 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +/** @file + * OpenGL specific pipeline code. + */ + +#include "pipeline.h" + +namespace openage { +namespace renderer { +namespace opengl { + +// TODO + +}}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/program.cpp b/libopenage/renderer/opengl/program.cpp index cf34cbd1d2..30ad5e0a72 100644 --- a/libopenage/renderer/opengl/program.cpp +++ b/libopenage/renderer/opengl/program.cpp @@ -6,7 +6,7 @@ #include "program.h" #include "texture.h" - +#include "../vertex_buffer.h" #include "../../log/log.h" #include "../../error/error.h" #include "../../util/compiler.h" @@ -201,6 +201,11 @@ void Program::dump_attributes() { } +void Program::set_vertex_buffer(const VertexBuffer &buf) { + buf.upload(); +} + + void Program::set_uniform_3f(const char *name, const util::Vector<3> &value) { this->use(); GLint location = this->get_uniform_id(name); diff --git a/libopenage/renderer/opengl/program.h b/libopenage/renderer/opengl/program.h index 104b9d48d0..31a5335c6d 100644 --- a/libopenage/renderer/opengl/program.h +++ b/libopenage/renderer/opengl/program.h @@ -41,9 +41,9 @@ class Program : public renderer::Program { GLint get_uniformbuffer_id(const char *name); /** - * Return the opengl handle id for a given vertex attribute name. + * Return the opengl layout id for a given vertex attribute name. */ - GLint get_attribute_id(const char *name); + int get_attribute_id(const char *name) override; /** * Set vertex attribute with given name to have a custom id. @@ -59,6 +59,11 @@ class Program : public renderer::Program { /* ====== */ // shader variables + /** + * Upload the vertex buffer object to the gpu. + */ + void set_vertex_buffer(const VertexBuffer &buf) override; + /** * Set a 3 dimensional float vector */ diff --git a/libopenage/renderer/pipeline.cpp b/libopenage/renderer/pipeline.cpp index fa6bcbab3e..775e78bdb4 100644 --- a/libopenage/renderer/pipeline.cpp +++ b/libopenage/renderer/pipeline.cpp @@ -6,48 +6,154 @@ #include "pipeline.h" +#include "vertex_buffer.h" +#include "../util/compiler.h" + + namespace openage { namespace renderer { -PipelineVariable::PipelineVariable(Program *program) +PipelineVariable::PipelineVariable(const std::string &name, + Pipeline *pipeline) : - program{program} { + pipeline{pipeline}, + name{name} { + + pipeline->add_var(this); } PipelineVariable::~PipelineVariable() {} -void PipelineVariable::set_program(Program *program) { - this->program = program; +const std::string &PipelineVariable::get_name() { + return this->name; } -void PipelineVariable::set_name(const std::string &name) { - this->name = name; +const char *PipelineVariable::get_name_cstr() { + return this->name.c_str(); } template<> -void Uniform::set_impl(const Texture &value) { - this->program->set_uniform_2dtexture(this->name.c_str(), value); +void Uniform::set(const Texture &value) { + this->pipeline->program->set_uniform_2dtexture(this->get_name_cstr(), value); } template<> -void Uniform::set_impl(const int &value) { - this->program->set_uniform_1i(this->name.c_str(), value); +void Uniform::set(const int &value) { + this->pipeline->program->set_uniform_1i(this->get_name_cstr(), value); } Pipeline::Pipeline(Program *prg) : - program{prg} { -} + program{prg}, + buffer{prg->context} {} Pipeline::~Pipeline() {} -void Pipeline::add_var(const std::string &name, PipelineVariable &var) { +void Pipeline::add_var(PipelineVariable *var) { // just add the variable to the known list - this->variables[name] = &var; - var.set_program(this->program); + if (BaseAttribute *attr = dynamic_cast(var)) { + this->attributes.push_back(attr); + } else { + // non-attribute variable, ignore it. + } +} + + +void Pipeline::enqueue_repack() { + this->attributes_dirty = true; +} + +void Pipeline::pack_attribute_buffer() { + // determine vertex attribute buffer size + size_t buf_size = 0; + + // number of vertices configured + size_t vertex_count = 0; + + VertexBuffer *vbuf = &this->buffer; + + // we'll overwrite the whole buffer, so reset the metadata. + vbuf->reset(); + + // gather the expected buffer section and sizes. + for (auto &var : this->attributes) { + size_t entry_size = var->entry_size(); + size_t entry_count = var->entry_count(); + + // the first attribute determines the expected size. + if (unlikely(vertex_count == 0)) { + vertex_count = entry_count; + } + else if (unlikely(vertex_count != entry_count)) { + throw error::Error{MSG(err) + << "inconsistent vertex attribute count: expected " + << vertex_count << " but " << var->get_name() + << " has " << entry_count << " entries."}; + } + + // a new section in the big vbo + VertexBuffer::vbo_section section{ + var->get_attr_id(), + entry_size, buf_size}; + vbuf->add_section(section); + + buf_size += entry_size * entry_count; + } + + // allocate a buffer to hold all the values. + vbuf->alloc(buf_size); + + // pack the values to the buffer. + for (size_t i = 0; i < this->attributes.size(); i++) { + BaseAttribute *var = this->attributes[i]; + VertexBuffer::vbo_section *section = &vbuf->sections[i]; + + // store the attribute section to the buffer + char *pos = vbuf->get() + section->offset; + var->pack(pos, section->entry_width * vertex_count); + } +} + +void Pipeline::draw() { + + // opengl-pipeline: + // pack buffer. (vmethod?) + // upload buffer. + // check if attributes are active + // enable/set attributes (vmethod?) + // draw + // disable attributes + + // 0. pack buffer + if (this->attributes_dirty) { + this->pack_attribute_buffer(); + } + + // TODO: dirtying: + // upload buffer + this->program->set_vertex_buffer(this->buffer); + + // next: set attrib ptrs + // this->program->activate_vertex_buffer(this->buffer); + + /* + // attribute enabling + glBindBuffer(GL_ARRAY_BUFFER, vpos_buf); + glEnableVertexAttribArray(posattr_id); + glEnableVertexAttribArray(texcoord_id); + glVertexAttribPointer(posattr_id, 4, GL_FLOAT, GL_FALSE, 0, (void *)0); + glVertexAttribPointer(texcoord_id, 2, GL_FLOAT, GL_FALSE, 0, (void *)(4 * 4 * 6)); + + // draw call + glDrawArrays(GL_TRIANGLES, 0, 6); + + // attribute disabling + glDisableVertexAttribArray(posattr_id); + glDisableVertexAttribArray(texcoord_id); + */ } diff --git a/libopenage/renderer/pipeline.h b/libopenage/renderer/pipeline.h index 0953035c84..1d705fb7d0 100644 --- a/libopenage/renderer/pipeline.h +++ b/libopenage/renderer/pipeline.h @@ -3,39 +3,47 @@ #ifndef OPENAGE_RENDERER_PIPELINE_H_ #define OPENAGE_RENDERER_PIPELINE_H_ +#include #include +#include +#include #include "program.h" +#include "vertex_buffer.h" #include "../error/error.h" #include "../util/compiler.h" +#include "../util/vector.h" namespace openage { namespace renderer { +class Pipeline; + /** * A pipeline property. Wraps GPU state to be set. */ class PipelineVariable { public: - PipelineVariable(Program *program); + PipelineVariable(const std::string &name, + Pipeline *pipeline); virtual ~PipelineVariable(); /** - * Assign this uniform to the given pipeline program. + * Return the associated shader variable name. */ - void set_program(Program *program); + const std::string &get_name(); /** - * Set the shader-defined uniform variable name. + * Return the shader variable name as a C string. */ - void set_name(const std::string &name); + const char *get_name_cstr(); protected: /** - * The associated gpu program. + * The associated pipeline metadata container. */ - Program *program; + Pipeline *const pipeline; /** * Shader variable name. @@ -45,32 +53,60 @@ class PipelineVariable { /** - * Pipeline uniform variable, which is a global value for all shader stages. + * Pipeline uniform variable, + * which is a global value for all pipeline stages. */ template class Uniform : public PipelineVariable { public: - Uniform(Program *program=nullptr) + Uniform(const std::string &name, Pipeline *pipeline) : - PipelineVariable{program} { - } + PipelineVariable{name, pipeline} {} - virtual ~Uniform() {}; + virtual ~Uniform() = default; /** - * set the uniform value to the gpu. + * Per-type specialization of how to set the uniform value. + * Calls the backend-dependent function to push the data to the gpu. */ - void set(const T &value) { - if (unlikely(this->program == nullptr)) { - throw error::Error(MSG(err) << "can't set uniform when its program is unset."); - } - this->set_impl(value); - } + void set(const T &value); +}; + + +/** + * Boilerplate base class for templated vertex attributes. + * Stores the attributes until they are packed to a uploadable buffer. + */ +class BaseAttribute : public PipelineVariable { +public: + BaseAttribute(const std::string &name, Pipeline *pipeline) + : + PipelineVariable{name, pipeline} {} + + virtual ~BaseAttribute() = default; /** - * Per-type specialization of how to set the uniform value. + * Pack all the stored attributes to the given buffer. + * Write a maximum of n chars. + */ + virtual void pack(char *buffer, size_t n) = 0; + + /** + * Return the number of attribute entries, + * aka the number of configured vertices. */ - void set_impl(const T &value); + virtual size_t entry_count() = 0; + + /** + * Return the size of a single attribute entry. + * For a vec2 this is two floats, namely 8 chars. + */ + virtual size_t entry_size() = 0; + + /** + * Return the glsl layout id for this attribute. + */ + virtual int get_attr_id() = 0; }; @@ -81,21 +117,91 @@ class Uniform : public PipelineVariable { * All of those attribtes are merged into a single buffer on request. * The buffer merging is done in the respective context. * This buffer is then transmitted to the GPU. + * + * You may only use types for T that can be copied by memcpy to a + * char buffer. + * + * We could extend this to support any class by specializing + * the pack method for POD types and some other magic base class type + * that provides the availability of a specific packing method. */ -template -class Attribute : public PipelineVariable { +template +class Attribute : public BaseAttribute { public: - Attribute(Program *program=nullptr) + Attribute(const std::string &name, Pipeline *pipeline) : - PipelineVariable{program} { + BaseAttribute{name, pipeline}, + attr_id{-1} {} + + virtual ~Attribute() = default; + + // as we wanna copy the values to the gpu, they need to be + // easily copyable. + static_assert(std::is_pod::value, + "only plain old datatypes supported as attributes"); + + /** + * Set this attribute to some value array. + */ + void set(const std::vector &values) { + this->pipeline->enqueue_repack(); + this->values = values; + } + + /** + * Set the glsl layout position. + * if unset, determine the position automatically. + */ + void set_layout(unsigned int position) { + this->attr_id = position; + } + + /** + * return the glsl layout position. + * If it's unknown until now, determine it by querying the gpu. + */ + int get_attr_id() override { + if (unlikely(this->attr_id < 0)) { + this->attr_id = this->pipeline->program->get_attribute_id(this->get_name_cstr()); + } + + return this->attr_id; + } + + /** + * Store the data to the given buffer. + * This could be extended to also support other than POD types. + */ + void pack(char *buffer, size_t n) override { + memcpy(buffer, &this->values[0], n); + } + + /** + * Return the number of configured vertices. + */ + size_t entry_count() override { + return this->values.size(); + } + + /** + * Return the size of one vertex configuration entry. + */ + size_t entry_size() override { + return sizeof(T); } - virtual ~Attribute() {}; protected: /** - * The vertex attribute values + * The vertex attribute values to be packed and submitted. + * We need to cache the values as the buffer packing and transfer + * is done after multiple attributes were set. */ std::vector values; + + /** + * glsl layout position of this attribute. + */ + int attr_id; }; @@ -111,17 +217,30 @@ class Pipeline { Pipeline(Program *prg); virtual ~Pipeline(); -protected: /** * Add the given program variable to the list of maintained * pipeline attributes. */ - void add_var(const std::string &name, PipelineVariable &var); + void add_var(PipelineVariable *var); + + void enqueue_repack(); + + void draw(); /** * The pipeline program associated with this property definition class. */ - Program *program; + Program *const program; + +protected: + void pack_attribute_buffer(); + + VertexBuffer buffer; + + /** + * True, when the vertex attribute buffer needs repacking. + */ + bool attributes_dirty; /** * Syncs attribute entry lengths. @@ -129,7 +248,7 @@ class Pipeline { * e.g. vec3 1337 color entries require 1337 vec4 positions. * These have different per-attribute sizes but the same lengths. */ - std::unordered_map variables; + std::vector attributes; }; }} // openage::renderer diff --git a/libopenage/renderer/program.h b/libopenage/renderer/program.h index 5bd1576c1c..436199b284 100644 --- a/libopenage/renderer/program.h +++ b/libopenage/renderer/program.h @@ -15,6 +15,7 @@ namespace renderer { class Context; class ShaderSource; class Texture; +class VertexBuffer; /** @@ -59,6 +60,8 @@ class Program { public: virtual ~Program() {}; + Context *get_context(); + /** * Use this program now on the GPU. */ @@ -69,9 +72,19 @@ class Program { */ virtual void dump_attributes() = 0; + /** + * Return the glsl layout id for a given vertex attribute name. + */ + virtual int get_attribute_id(const char *name) = 0; + /* ========================================== */ // available pipeline properties + /** + * Upload a vertex buffer to the gpu + */ + virtual void set_vertex_buffer(const VertexBuffer &buf) = 0; + /** * Set a 3 dimensional float vector */ @@ -82,7 +95,6 @@ class Program { */ virtual void set_uniform_1i(const char *name, const int &value) = 0; - /** * Set 2d texture data. */ @@ -90,11 +102,10 @@ class Program { /* ========================================== */ -protected: /** * The associated context. */ - Context *context; + Context *const context; }; }} // openage::renderer diff --git a/libopenage/renderer/shaders/simpletexture.cpp b/libopenage/renderer/shaders/simpletexture.cpp index 516d8bc8d5..9d15863aae 100644 --- a/libopenage/renderer/shaders/simpletexture.cpp +++ b/libopenage/renderer/shaders/simpletexture.cpp @@ -7,11 +7,10 @@ namespace renderer { SimpleTexturePipeline::SimpleTexturePipeline(Program *prg) : - Pipeline{prg} { - - this->add_var("tex", this->tex); - this->add_var("position", this->position); - this->add_var("texcoord", this->texcoord); + Pipeline{prg}, + tex{"tex", this}, + position{"position", this}, + texcoord{"texcoord", this} { } SimpleTexturePipeline::~SimpleTexturePipeline() {} diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index ceb238bf96..bfff2d0888 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -188,7 +188,6 @@ void renderer_demo_1() { ); ProgramSource simpletex_src({&vshader_src, &fshader_src}); - std::unique_ptr simpletex = renderer.add_program(simpletex_src); simpletex->dump_attributes(); @@ -197,31 +196,6 @@ void renderer_demo_1() { std::unique_ptr gaben = renderer.add_texture(gaben_data); SimpleTexturePipeline tex_pipeline{simpletex.get()}; - - tex_pipeline.tex.set(*gaben.get()); - - float val = 0.9f; - const float vpos[] = { - -val, -val, .0f, 1.0f, - val, -val, .0f, 1.0f, - -val, val, .0f, 1.0f, - - val, -val, .0f, 1.0f, - -val, val, .0f, 1.0f, - val, val, .0f, 1.0f, - - 0.0f, 1.0f, - 1.0f, 1.0f, - 0.0f, 0.0f, - - 1.0f, 1.0f, - 0.0f, 0.0f, - 1.0f, 0.0f, - }; - - // defined in the layout - GLuint vpos_buf, posattr_id = 0, texcoord_id = 1; - GLuint vao; render_demo test1{ @@ -229,31 +203,42 @@ void renderer_demo_1() { [&](Window */*window*/) { glEnable(GL_BLEND); - glGenBuffers(1, &vpos_buf); - glBindBuffer(GL_ARRAY_BUFFER, vpos_buf); - // save vertex attributes to GPU: - glBufferData(GL_ARRAY_BUFFER, sizeof(vpos), vpos, GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - + tex_pipeline.tex.set(*gaben.get()); + tex_pipeline.position.set_layout(0); + tex_pipeline.texcoord.set_layout(1); + + float val = 0.9f; + tex_pipeline.position.set({ + -val, -val, .0f, 1.0f, + val, -val, .0f, 1.0f, + -val, val, .0f, 1.0f, + + val, -val, .0f, 1.0f, + -val, val, .0f, 1.0f, + val, val, .0f, 1.0f, + }); + + tex_pipeline.texcoord.set({ + 0.0f, 1.0f, + 1.0f, 1.0f, + 0.0f, 0.0f, + + 1.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f, + }); + + // stores all the vertex attrib state. + // pointer pos, buffer assignment glGenVertexArrays(1, &vao); - glBindVertexArray(vao); // stores all the vertex attrib state. + glBindVertexArray(vao); }, // frame [&]() { glClearColor(0.0, 0.0, 0.2, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - simpletex->use(); - - glBindBuffer(GL_ARRAY_BUFFER, vpos_buf); - glEnableVertexAttribArray(posattr_id); - glEnableVertexAttribArray(texcoord_id); - glVertexAttribPointer(posattr_id, 4, GL_FLOAT, GL_FALSE, 0, (void *)0); - glVertexAttribPointer(texcoord_id, 2, GL_FLOAT, GL_FALSE, 0, (void *)(4 * 4 * 6)); - glDrawArrays(GL_TRIANGLES, 0, 6); - - glDisableVertexAttribArray(posattr_id); - glDisableVertexAttribArray(texcoord_id); + tex_pipeline.draw(); util::gl_check_error(); }, diff --git a/libopenage/renderer/vertex_buffer.cpp b/libopenage/renderer/vertex_buffer.cpp new file mode 100644 index 0000000000..633c608d97 --- /dev/null +++ b/libopenage/renderer/vertex_buffer.cpp @@ -0,0 +1,39 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#include "vertex_buffer.h" + +#include "context.h" + +namespace openage { +namespace renderer { + +VertexBuffer::VertexBuffer(Context *ctx) { + this->buffer = ctx->create_buffer(); +} + +void VertexBuffer::upload() const { + this->buffer->upload(Buffer::bind_target::vertex_attributes); +} + +void VertexBuffer::alloc(size_t size) { + this->buffer->create(size); +} + +void VertexBuffer::reset() { + this->sections.clear(); +} + +void VertexBuffer::add_section(const vbo_section §ion) { + this->sections.push_back(section); +} + +char *VertexBuffer::get() { + return this->buffer->get(); +} + +Buffer *VertexBuffer::get_buffer() { + return this->buffer.get(); +} + + +}} // openage::renderer diff --git a/libopenage/renderer/vertex_buffer.h b/libopenage/renderer/vertex_buffer.h new file mode 100644 index 0000000000..4b5d7ee1f5 --- /dev/null +++ b/libopenage/renderer/vertex_buffer.h @@ -0,0 +1,77 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_VERTEX_BUFFER_H_ +#define OPENAGE_RENDERER_VERTEX_BUFFER_H_ + +#include "buffer.h" + +#include + +namespace openage { +namespace renderer { + +class Context; + +/** + * Represents a graphics vertex buffer. + * This can be uploaded to the GPU to provide vertex-specific variables + * in a shader. + */ +class VertexBuffer { +public: + /** + * Describes a data section in a vertex buffer. + */ + struct vbo_section { + int attr_id; //!< vertex attribute layout id. + size_t entry_width; //!< number of chars for one vertex attribute + size_t offset; //!< start offset in the buffer + }; + + VertexBuffer(Context *ctx); + virtual ~VertexBuffer() = default; + + /** + * Upload this buffer to the GPU and bind it to the + * required vertex attribute slot. + */ + void upload() const; + + /** + * Allocate a buffer of given size. + */ + void alloc(size_t size); + + /** + * Reset the metadata, leave data intact. + * To destroy the data buffer, call alloc again. + */ + void reset(); + + /** + * Add a single vertex buffer metadata entry. + */ + void add_section(const vbo_section §ion); + + /** + * Return the raw pointer to the allocated buffer. + */ + char *get(); + + /** + * Return the underlying context-specific buffer object. + */ + Buffer *get_buffer(); + + /** + * List of vertex buffer sections describing the data width and types. + */ + std::vector sections; + +protected: + std::unique_ptr buffer; +}; + +}} // openage::renderer + +#endif diff --git a/libopenage/renderer/window.cpp b/libopenage/renderer/window.cpp index 6afd1107a9..736b8d2d1d 100644 --- a/libopenage/renderer/window.cpp +++ b/libopenage/renderer/window.cpp @@ -19,7 +19,7 @@ Window::Window(const char *title) // TODO: ^ detect screen resolution and determine window size from it. // TODO: make the type user-selectable - this->context = std::move(Context::generate(context_type::autodetect)); + this->context = Context::generate(context_type::autodetect); if (SDL_Init(SDL_INIT_VIDEO) < 0) { throw Error{MSG(err) << "SDL video initialization: " << SDL_GetError()}; From 7a2405745d76003d6e0b0006e07702344fa4ff6b Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Sat, 19 Sep 2015 16:07:01 +0200 Subject: [PATCH 23/32] datastructure: implemented constexpr key-value map --- libopenage/datastructure/constexpr_map.h | 300 +++++++++++++++++++++++ libopenage/datastructure/tests.cpp | 49 +++- openage/testing/testlist.py | 1 + 3 files changed, 347 insertions(+), 3 deletions(-) create mode 100644 libopenage/datastructure/constexpr_map.h diff --git a/libopenage/datastructure/constexpr_map.h b/libopenage/datastructure/constexpr_map.h new file mode 100644 index 0000000000..8138eccfa1 --- /dev/null +++ b/libopenage/datastructure/constexpr_map.h @@ -0,0 +1,300 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_DATASTRUCTURE_CONSTEXPR_MAP_H_ +#define OPENAGE_DATASTRUCTURE_CONSTEXPR_MAP_H_ + +#include +#include + +namespace openage { +namespace datastructure { + +/** + * One entry in the lookup map. + */ +template +class ConstMapEntry { +public: + constexpr ConstMapEntry(const K &key, const V &value) + : + key{key}, + value{value} {} + + constexpr ConstMapEntry(std::pair pair) + : + key{pair.first}, + value{pair.second} {} + + /** + * Map entry key. + */ + const K key; + + /** + * Map entry value. + */ + const V value; +}; + +/** + * Compiletime generic lookup map. + * + * This is a recursive template that decreases `pos` which each + * iteration step. Each `pos` stores one map entry. + * The lookup recursively descends those entries down to the zero-element. + * + * If you experience compiler errors, make sure you request _existing_ keys. + * We intentionally trigger compiler failures when a key doesn't exist. + * + * Messages include: "error: ‘*0u’ is not a constant expression" + * -> nonexistant key + */ +template +class ConstMap { +public: + template + constexpr ConstMap(Head head, Tail... tail) + : + value{head}, + tail{tail...} {} + + /** + * Return the number of entries in this map. + */ + constexpr int inline size() const { + return this->has_duplicates(), this->position(); + } + + /** + * Tests if the given key is in this map. + */ + constexpr bool inline contains(const K &key) const { + return this->has_duplicates(), this->has_entry(key); + } + + /** + * Return the stored value for the given key. + */ + constexpr const inline V &get(const K &key) const { + return this->has_duplicates(), this->must_contain(key), this->fetch(key); + } + + /** + * Access entries by map[key]. + */ + constexpr const inline V &operator [](const K &key) const { + return this->get(key); + } + +protected: + /** + * Called at compiletime when a key was used multiple times. + */ + const V &has_duplicate_key() const { + // duplicate key in map! + return *this->null; + } + + /** + * Called when a requested key is missing. + */ + constexpr bool missing_key() const { + // the requested key is not in the map! + return *this->null; + } + + /** + * Verifies that this entry's key is not used in following entries, + * then continues checking with the next entry checking for the same. + */ + constexpr bool inline all_are_unique() const { + return this->is_unique() and this->tail.all_are_unique(); + } + + /** + * Test if the key of this entry is not used in following entries. + */ + constexpr bool inline is_unique() const { + return not this->tail.has_entry(this->value.key); + } + + /** + * Returns the current entry position. + */ + constexpr int inline position() const { + return pos; + } + + /** + * Test if if the given key is stored in this element or in following elements. + */ + constexpr bool inline has_entry(const K &key) const { + return (this->value.key == key) or this->tail.has_entry(key); + } + + /** + * Returns the value matching key from this or following elements. + */ + constexpr const inline V &fetch(const K &key) const { + return + // if we found the key + (this->value.key == key) ? + // return the value: + this->value.value + : + // else try the lookup on the remaining elements: + this->tail.fetch(key); + } + + /** + * Abort when the requested key is not in the map. + */ + constexpr bool inline must_contain(const K &key) const { + return + this->has_entry(key) ? + true + : + // the requested key is not in the map + this->missing_key(); + } + + /** + * Abort when the map uses the same key more than once. + */ + constexpr bool inline has_duplicates() const { + return + this->all_are_unique() ? + true + : + this->has_duplicate_key(); + } + + /** + * The entry associated with this map position. + */ + const ConstMapEntry value; + + /** + * The remaining map entries. + */ + const ConstMap tail; + + /** + * Nullptr to induce compiler failures. + */ + static constexpr const V *null = nullptr; + + /** + * The previous entry is our friend. + */ + friend class ConstMap; +}; + + +/** + * constexpr map end container. + * When this element is reached, all contained entries have been processed. + * + * For example, when searching a key and calling to this class means + * the key was not found. + */ +template +class ConstMap { +public: + constexpr ConstMap() {} + + /** + * The size of the end element. + */ + constexpr int inline size() const { + return this->position(); + } + + /** + * The end element can't contain the key. Returns false. + */ + constexpr bool inline contains(const K &key) const { + return this->has_entry(key); + } + + /** + * The end element doesn't contain a value. Aborts. + */ + constexpr const inline V &get(const K &key) const { + return this->fetch(key); + } + +protected: + /** + * A key is not in the map. + * This function should never be called. If it is called, + * you requested a nonexisting key. + */ + constexpr const V &missing_key() const { + // intentional failure. + return *this->null; + } + + /** + * Reaching the last entry of the uniqueness check means + * we had no duplicates. + */ + constexpr bool inline all_are_unique() const { + return this->is_unique(); + } + + /** + * The end element doesn't have a key, so it's unique. + */ + constexpr bool inline is_unique() const { + return true; + } + + /** + * The end element is the last entry. + */ + constexpr int inline position() const { + return 0; + } + + /** + * The end element doesn't have an entry, + * so it can't supply a requested key. + */ + constexpr bool inline has_entry(const K &) const { + return false; + } + + /** + * Fetching a value from the end element aborts as it has no entry. + */ + constexpr const inline V &fetch(const K &) const { + return this->missing_key(); + } + + /** + * Null pointer dereference helper to trigger failures. + */ + static constexpr const V *null = nullptr; + + /** + * The previous entry class is our friend. + */ + friend class ConstMap; +}; + + +/** + * Creates a compiletime lookup table from + * K to V, where all entries of K must be unique. + * + * usage: constexpr auto bla = create_const_map(entry0, entry1, ...); + */ +template +constexpr ConstMap create_const_map(Entries&&... entry) { + return ConstMap(ConstMapEntry{std::forward(entry)}...); +} + +}} // openage::datastructure + +#endif diff --git a/libopenage/datastructure/tests.cpp b/libopenage/datastructure/tests.cpp index 6fb68afeea..106d79f1e6 100644 --- a/libopenage/datastructure/tests.cpp +++ b/libopenage/datastructure/tests.cpp @@ -2,8 +2,11 @@ #include "tests.h" +#include + #include "../testing/testing.h" +#include "constexpr_map.h" #include "doubly_linked_list.h" #include "pairing_heap.h" @@ -157,6 +160,46 @@ void doubly_linked_list() { (list.size() == 0) or TESTFAIL; } -} // namespace tests -} // namespace datastructure -} // namespace openage +// exported test +void constexpr_map() { + static_assert(create_const_map().size() == 0, "wrong size"); + static_assert(create_const_map(std::make_pair(0, 42)).size() == 1, + "wrong size"); + static_assert(create_const_map(std::make_pair(0, 42), + std::make_pair(13, 37)).size() == 2, + "wrong size"); + + static_assert(not create_const_map().contains(9001), + "empty map doesn't contain anything"); + static_assert(create_const_map(std::make_pair(42, 0), + std::make_pair(13, 37)).contains(42), + "contained element missing"); + static_assert(create_const_map(std::make_pair(42, 0), + std::make_pair(13, 37)).contains(13), + "contained element missing"); + static_assert(not create_const_map(std::make_pair(42, 0), + std::make_pair(13, 37)).contains(9001), + "uncontained element seems to be contained."); + + static_assert(create_const_map(std::make_pair(42, 9001), + std::make_pair(13, 37)).get(42) == 9001, + "fetched wrong value"); + static_assert(create_const_map(std::make_pair(42, 9001), + std::make_pair(13, 37)).get(13) == 37, + "fetched wrong value"); + + constexpr auto cmap = create_const_map( + std::make_pair(0, 0), + std::make_pair(13, 37), + std::make_pair(42, 9001) + ); + + cmap.contains(0) or TESTFAIL; + not cmap.contains(18) or TESTFAIL; + + cmap.size() == 3 or TESTFAIL; + cmap.get(13) == 37 or TESTFAIL; + cmap.get(42) == 9001 or TESTFAIL; +} + +}}} // openage::datastructure::tests diff --git a/openage/testing/testlist.py b/openage/testing/testlist.py index 7835d456c6..d4bb30edc4 100644 --- a/openage/testing/testlist.py +++ b/openage/testing/testlist.py @@ -54,6 +54,7 @@ def tests_cpp(): """ yield "openage::coord::tests::coord" + yield "openage::datastructure::tests::constexpr_map" yield "openage::datastructure::tests::doubly_linked_list" yield "openage::datastructure::tests::pairing_heap" yield "openage::job::tests::test_job_manager" From 2cce2cccba33715bc58e863fcb76bdf4bdd9adf0 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Sun, 20 Sep 2015 03:56:58 +0200 Subject: [PATCH 24/32] renderer: drawing texture with pipeline abstraction --- libopenage/renderer/CMakeLists.txt | 1 + libopenage/renderer/buffer.cpp | 3 +- libopenage/renderer/buffer.h | 28 +++++- libopenage/renderer/context.h | 36 +++++++ libopenage/renderer/opengl/CMakeLists.txt | 1 + libopenage/renderer/opengl/buffer.cpp | 55 +++++++++-- libopenage/renderer/opengl/buffer.h | 19 +++- libopenage/renderer/opengl/context.cpp | 56 ++++++++--- libopenage/renderer/opengl/context.h | 11 +++ libopenage/renderer/opengl/pipeline.cpp | 5 + libopenage/renderer/opengl/pipeline.h | 2 - libopenage/renderer/opengl/program.cpp | 10 +- libopenage/renderer/opengl/program.h | 5 - libopenage/renderer/opengl/vertex_state.cpp | 89 +++++++++++++++++ libopenage/renderer/opengl/vertex_state.h | 54 ++++++++++ libopenage/renderer/pipeline.cpp | 98 +++++++------------ libopenage/renderer/pipeline.h | 64 ++++++++---- libopenage/renderer/program.h | 7 +- libopenage/renderer/shaders/simpletexture.cpp | 8 +- libopenage/renderer/shaders/simpletexture.h | 6 +- libopenage/renderer/tests.cpp | 46 +++++---- libopenage/renderer/vertex_buffer.cpp | 24 ++++- libopenage/renderer/vertex_buffer.h | 64 ++++++++++-- libopenage/renderer/vertex_state.cpp | 12 +++ libopenage/renderer/vertex_state.h | 67 +++++++++++++ libopenage/util/compiler.h | 2 +- libopenage/util/constexpr.h | 18 +++- libopenage/util/enum.h | 4 +- 28 files changed, 624 insertions(+), 171 deletions(-) create mode 100644 libopenage/renderer/opengl/vertex_state.cpp create mode 100644 libopenage/renderer/opengl/vertex_state.h create mode 100644 libopenage/renderer/vertex_state.cpp create mode 100644 libopenage/renderer/vertex_state.h diff --git a/libopenage/renderer/CMakeLists.txt b/libopenage/renderer/CMakeLists.txt index 2848af63eb..271ae46f4d 100644 --- a/libopenage/renderer/CMakeLists.txt +++ b/libopenage/renderer/CMakeLists.txt @@ -11,6 +11,7 @@ add_sources(libopenage text.cpp texture.cpp vertex_buffer.cpp + vertex_state.cpp window.cpp ) diff --git a/libopenage/renderer/buffer.cpp b/libopenage/renderer/buffer.cpp index a12a85eaca..d374b500e8 100644 --- a/libopenage/renderer/buffer.cpp +++ b/libopenage/renderer/buffer.cpp @@ -8,7 +8,8 @@ namespace renderer { Buffer::Buffer(Context *ctx, size_t size) : context{ctx}, - allocd{size} { + allocd{size}, + on_gpu{false} { if (size > 0) { this->create(size); diff --git a/libopenage/renderer/buffer.h b/libopenage/renderer/buffer.h index e299d0d0a9..1d6817e344 100644 --- a/libopenage/renderer/buffer.h +++ b/libopenage/renderer/buffer.h @@ -18,6 +18,20 @@ class Buffer { public: enum class bind_target { vertex_attributes, + element_indices, + query, + }; + + enum class usage { + static_draw, + static_read, + stream_draw, + stream_read, + stream_copy, + static_copy, + dynamic_draw, + dynamic_read, + dynamic_copy, }; Buffer(Context *ctx, size_t size=0); @@ -44,9 +58,14 @@ class Buffer { size_t size() const; /** - * Uploads the current state of the buffer to the GPU. + * Uploads the current buffer contents to the GPU. + */ + virtual void upload(bind_target target, usage usage) = 0; + + /** + * Bind this buffer to the given target. */ - virtual void upload(bind_target target) const = 0; + virtual void bind(bind_target target) const = 0; /** * The associated graphics context. @@ -63,6 +82,11 @@ class Buffer { * Stores the allocated buffer size. */ size_t allocd; + + /** + * True, when the buffer is present on the GPU. + */ + bool on_gpu; }; }} // openage::renderer diff --git a/libopenage/renderer/context.h b/libopenage/renderer/context.h index e6ec507c00..e28a51a62a 100644 --- a/libopenage/renderer/context.h +++ b/libopenage/renderer/context.h @@ -16,6 +16,7 @@ class ProgramSource; class Program; class Texture; class TextureData; +class VertexState; /** @@ -38,6 +39,30 @@ enum class context_feature { depth_test, //!< z-buffering for fragment depth tests }; + +/** + * Stores information about context capabilities and limitations. + */ +struct context_capability { + int max_vertex_attributes; + int max_texture_slots; + int max_texture_size; + + int major_version; + int minor_version; + + context_type type; +}; + + +/** + * Represents the rendering context. + * This class provides functionality that is dispatched according to the + * used backend. + * + * GPUs are state machines, this context stores/manages the state for + * the requested backend driver. + */ class Context { public: Context(context_type type); @@ -75,6 +100,12 @@ class Context { */ virtual void destroy() = 0; + /** + * Return the context properties such as max texture units, + * vertex attributes, etc. + */ + virtual context_capability get_capabilities() = 0; + /** * Enable or disable a given context feature. * @param feature the feature to modify @@ -105,6 +136,11 @@ class Context { */ virtual std::unique_ptr create_buffer(size_t size=0) = 0; + /** + * Create a vertex state tracker + */ + virtual std::unique_ptr create_vertex_state() = 0; + /** * Resize the context because the surrounding window size was updated. */ diff --git a/libopenage/renderer/opengl/CMakeLists.txt b/libopenage/renderer/opengl/CMakeLists.txt index 7937ef1e86..abff85271b 100644 --- a/libopenage/renderer/opengl/CMakeLists.txt +++ b/libopenage/renderer/opengl/CMakeLists.txt @@ -5,4 +5,5 @@ add_sources(libopenage program.cpp shader.cpp texture.cpp + vertex_state.cpp ) diff --git a/libopenage/renderer/opengl/buffer.cpp b/libopenage/renderer/opengl/buffer.cpp index 8c257bc63e..2067d852be 100644 --- a/libopenage/renderer/opengl/buffer.cpp +++ b/libopenage/renderer/opengl/buffer.cpp @@ -1,5 +1,8 @@ // Copyright 2015-2015 the openage authors. See copying.md for legal info. +#include "../../config.h" +#if WITH_OPENGL + #include "buffer.h" #include @@ -21,24 +24,62 @@ Buffer::~Buffer() { glDeleteBuffers(1, &this->id); } -void Buffer::upload(bind_target target) const { - GLenum slot = this->get_target(target); +void Buffer::upload(bind_target target, usage usage) { + GLenum gl_usage = this->get_usage(usage); + GLenum gl_slot = this->get_target(target); + + this->bind(target); + glBufferData(gl_slot, this->allocd, this->get(), gl_usage); - glBindBuffer(slot, this->id); - // submit whole buffer to the GPU - // TODO: really static draw? probably not. - glBufferData(slot, this->allocd, this->get(), GL_STATIC_DRAW); - glBindBuffer(slot, 0); + this->on_gpu = true; } +void Buffer::bind(bind_target target) const { + GLenum gl_slot = this->get_target(target); + // TODO: ask the context if already bound. + glBindBuffer(gl_slot, this->id); +} + + GLenum Buffer::get_target(bind_target target) { switch (target) { case bind_target::vertex_attributes: return GL_ARRAY_BUFFER; + case bind_target::element_indices: + return GL_ELEMENT_ARRAY_BUFFER; + case bind_target::query: + return GL_QUERY_BUFFER; default: throw Error{MSG(err) << "unimplemented buffer binding target!"}; } } +GLenum Buffer::get_usage(usage u) { + switch (u) { + case usage::static_draw: + return GL_STATIC_DRAW; + case usage::static_read: + return GL_STATIC_READ; + case usage::stream_draw: + return GL_STREAM_DRAW; + case usage::stream_read: + return GL_STREAM_READ; + case usage::stream_copy: + return GL_STREAM_COPY; + case usage::static_copy: + return GL_STATIC_COPY; + case usage::dynamic_draw: + return GL_DYNAMIC_DRAW; + case usage::dynamic_read: + return GL_DYNAMIC_READ; + case usage::dynamic_copy: + return GL_DYNAMIC_COPY; + + default: + throw Error{MSG(err) << "unimplemented buffer usage prediction!"}; + } +} }}} // openage::renderer::opengl + +#endif diff --git a/libopenage/renderer/opengl/buffer.h b/libopenage/renderer/opengl/buffer.h index 65e6f0101d..1b26c2df15 100644 --- a/libopenage/renderer/opengl/buffer.h +++ b/libopenage/renderer/opengl/buffer.h @@ -13,9 +13,7 @@ namespace renderer { namespace opengl { /** - * Context-specialized graphics system buffer. - * - * TODO: auto-dirty flagging when modified. + * OpenGL data buffer. */ class Buffer : public renderer::Buffer { public: @@ -30,10 +28,23 @@ class Buffer : public renderer::Buffer { /** * Uploads the current state of the buffer to the GPU. */ - void upload(bind_target target) const override; + void upload(bind_target target, usage usage) override; + /** + * Bind this buffer to the specified slot. + */ + void bind(bind_target target) const override; + + /** + * Fetch the OpenGL specific buffer slot identification. + */ static GLenum get_target(bind_target target); + /** + * Fetch the OpenGL specific buffer usage prediction id. + */ + static GLenum get_usage(usage usage); + protected: GLuint id; }; diff --git a/libopenage/renderer/opengl/context.cpp b/libopenage/renderer/opengl/context.cpp index bfeea9fb14..eadbf90b17 100644 --- a/libopenage/renderer/opengl/context.cpp +++ b/libopenage/renderer/opengl/context.cpp @@ -12,6 +12,7 @@ #include "buffer.h" #include "program.h" #include "texture.h" +#include "vertex_state.h" #include "../../log/log.h" #include "../../error/error.h" @@ -61,38 +62,61 @@ void Context::create(SDL_Window *window) { } void Context::setup() { + // TODO: context capability checking + context_capability caps = this->get_capabilities(); + // to quote the standard doc: 'The value gives a rough estimate of the // largest texture that the GL can handle' // -> wat? anyways, we need at least 1024x1024. - int max_texture_size; - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); - log::log(MSG(dbg) << "Maximum supported texture size: " << max_texture_size); - if (max_texture_size < 1024) { - throw Error(MSG(err) << "Maximum supported texture size too small: " << max_texture_size); + log::log(MSG(dbg) << "Maximum supported texture size: " + << caps.max_texture_size); + if (caps.max_texture_size < 1024) { + throw Error(MSG(err) << "Maximum supported texture size too small: " + << caps.max_texture_size); } - int max_texture_units; - glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_texture_units); - log::log(MSG(dbg) << "Maximum supported texture units: " << max_texture_units); - if (max_texture_units < 2) { - throw Error(MSG(err) << "Your GPU has not enough texture units: " << max_texture_units); + log::log(MSG(dbg) << "Maximum supported texture units: " + << caps.max_texture_slots); + if (caps.max_texture_slots < 2) { + throw Error(MSG(err) << "Your GPU has not enough texture units: " + << caps.max_texture_slots); } + // vsync on + // TODO: maybe move somewhere else or to the window. SDL_GL_SetSwapInterval(1); - // TODO: transform the following to this::set_feature + // TODO: move to somewhere else, not all contexts may want those: // enable alpha blending - glEnable(GL_BLEND); + this->set_feature(context_feature::blending, true); // order of drawing relevant for depth // what gets drawn last is displayed on top. - glDisable(GL_DEPTH_TEST); + this->set_feature(context_feature::depth_test, false); + // TODO: generalize like set_feature. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } + +context_capability Context::get_capabilities() { + context_capability ret; + + ret.type = this->type; + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &ret.max_texture_size); + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &ret.max_texture_slots); + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &ret.max_vertex_attributes); + + glGetIntegerv(GL_MAJOR_VERSION, &ret.major_version); + glGetIntegerv(GL_MINOR_VERSION, &ret.minor_version); + + return ret; +} + + void Context::destroy() { SDL_GL_DeleteContext(this->glcontext); } @@ -185,6 +209,12 @@ std::unique_ptr Context::create_buffer(size_t size) { return buf; } +std::unique_ptr Context::create_vertex_state() { + std::unique_ptr vstate = std::make_unique(this); + return vstate; +} + + void Context::resize_canvas(const coord::window &new_size) { log::log(MSG(dbg) << "opengl viewport resize to " << new_size.x << "x" << new_size.y); diff --git a/libopenage/renderer/opengl/context.h b/libopenage/renderer/opengl/context.h index 3f2c76fc0b..d3bce00c66 100644 --- a/libopenage/renderer/opengl/context.h +++ b/libopenage/renderer/opengl/context.h @@ -78,6 +78,17 @@ class Context : public renderer::Context { */ std::unique_ptr create_buffer(size_t size=0) override; + /** + * Create an opengl vertex state tracker + */ + std::unique_ptr create_vertex_state() override; + + + /** + * Return the available OpenGL context properties and limitations. + */ + context_capability get_capabilities() override; + protected: /** * Resize the opengl viewport. diff --git a/libopenage/renderer/opengl/pipeline.cpp b/libopenage/renderer/opengl/pipeline.cpp index a2bcbf5977..5081509da7 100644 --- a/libopenage/renderer/opengl/pipeline.cpp +++ b/libopenage/renderer/opengl/pipeline.cpp @@ -4,6 +4,9 @@ * OpenGL specific pipeline code. */ +#include "../../config.h" +#if WITH_OPENGL + #include "pipeline.h" namespace openage { @@ -13,3 +16,5 @@ namespace opengl { // TODO }}} // openage::renderer::opengl + +#endif diff --git a/libopenage/renderer/opengl/pipeline.h b/libopenage/renderer/opengl/pipeline.h index b84d495f20..ecd2964ce5 100644 --- a/libopenage/renderer/opengl/pipeline.h +++ b/libopenage/renderer/opengl/pipeline.h @@ -11,8 +11,6 @@ namespace openage { namespace renderer { namespace opengl { -// TODO - }}} // openage::renderer::opengl #endif diff --git a/libopenage/renderer/opengl/program.cpp b/libopenage/renderer/opengl/program.cpp index 30ad5e0a72..56554edff9 100644 --- a/libopenage/renderer/opengl/program.cpp +++ b/libopenage/renderer/opengl/program.cpp @@ -164,10 +164,9 @@ GLint Program::get_attribute_id(const char *name) { void Program::set_attribute_id(const char *name, GLuint id) { if (unlikely(this->is_linked)) { - // TODO: maybe enable overwriting, but after that relink the program throw Error{MSG(err) - << "assigned attribute " << name << " = " - << id << " after program was linked!", true}; + << "you assigned attribute '" << name << " = " + << id << "' after program was linked!", true}; } else { glBindAttribLocation(this->id, id, name); @@ -201,11 +200,6 @@ void Program::dump_attributes() { } -void Program::set_vertex_buffer(const VertexBuffer &buf) { - buf.upload(); -} - - void Program::set_uniform_3f(const char *name, const util::Vector<3> &value) { this->use(); GLint location = this->get_uniform_id(name); diff --git a/libopenage/renderer/opengl/program.h b/libopenage/renderer/opengl/program.h index 31a5335c6d..457ac0b560 100644 --- a/libopenage/renderer/opengl/program.h +++ b/libopenage/renderer/opengl/program.h @@ -59,11 +59,6 @@ class Program : public renderer::Program { /* ====== */ // shader variables - /** - * Upload the vertex buffer object to the gpu. - */ - void set_vertex_buffer(const VertexBuffer &buf) override; - /** * Set a 3 dimensional float vector */ diff --git a/libopenage/renderer/opengl/vertex_state.cpp b/libopenage/renderer/opengl/vertex_state.cpp new file mode 100644 index 0000000000..f5bae4a803 --- /dev/null +++ b/libopenage/renderer/opengl/vertex_state.cpp @@ -0,0 +1,89 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#include "../../config.h" +#if WITH_OPENGL + +#include "vertex_state.h" + +#include "../../error/error.h" +#include "../../log/log.h" + +namespace openage { +namespace renderer { +namespace opengl { + +VertexState::VertexState(renderer::Context *ctx) + : + renderer::VertexState{ctx} { + + glGenVertexArrays(1, &this->id); +} + +VertexState::~VertexState() { + glDeleteVertexArrays(1, &this->id); +} + +void VertexState::attach_buffer(const VertexBuffer &buf) { + // make the vao active, it will store the section assignment + this->bind(); + + // make the buffer active. + buf.upload(); + + // assign the sections, set the pointers + for (auto §ion : buf.get_sections()) { + glEnableVertexAttribArray(section.attr_id); + + GLenum type = this->get_attribute_type(section.type); + + glVertexAttribPointer( + section.attr_id, + section.dimension, + type, GL_FALSE, 0, + reinterpret_cast(section.offset) + ); + + // add to the list of active attributes + this->bound_attributes.insert(section.attr_id); + } +} + +void VertexState::detach_buffer(const VertexBuffer &buf) { + // make the vao active, it will store the section removal + this->bind(); + + // remove sections and attribute ids + for (auto §ion : buf.get_sections()) { + glDisableVertexAttribArray(section.attr_id); + + // remove from the list of active attributes + this->bound_attributes.erase(section.attr_id); + } +} + +void VertexState::bind() const { + // when binding, first enable the vao as state storage + glBindVertexArray(this->id); + + // then enable all contained attribute ids + for (auto &attr_id : this->bound_attributes) { + glEnableVertexAttribArray(attr_id); + } +} + +GLenum VertexState::get_attribute_type(vertex_attribute_type type) { + switch (type) { + case vertex_attribute_type::float_32: + return GL_FLOAT; + case vertex_attribute_type::integer_32: + return GL_INT; + + default: + throw Error{MSG(err) << "unimplemented vertex buffer data type!"}; + } +} + + +}}} // openage::renderer::opengl + +#endif diff --git a/libopenage/renderer/opengl/vertex_state.h b/libopenage/renderer/opengl/vertex_state.h new file mode 100644 index 0000000000..cf9354587c --- /dev/null +++ b/libopenage/renderer/opengl/vertex_state.h @@ -0,0 +1,54 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_OPENGL_VERTEX_STATE_H_ +#define OPENAGE_RENDERER_OPENGL_VERTEX_STATE_H_ + +#include + +#include "../vertex_buffer.h" +#include "../vertex_state.h" + +namespace openage { +namespace renderer { +namespace opengl { + +/** + * OpenGL-specific vertex array object management. + * This wraps a vertex array object (vao). + */ +class VertexState : public renderer::VertexState { +public: + VertexState(renderer::Context *ctx); + virtual ~VertexState(); + + /** + * Do attribute buffer section assignments. + * This sets pointers to the given buffer. + */ + void attach_buffer(const VertexBuffer &buf) override; + + /** + * Remove attribute buffer section assignments. + */ + void detach_buffer(const VertexBuffer &buf) override; + + /** + * Make this vertex array object the current one. + */ + void bind() const override; + + /** + * Convert the buffer section data type to the OpenGL enum. + */ + static GLenum get_attribute_type(vertex_attribute_type type); + +protected: + /** + * Internal OpenGL handle id. + */ + GLuint id; +}; + +}}} // openage::renderer::opengl + +#endif diff --git a/libopenage/renderer/pipeline.cpp b/libopenage/renderer/pipeline.cpp index 775e78bdb4..dc68812caf 100644 --- a/libopenage/renderer/pipeline.cpp +++ b/libopenage/renderer/pipeline.cpp @@ -6,9 +6,14 @@ #include "pipeline.h" +#include "context.h" #include "vertex_buffer.h" +#include "vertex_state.h" +#include "../log/log.h" #include "../util/compiler.h" +#include +#include namespace openage { namespace renderer { @@ -17,12 +22,7 @@ PipelineVariable::PipelineVariable(const std::string &name, Pipeline *pipeline) : pipeline{pipeline}, - name{name} { - - pipeline->add_var(this); -} - -PipelineVariable::~PipelineVariable() {} + name{name} {} const std::string &PipelineVariable::get_name() { return this->name; @@ -46,41 +46,50 @@ void Uniform::set(const int &value) { Pipeline::Pipeline(Program *prg) : - program{prg}, - buffer{prg->context} {} - -Pipeline::~Pipeline() {} + program{prg} {} void Pipeline::add_var(PipelineVariable *var) { - // just add the variable to the known list + log::log(MSG(dbg) << "registering pipeline variable '" + << var->get_name() << "'"); + + log::log(MSG(dbg) << " has type: " << util::demangle(typeid(*var).name())); + if (BaseAttribute *attr = dynamic_cast(var)) { + // just add the variable to the known list + log::log(MSG(dbg) << " adding vertex attribute"); + this->attributes.push_back(attr); } else { // non-attribute variable, ignore it. + log::log(MSG(dbg) << " ignoring"); } } -void Pipeline::enqueue_repack() { - this->attributes_dirty = true; +VertexBuffer Pipeline::create_attribute_buffer() { + // create a fresh buffer + VertexBuffer vbuf{this->program->context}; + + // fill the buffer with the current vertex data. + this->update_buffer(&vbuf); + + return std::move(vbuf); } -void Pipeline::pack_attribute_buffer() { + +void Pipeline::update_buffer(VertexBuffer *vbuf) { // determine vertex attribute buffer size size_t buf_size = 0; // number of vertices configured size_t vertex_count = 0; - VertexBuffer *vbuf = &this->buffer; - // we'll overwrite the whole buffer, so reset the metadata. vbuf->reset(); // gather the expected buffer section and sizes. for (auto &var : this->attributes) { - size_t entry_size = var->entry_size(); size_t entry_count = var->entry_count(); // the first attribute determines the expected size. @@ -94,67 +103,32 @@ void Pipeline::pack_attribute_buffer() { << " has " << entry_count << " entries."}; } - // a new section in the big vbo + // a new section in the big vertex buffer VertexBuffer::vbo_section section{ var->get_attr_id(), - entry_size, buf_size}; + var->type(), + var->dimension(), + buf_size // current buffer size, increased for each section. + }; vbuf->add_section(section); - buf_size += entry_size * entry_count; + buf_size += var->entry_size() * entry_count; } // allocate a buffer to hold all the values. vbuf->alloc(buf_size); + auto sections = vbuf->get_sections(); + // pack the values to the buffer. for (size_t i = 0; i < this->attributes.size(); i++) { BaseAttribute *var = this->attributes[i]; - VertexBuffer::vbo_section *section = &vbuf->sections[i]; + VertexBuffer::vbo_section *section = §ions[i]; // store the attribute section to the buffer char *pos = vbuf->get() + section->offset; - var->pack(pos, section->entry_width * vertex_count); + var->pack(pos, var->entry_size() * vertex_count); } } -void Pipeline::draw() { - - // opengl-pipeline: - // pack buffer. (vmethod?) - // upload buffer. - // check if attributes are active - // enable/set attributes (vmethod?) - // draw - // disable attributes - - // 0. pack buffer - if (this->attributes_dirty) { - this->pack_attribute_buffer(); - } - - // TODO: dirtying: - // upload buffer - this->program->set_vertex_buffer(this->buffer); - - // next: set attrib ptrs - // this->program->activate_vertex_buffer(this->buffer); - - /* - // attribute enabling - glBindBuffer(GL_ARRAY_BUFFER, vpos_buf); - glEnableVertexAttribArray(posattr_id); - glEnableVertexAttribArray(texcoord_id); - glVertexAttribPointer(posattr_id, 4, GL_FLOAT, GL_FALSE, 0, (void *)0); - glVertexAttribPointer(texcoord_id, 2, GL_FLOAT, GL_FALSE, 0, (void *)(4 * 4 * 6)); - - // draw call - glDrawArrays(GL_TRIANGLES, 0, 6); - - // attribute disabling - glDisableVertexAttribArray(posattr_id); - glDisableVertexAttribArray(texcoord_id); - */ -} - - }} // openage::renderer diff --git a/libopenage/renderer/pipeline.h b/libopenage/renderer/pipeline.h index 1d705fb7d0..97df016a81 100644 --- a/libopenage/renderer/pipeline.h +++ b/libopenage/renderer/pipeline.h @@ -13,6 +13,7 @@ #include "../error/error.h" #include "../util/compiler.h" +#include "../util/constexpr.h" #include "../util/vector.h" namespace openage { @@ -27,7 +28,7 @@ class PipelineVariable { public: PipelineVariable(const std::string &name, Pipeline *pipeline); - virtual ~PipelineVariable(); + virtual ~PipelineVariable() = default; /** * Return the associated shader variable name. @@ -98,11 +99,22 @@ class BaseAttribute : public PipelineVariable { virtual size_t entry_count() = 0; /** - * Return the size of a single attribute entry. - * For a vec2 this is two floats, namely 8 chars. + * Return the size in chars of one attribute entry. + * This equals dimension() * sizeof(attr_type) */ virtual size_t entry_size() = 0; + /** + * Return the dimension of a single attribute entry. + * For a vec2 this is two. + */ + virtual size_t dimension() = 0; + + /** + * Return the vertex attribute type. + */ + virtual vertex_attribute_type type() = 0; + /** * Return the glsl layout id for this attribute. */ @@ -125,7 +137,9 @@ class BaseAttribute : public PipelineVariable { * the pack method for POD types and some other magic base class type * that provides the availability of a specific packing method. */ -template +template class Attribute : public BaseAttribute { public: Attribute(const std::string &name, Pipeline *pipeline) @@ -144,7 +158,6 @@ class Attribute : public BaseAttribute { * Set this attribute to some value array. */ void set(const std::vector &values) { - this->pipeline->enqueue_repack(); this->values = values; } @@ -184,10 +197,25 @@ class Attribute : public BaseAttribute { } /** - * Return the size of one vertex configuration entry. + * Compute the size of a full vertex attribute data value. + * Equals dimensions * sizeof(entry_type) */ size_t entry_size() override { - return sizeof(T); + return this->dimension() * util::compiletime(); + } + + /** + * Return the dimension of one vertex configuration entry. + */ + size_t dimension() override { + return dimensions; + } + + /** + * Provide the type of one component of one vertex attribute entry. + */ + vertex_attribute_type type() override { + return attribute_type; } protected: @@ -215,7 +243,7 @@ class Attribute : public BaseAttribute { class Pipeline { public: Pipeline(Program *prg); - virtual ~Pipeline(); + virtual ~Pipeline() = default; /** * Add the given program variable to the list of maintained @@ -223,9 +251,17 @@ class Pipeline { */ void add_var(PipelineVariable *var); - void enqueue_repack(); + /** + * Create a vertex buffer that stores the attribute + * values set in this pipeline. + */ + VertexBuffer create_attribute_buffer(); - void draw(); + /** + * Update the given vertex buffer with attributes set in this + * pipeline instance. + */ + void update_buffer(VertexBuffer *vbuf); /** * The pipeline program associated with this property definition class. @@ -233,14 +269,6 @@ class Pipeline { Program *const program; protected: - void pack_attribute_buffer(); - - VertexBuffer buffer; - - /** - * True, when the vertex attribute buffer needs repacking. - */ - bool attributes_dirty; /** * Syncs attribute entry lengths. diff --git a/libopenage/renderer/program.h b/libopenage/renderer/program.h index 436199b284..e14dd506cc 100644 --- a/libopenage/renderer/program.h +++ b/libopenage/renderer/program.h @@ -15,7 +15,7 @@ namespace renderer { class Context; class ShaderSource; class Texture; -class VertexBuffer; +class VertexState; /** @@ -80,11 +80,6 @@ class Program { /* ========================================== */ // available pipeline properties - /** - * Upload a vertex buffer to the gpu - */ - virtual void set_vertex_buffer(const VertexBuffer &buf) = 0; - /** * Set a 3 dimensional float vector */ diff --git a/libopenage/renderer/shaders/simpletexture.cpp b/libopenage/renderer/shaders/simpletexture.cpp index 9d15863aae..3fd75dd032 100644 --- a/libopenage/renderer/shaders/simpletexture.cpp +++ b/libopenage/renderer/shaders/simpletexture.cpp @@ -11,8 +11,12 @@ SimpleTexturePipeline::SimpleTexturePipeline(Program *prg) tex{"tex", this}, position{"position", this}, texcoord{"texcoord", this} { -} -SimpleTexturePipeline::~SimpleTexturePipeline() {} + // we can't register the variables in the constructor of a + // PipelineVariable, as this would store the wrong type. + this->add_var(&this->tex); + this->add_var(&this->position); + this->add_var(&this->texcoord); +} }} // openage::renderer diff --git a/libopenage/renderer/shaders/simpletexture.h b/libopenage/renderer/shaders/simpletexture.h index 306db1e49a..d7c52fa52d 100644 --- a/libopenage/renderer/shaders/simpletexture.h +++ b/libopenage/renderer/shaders/simpletexture.h @@ -14,11 +14,11 @@ namespace renderer { class SimpleTexturePipeline : public Pipeline { public: SimpleTexturePipeline(Program *prg); - virtual ~SimpleTexturePipeline(); + virtual ~SimpleTexturePipeline() = default; Uniform tex; - Attribute> position; - Attribute> texcoord; + Attribute, vertex_attribute_type::float_32, 4> position; + Attribute, vertex_attribute_type::float_32, 2> texcoord; }; diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index bfff2d0888..0c46f766cc 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -9,6 +9,7 @@ #include "../util/opengl.h" #include "opengl/shader.h" #include "opengl/program.h" +#include "vertex_state.h" #include "window.h" #include "renderer.h" #include "shader.h" @@ -196,12 +197,17 @@ void renderer_demo_1() { std::unique_ptr gaben = renderer.add_texture(gaben_data); SimpleTexturePipeline tex_pipeline{simpletex.get()}; - GLuint vao; + + + std::unique_ptr vao = window.get_context()->create_vertex_state(); + + vao->bind(); + render_demo test1{ // init [&](Window */*window*/) { - glEnable(GL_BLEND); + log::log(MSG(dbg) << "preparing test"); tex_pipeline.tex.set(*gaben.get()); tex_pipeline.position.set_layout(0); @@ -209,36 +215,36 @@ void renderer_demo_1() { float val = 0.9f; tex_pipeline.position.set({ - -val, -val, .0f, 1.0f, - val, -val, .0f, 1.0f, - -val, val, .0f, 1.0f, + {-val, -val, .0f, 1.0f}, + {val, -val, .0f, 1.0f}, + {-val, val, .0f, 1.0f}, - val, -val, .0f, 1.0f, - -val, val, .0f, 1.0f, - val, val, .0f, 1.0f, + {val, -val, .0f, 1.0f}, + {-val, val, .0f, 1.0f}, + {val, val, .0f, 1.0f}, }); tex_pipeline.texcoord.set({ - 0.0f, 1.0f, - 1.0f, 1.0f, - 0.0f, 0.0f, + {0.0f, 1.0f}, + {1.0f, 1.0f}, + {0.0f, 0.0f}, - 1.0f, 1.0f, - 0.0f, 0.0f, - 1.0f, 0.0f, + {1.0f, 1.0f}, + {0.0f, 0.0f}, + {1.0f, 0.0f}, }); - - // stores all the vertex attrib state. - // pointer pos, buffer assignment - glGenVertexArrays(1, &vao); - glBindVertexArray(vao); }, // frame [&]() { glClearColor(0.0, 0.0, 0.2, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - tex_pipeline.draw(); + VertexBuffer vbo = tex_pipeline.create_attribute_buffer(); + vao->attach_buffer(vbo); + + glDrawArrays(GL_TRIANGLES, 0, 6); + + vao->detach_buffer(vbo); util::gl_check_error(); }, diff --git a/libopenage/renderer/vertex_buffer.cpp b/libopenage/renderer/vertex_buffer.cpp index 633c608d97..0f787d0944 100644 --- a/libopenage/renderer/vertex_buffer.cpp +++ b/libopenage/renderer/vertex_buffer.cpp @@ -7,12 +7,28 @@ namespace openage { namespace renderer { -VertexBuffer::VertexBuffer(Context *ctx) { +VertexBuffer::VertexBuffer(Context *ctx, Buffer::usage usage) + : + usage{usage} { + this->buffer = ctx->create_buffer(); } +VertexBuffer::VertexBuffer(VertexBuffer &&other) + : + sections{std::move(other.sections)}, + buffer{std::move(other.buffer)}, + usage{other.usage} {} + +const VertexBuffer &VertexBuffer::operator =(VertexBuffer &&other) { + sections = std::move(other.sections); + buffer = std::move(other.buffer); + usage = other.usage; + return *this; +} + void VertexBuffer::upload() const { - this->buffer->upload(Buffer::bind_target::vertex_attributes); + this->buffer->upload(Buffer::bind_target::vertex_attributes, this->usage); } void VertexBuffer::alloc(size_t size) { @@ -35,5 +51,9 @@ Buffer *VertexBuffer::get_buffer() { return this->buffer.get(); } +const std::vector &VertexBuffer::get_sections() const { + return this->sections; +} + }} // openage::renderer diff --git a/libopenage/renderer/vertex_buffer.h b/libopenage/renderer/vertex_buffer.h index 4b5d7ee1f5..3d200a4b03 100644 --- a/libopenage/renderer/vertex_buffer.h +++ b/libopenage/renderer/vertex_buffer.h @@ -3,15 +3,42 @@ #ifndef OPENAGE_RENDERER_VERTEX_BUFFER_H_ #define OPENAGE_RENDERER_VERTEX_BUFFER_H_ -#include "buffer.h" - #include +#include "buffer.h" +#include "../datastructure/constexpr_map.h" + namespace openage { namespace renderer { class Context; + +/** + * The data type of a vertex attribute. + */ +enum class vertex_attribute_type { + integer_8, + integer_16, + integer_32, + uinteger_8, + uinteger_16, + uinteger_32, + float_32, + fixed_16_16, +}; + +constexpr auto vertex_attribute_size = datastructure::create_const_map( + std::make_pair(vertex_attribute_type::integer_8, 1), + std::make_pair(vertex_attribute_type::integer_16, 2), + std::make_pair(vertex_attribute_type::integer_32, 4), + std::make_pair(vertex_attribute_type::uinteger_8, 1), + std::make_pair(vertex_attribute_type::uinteger_16, 2), + std::make_pair(vertex_attribute_type::uinteger_32, 4), + std::make_pair(vertex_attribute_type::float_32, 4), + std::make_pair(vertex_attribute_type::fixed_16_16, 4) +); + /** * Represents a graphics vertex buffer. * This can be uploaded to the GPU to provide vertex-specific variables @@ -19,21 +46,28 @@ class Context; */ class VertexBuffer { public: + /** * Describes a data section in a vertex buffer. */ struct vbo_section { - int attr_id; //!< vertex attribute layout id. - size_t entry_width; //!< number of chars for one vertex attribute - size_t offset; //!< start offset in the buffer + int attr_id; //!< attribute layout id. + vertex_attribute_type type; //!< attribute data type + size_t dimension; //!< attribute dimension + size_t offset; //!< start offset in the buffer }; - VertexBuffer(Context *ctx); + VertexBuffer(Context *ctx, + Buffer::usage usage=Buffer::usage::static_draw); + + VertexBuffer(VertexBuffer &&other); + const VertexBuffer &operator =(VertexBuffer &&other); + virtual ~VertexBuffer() = default; /** * Upload this buffer to the GPU and bind it to the - * required vertex attribute slot. + * required vertex attribute buffer slot. */ void upload() const; @@ -58,18 +92,32 @@ class VertexBuffer { */ char *get(); + /** + * Return the buffer metadata describing layout and position + * of buffer areas. + */ + const std::vector &get_sections() const; + /** * Return the underlying context-specific buffer object. */ Buffer *get_buffer(); +protected: /** * List of vertex buffer sections describing the data width and types. */ std::vector sections; -protected: + /** + * Associated raw gpu buffer. + */ std::unique_ptr buffer; + + /** + * The predicted buffer access method. + */ + Buffer::usage usage; }; }} // openage::renderer diff --git a/libopenage/renderer/vertex_state.cpp b/libopenage/renderer/vertex_state.cpp new file mode 100644 index 0000000000..59611c9f58 --- /dev/null +++ b/libopenage/renderer/vertex_state.cpp @@ -0,0 +1,12 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#include "vertex_state.h" + +namespace openage { +namespace renderer { + +VertexState::VertexState(Context *ctx) + : + context{ctx} {} + +}} // openage::renderer diff --git a/libopenage/renderer/vertex_state.h b/libopenage/renderer/vertex_state.h new file mode 100644 index 0000000000..bf706f5799 --- /dev/null +++ b/libopenage/renderer/vertex_state.h @@ -0,0 +1,67 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_VERTEX_STATE_H_ +#define OPENAGE_RENDERER_VERTEX_STATE_H_ + +#include +#include + +namespace openage { +namespace renderer { + +class Buffer; +class Context; +class VertexBuffer; + + +/** + * Represents a vertex buffer state. + * You can enable this vertex state context so that the + * GPU pipeline uses the state defined in here for the vertex specification. + * This class is specialized by each graphics backend. + */ +class VertexState { +public: + VertexState(Context *ctx); + + const VertexState &operator =(VertexState &&other) = delete; + const VertexState &operator =(const VertexState &other) = delete; + VertexState(const VertexState &other) = delete; + VertexState(VertexState &&other) = delete; + + virtual ~VertexState() = default; + + /** + * Attach the given vertex buffer to this vertex state. + * Buffer contents are assigned to their corresponding + * attribute layout ids. + */ + virtual void attach_buffer(const VertexBuffer &buf) = 0; + + /** + * Remove the attributes of the given vertex buffer from the + * active list. + */ + virtual void detach_buffer(const VertexBuffer &buf) = 0; + + /** + * Make this vertex state the current one. + */ + virtual void bind() const = 0; + +protected: + /** + * Associated rendering context + */ + Context *const context; + + /** + * This vertex state has data for attributes with + * layout ids stored in here. + */ + std::unordered_set bound_attributes; +}; + +}} // openage::renderer + +#endif diff --git a/libopenage/util/compiler.h b/libopenage/util/compiler.h index 957278cc36..6ef4bf8f44 100644 --- a/libopenage/util/compiler.h +++ b/libopenage/util/compiler.h @@ -2,7 +2,7 @@ #pragma once -/* +/** @file * Some general-purpose utilities related to the C++ compiler and standard * library implementations. * diff --git a/libopenage/util/constexpr.h b/libopenage/util/constexpr.h index bca775119d..e3da528989 100644 --- a/libopenage/util/constexpr.h +++ b/libopenage/util/constexpr.h @@ -8,6 +8,17 @@ namespace openage { namespace util { + +/** + * Evaluate `value` at compiletime and return it. + * This can force constexpr evaluation. + */ +template +constexpr inline T compiletime() { + return value; +} + + /** * this namespace contains constexpr functions, i.e. C++11 functions that are designed * to run at compile-time. @@ -106,7 +117,7 @@ constexpr truncated_string_literal create_truncated_string_literal(const char *s * * @param pos start checking at a certain position. for internal recursion usage only. */ -constexpr bool has_prefix(const char *str, const truncated_string_literal prefix, size_t pos = 0) { +constexpr bool has_prefix(const char *str, const truncated_string_literal prefix, size_t pos=0) { // if only c++14 had arrived a little bit earlier... return @@ -150,7 +161,4 @@ constexpr const char *strip_prefix(const char *str, const char *prefix) { return strip_prefix(str, create_truncated_string_literal(prefix)); } - -} // namespace constexpr_ -} // namespace util -} // namespace openage +}}} // openage::util::constexpr_ diff --git a/libopenage/util/enum.h b/libopenage/util/enum.h index 00f5a63e5a..e7ec64907f 100644 --- a/libopenage/util/enum.h +++ b/libopenage/util/enum.h @@ -65,13 +65,13 @@ class Enum { */ Enum() : - id{0} {}; + id{0} {} // regular low-cost copying. Enum(const Enum &other) : - id{other.id} {}; + id{other.id} {} Enum &operator =(const Enum other) { From b6c0089b8d2f75c4b16f86663c0ccb766fb7ee87 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Wed, 23 Sep 2015 02:02:42 +0200 Subject: [PATCH 25/32] util: implemented quaternion --- libopenage/unit/unit_texture.cpp | 2 +- libopenage/util/CMakeLists.txt | 2 + libopenage/util/math.h | 29 ++ libopenage/util/math_constants.h | 3 +- libopenage/util/matrix.h | 135 ++++++--- libopenage/util/quaternion.cpp | 10 + libopenage/util/quaternion.h | 438 ++++++++++++++++++++++++++++ libopenage/util/quaternion_test.cpp | 248 ++++++++++++++++ libopenage/util/vector.h | 82 ++++-- libopenage/util/vector_test.cpp | 34 ++- openage/testing/testlist.py | 1 + 11 files changed, 895 insertions(+), 89 deletions(-) create mode 100644 libopenage/util/math.h create mode 100644 libopenage/util/quaternion.cpp create mode 100644 libopenage/util/quaternion.h create mode 100644 libopenage/util/quaternion_test.cpp diff --git a/libopenage/unit/unit_texture.cpp b/libopenage/unit/unit_texture.cpp index b03aa35af1..2fefa58ab9 100644 --- a/libopenage/unit/unit_texture.cpp +++ b/libopenage/unit/unit_texture.cpp @@ -5,9 +5,9 @@ #include "../coord/phys3.h" #include "../coord/window.h" -#include "../util/math_constants.h" #include "../gamestate/game_spec.h" #include "../texture.h" +#include "../util/math_constants.h" #include "unit_texture.h" namespace openage { diff --git a/libopenage/util/CMakeLists.txt b/libopenage/util/CMakeLists.txt index 0dd345c517..5f1ba2c13b 100644 --- a/libopenage/util/CMakeLists.txt +++ b/libopenage/util/CMakeLists.txt @@ -20,6 +20,8 @@ add_sources(libopenage opengl.cpp os.cpp profiler.cpp + quaternion.cpp + quaternion_test.cpp stringformatter.cpp strings.cpp subprocess.cpp diff --git a/libopenage/util/math.h b/libopenage/util/math.h new file mode 100644 index 0000000000..759e939459 --- /dev/null +++ b/libopenage/util/math.h @@ -0,0 +1,29 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_UTIL_MATH_H_ +#define OPENAGE_UTIL_MATH_H_ + +#include + +namespace openage { +namespace math { + + +/** + * Approximate a float square root by magic. + */ +float reciprocal_sqrt(float x) { + long i; + float y, r; + + y = x * 0.5f; + i = *(long *)(&x); + i = 0x5f3759df - (i >> 1); // magic! + r = *(float *)(&i); + r = r * (1.5f - (r * r * y)); + return r; +} + +}} // openage::util + +#endif diff --git a/libopenage/util/math_constants.h b/libopenage/util/math_constants.h index e3a0f72107..32da424255 100644 --- a/libopenage/util/math_constants.h +++ b/libopenage/util/math_constants.h @@ -19,5 +19,4 @@ constexpr double INV2_SQRT_PI = 1.12837916709551257390; //!< 2/sqrt(pi) constexpr double SQRT_2 = 1.41421356237309504880; //!< sqrt(2) constexpr double INV_SQRT_2 = 0.707106781186547524401; //!< 1/sqrt(2) -} // namespace math -} // namespace openage +}} // openage::math diff --git a/libopenage/util/matrix.h b/libopenage/util/matrix.h index 7fa316d8e5..c90aa94802 100644 --- a/libopenage/util/matrix.h +++ b/libopenage/util/matrix.h @@ -2,9 +2,10 @@ #pragma once -#include -#include #include +#include +#include +#include #include #include "vector.h" @@ -20,6 +21,8 @@ class Matrix : public std::array, M> { public: static_assert(M > 0 and N > 0, "0-dimensional matrix not allowed"); + static constexpr float default_eps = 1e-5; + static constexpr size_t rows = M; static constexpr size_t cols = N; static constexpr bool is_square = (M == N); @@ -27,20 +30,23 @@ class Matrix : public std::array, M> { static constexpr bool is_column_vector = (N == 1); /** - * Default constructor + * Initialize the matrix to zeroes. */ - Matrix() = default; + Matrix() { + for (size_t i = 0; i < M; i++) { + for (size_t j = 0; j < N; j++) { + (*this)[i][j] = 0; + } + } + } - /** - * Default destructor - */ ~Matrix() = default; /** * Constructor from Vector */ - template::type> + template ::type> Matrix(const Vector &vec) { for (size_t i = 0; i < M; i++) { (*this)[i][0] = vec[i]; @@ -50,19 +56,50 @@ class Matrix : public std::array, M> { /** * Constructor with N*M values */ - template + template Matrix(T ... args) { + static_assert(sizeof...(args) == N*M, "not all values supplied"); + std::array temp{{static_cast(args)...}}; for (size_t i = 0; i < N*M; i++) { (*this)[i / (N*M)][i % (N*M)] = temp[i]; } } + /** + * Constructs the identity matrix for the current size. + */ + template ::type> + static Matrix identity() { + Matrix res; + + for (size_t i = 0; i < N; i++) { + res[i][i] = 1; + } + + return res; + } + + /** + * Test if both matrices contain the same values within epsilon. + */ + bool equals(const Matrix &other, float eps=default_eps) const { + for (size_t i = 0; i < N; i++) { + for (size_t j = 0; j < M; j++) { + if (std::abs((*this)[i][j] - other[i][j]) >= eps) { + return false; + } + } + } + return true; + } + /** * Matrix multiplication */ - template - Matrix operator*(const Matrix &other) const { + template + Matrix operator *(const Matrix &other) const { Matrix res; for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < P; j++) { @@ -78,14 +115,14 @@ class Matrix : public std::array, M> { /** * Matrix-Vector multiplication */ - Matrix operator*(const Vector &vec) const { + Matrix operator *(const Vector &vec) const { return (*this) * static_cast>(vec); } /** * Matrix addition */ - Matrix operator+(const Matrix &other) const { + Matrix operator +(const Matrix &other) const { Matrix res; for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { @@ -98,7 +135,7 @@ class Matrix : public std::array, M> { /** * Matrix subtraction */ - Matrix operator-(const Matrix &other) const { + Matrix operator -(const Matrix &other) const { Matrix res; for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { @@ -111,7 +148,7 @@ class Matrix : public std::array, M> { /** * Scalar multiplication with assignment */ - void operator*=(float other) { + void operator *=(float other) { for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { (*this)[i][j] *= other; @@ -122,7 +159,7 @@ class Matrix : public std::array, M> { /** * Scalar multiplication */ - Matrix operator*(float other) const { + Matrix operator *(float other) const { Matrix res(*this); res *= other; return res; @@ -131,7 +168,7 @@ class Matrix : public std::array, M> { /** * Scalar division with assignment */ - void operator/=(float other) { + void operator /=(float other) { for (size_t i = 0; i < M; i++) { for (size_t j = 0; j < N; j++) { (*this)[i][j] /= other; @@ -142,7 +179,7 @@ class Matrix : public std::array, M> { /** * Scalar division */ - Matrix operator/(float other) const { + Matrix operator /(float other) const { Matrix res(*this); res /= other; return res; @@ -165,7 +202,7 @@ class Matrix : public std::array, M> { * Conversion to Vector */ template::type> + typename=typename std::enable_if::type> Vector to_vector() const { auto res = Vector(); for (size_t i = 0; i < M; i++) { @@ -174,6 +211,42 @@ class Matrix : public std::array, M> { return res; } + /** + * Matrix trace: the sum of all diagonal entries + */ + template::type> + float trace() const { + float res = 0.0f; + + for (size_t i = 0; i < N; i++) { + res += (*this)[i][i]; + } + + return res; + } + + /** + * Print to output stream using '<<' + */ + friend std::ostream &operator <<(std::ostream &o, + const Matrix &mat) { + o << "("; + for (size_t j = 0; j < M-1; j++) { + o << "("; + for (size_t i = 0; i < N-1; i++) { + o << mat[j][i] << ",\t"; + } + o << mat[j][N-1] << ")"; + o << "," << std::endl << " "; + } + o << "("; + for (size_t i = 0; i < N-1; i++) { + o << mat[M-1][i] << ",\t"; + } + o << mat[M-1][N-1] << "))"; + return o; + } }; /** @@ -184,28 +257,6 @@ Matrix operator *(float a, const Matrix &mat) { return mat * a; } -/** - * Print to output stream using '<<' - */ -template -std::ostream &operator <<(std::ostream &o, const Matrix &mat) { - o << "("; - for (size_t j = 0; j < M-1; j++) { - o << "("; - for (size_t i = 0; i < N-1; i++) { - o << mat[j][i] << ",\t"; - } - o << mat[j][N-1] << ")"; - o << "," << std::endl << " "; - } - o << "("; - for (size_t i = 0; i < N-1; i++) { - o << mat[M-1][i] << ",\t"; - } - o << mat[M-1][N-1] << "))"; - return o; -} - using Matrix2 = Matrix<2, 2>; using Matrix3 = Matrix<3, 3>; using Matrix4 = Matrix<4, 4>; diff --git a/libopenage/util/quaternion.cpp b/libopenage/util/quaternion.cpp new file mode 100644 index 0000000000..47098d2694 --- /dev/null +++ b/libopenage/util/quaternion.cpp @@ -0,0 +1,10 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#include "quaternion.h" + +namespace openage { +namespace util { + +// Quaternion is all templated, so there's nothing to implement. + +}} // openage::util diff --git a/libopenage/util/quaternion.h b/libopenage/util/quaternion.h new file mode 100644 index 0000000000..dd03e29732 --- /dev/null +++ b/libopenage/util/quaternion.h @@ -0,0 +1,438 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_UTIL_QUATERNION_H_ +#define OPENAGE_UTIL_QUATERNION_H_ + +#include + +#include "matrix.h" +#include "math_constants.h" +#include "vector.h" + +#include "../error/error.h" +#include "../log/log.h" + +namespace openage { +namespace util { + +/** + * Implements Quaternions to represent 3d rotations. + * + * The 4 components stores 3 components as rotation axis, + * and one component as the rotation amount. + * + * This is mainly Ken Shoemake stuff from: + * http://www.cs.ucr.edu/~vbz/resources/quatut.pdf + * + * Also: + * From Quaternion to Matrix and Back + * J.M.P. van Waveren, id Software, 2005 + */ +template +class Quaternion { +public: + using this_type = Quaternion; + using type = T; + + static constexpr T default_eps = 1e-4; + + Quaternion(T w, T x, T y, T z) + : + w{w}, x{x}, y{y}, z{z} {} + + /** + * Create a identity quaternion. + */ + Quaternion() + : + w{1}, x{0}, y{0}, z{0} {} + + /** + * Constructs a quaternion from a rotation matrix. + * mat is assumed to be a left-matrix: + * vec_transformed = mat * vec_orig. + * + * mat can be 3x3 or 4x4. + * + * This tries to avoid float-fuckups in near-zero divides + * by using larger components first: w, then x, y, or z. + * + * trace(mat) >= 0 => |w| > 1/2 + * => as small as a largest component can be. + * else: + * max diagonal entry <=> max (|x|, |y|, |z|) + * which is larger than |w| and >= 1/2 + */ + template + Quaternion(const Matrix &mat) { + + static_assert(N == 3 or N == 4, "only 3 and 4 dimensional matrices can be converted to a quaternion!"); + + T trace = mat.trace(); + T trace_cmp = trace; + + if (N == 4) { + trace_cmp -= mat[3][3]; + } else { + trace += 1.0; + } + + if (trace_cmp > 0) { + T trace_root = std::sqrt(trace); // = 2w + this->w = trace_root * 0.5; // = w + trace_root = 0.5 / trace_root; // = 1/4w + + this->x = (mat[2][1] - mat[1][2]) * trace_root; + this->y = (mat[0][2] - mat[2][0]) * trace_root; + this->z = (mat[1][0] - mat[0][1]) * trace_root; + } + else { + // determine highest diagonal component: + int n0 = 0; + + if (mat[1][1] > mat[0][0]) { + n0 = 1; + } + + if (mat[2][2] > mat[n0][n0]) { + n0 = 2; + } + + // following indices to 0, 1, 2 + int next_index[] = {1, 2, 0}; + + // indexable pointers + T *ptrs[] = {&this->x, &this->y, &this->z}; + + int n1 = next_index[n0]; + int n2 = next_index[n1]; + + // this avoids float fuckups: + T trace_ordered = (mat[n0][n0] - (mat[n1][n1] + mat[n2][n2])); + + if (N == 4) { + trace_ordered += mat[3][3]; + } else { + trace_ordered += 1.0; + } + + T trace_root = std::sqrt(trace_ordered); + + *ptrs[n0] = trace_root * 0.5; // = w + trace_root = 0.5 / trace_root; // = 1/4w + + *ptrs[n1] = (mat[n0][n1] + mat[n1][n0]) * trace_root; + *ptrs[n2] = (mat[n2][n0] + mat[n0][n2]) * trace_root; + + this->w = (mat[n2][n1] - mat[n1][n2]) * trace_root; + } + + if (N == 4) { + // normalize the quaternion by the matrix[3][3] entry + (*this) *= 1.0 / std::sqrt(mat[3][3]); + } + } + + Quaternion(const this_type &other) = default; + Quaternion(this_type &&other) = default; + Quaternion &operator =(const this_type &other) = default; + Quaternion &operator =(this_type &&other) = default; + + virtual ~Quaternion() = default; + + /** + * Create a quaternion from a rotation in radians around a given axis. + */ + static this_type from_rad(T rad, Vector3 axis) { + T rot = rad / 2.0; + this_type q{ + std::cos(rot), + axis[0] * std::sin(rot), + axis[1] * std::sin(rot), + axis[2] * std::sin(rot) + }; + return q; + } + + /** + * Create a quaternion from a rotation in degree around a given axis. + */ + static this_type from_deg(T deg, Vector3 axis) { + return this_type::from_rad((deg * math::PI) / 180.0, axis); + } + + /** + * Perform a dot product with another quaternion. + */ + T dot(const this_type &o) const { + return this->w * o.w + this->x * o.x + this->y * o.y + this->z * o.z; + } + + /** + * Calculate the length of the quaternion. + */ + T norm() const { + return std::sqrt(this->dot(*this)); + } + + /** + * Ensure that the quaternion's length equals 1. + */ + T normalize() { + T len = this->norm(); + *this /= len; + return len; + } + + /** + * Return the normalized version of this quaternion. + */ + this_type normalized() const { + this_type q{*this}; + q.normalize(); + return q; + } + + /** + * Inverted the quaternion: + * Flip the rotation axes and scale by + * inverse sum of all components squared. + * + * inv(q)= conj(q)/(w*w + x*x + y*y + z*z) + */ + void inverse() { + T dot = this->dot(*this); + if (dot > 0.0) { + *this = this->conjugated() / dot; + } + else { + throw Error(MSG(err) << "tried inverting " << *this); + } + } + + /** + * Return the inverted quaternion. + */ + this_type inversed() const { + this_type q{*this}; + q.inverse(); + return q; + } + + /** + * Conjugate the quaternion by additive inverting + * the x, y and z components. + */ + void conjugate() { + this->x = -this->x; + this->y = -this->y; + this->z = -this->z; + } + + /** + * Return the conjugated quaternion. + */ + this_type conjugated() const { + this_type q{*this}; + q.conjugate(); + return q; + } + + /** + * Test if the rotation of both quaternions is the same. + */ + bool equals(const this_type &other, T eps=default_eps) const { + T ori = this->dot(other); + return (1 - (ori*ori)) < eps; + } + + /** + * Test if both quaternion store the same numbers. + */ + bool equals_number(const this_type &other, T eps=default_eps) const { + bool result = true; + (this->w - other.w) < eps or (result = false); + (this->x - other.x) < eps or (result = false); + (this->y - other.y) < eps or (result = false); + (this->z - other.z) < eps or (result = false); + return result; + } + + /** + * Test rotation equality with another quaternion + * with given precision in radians. + */ + bool equals_rad(const this_type &other, T rad_eps=default_eps) const { + T ori = this->dot(other); + T angle = std::acos((2.0 * (ori * ori)) - 1.0); + + return std::abs(angle) < rad_eps; + } + + /** + * Test rotation equality with another quaternion + * with given precision in degree. + */ + bool equals_deg(const this_type &other, T deg_eps=default_eps) const { + return this->equals_rad(other, (deg_eps * 180.0) / math::PI); + } + + /** + * Generate the corresponding rotation matrix. + */ + Matrix3 to_matrix() const { + T x2 = this->x * 2; + T y2 = this->y * 2; + T z2 = this->z * 2; + + T x2w = x2 * this->w; + T y2w = y2 * this->w; + T z2w = z2 * this->w; + + T x3 = x2 * this->x; + T y2x = y2 * this->x; + T z2x = z2 * this->x; + + T y3 = y2 * this->y; + T z2y = z2 * this->y; + T z3 = z2 * this->z; + + Matrix3 m{ + 1.0 - (y3 + z3), y2x - z2w, z2x + y2w, + y2x + z2w, 1.0 - (x3 + z3), z2y - x2w, + z2x - y2w, z2y + x2w, 1.0 - (x3 + y3) + }; + + return m; + } + + /** + * Transforms a vector by this quaternion. + */ + Vector3 operator *(const Vector3 &vec) const { + Vector3 axis{this->x, this->y, this->z}; + + Vector3 axis_vec_normal = axis.cross_product(vec); + Vector3 axis_vec_inplane = axis.cross_product(axis_vec_normal); + + axis_vec_normal *= 2.0f * this->w; + axis_vec_inplane *= 2.0f; + + return vec + axis_vec_normal + axis_vec_inplane; + } + + const this_type &operator +=(const this_type &other) { + this->w += other.w; + this->x += other.x; + this->y += other.y; + this->z += other.z; + + return *this; + } + + this_type operator +(const this_type &other) const { + this_type q{*this}; + q += other; + return q; + } + + const this_type &operator -=(const this_type &other) { + this->w -= other.w; + this->x -= other.x; + this->y -= other.y; + this->z -= other.z; + + return *this; + } + + this_type operator -(const this_type &other) const { + this_type q{*this}; + q -= other; + return q; + } + + const this_type &operator *=(const T &fac) { + this->w *= fac; + this->x *= fac; + this->y *= fac; + this->z *= fac; + + return *this; + } + + this_type operator *(const T &fac) const { + this_type q{*this}; + q *= fac; + return q; + } + + const this_type &operator *=(const this_type &other) { + T w_new = (this->w * other.w - this->x * other.x - + this->y * other.y - this->z * other.z); + T x_new = (this->w * other.x + this->x * other.w + + this->y * other.z - this->z * other.y); + T y_new = (this->w * other.y - this->x * other.z + + this->y * other.w + this->z * other.x); + T z_new = (this->w * other.z + this->x * other.y - + this->y * other.x + this->z * other.w); + + this->w = w_new; + this->x = x_new; + this->y = y_new; + this->z = z_new; + + return *this; + } + + this_type operator *(const this_type &other) const { + this_type q{*this}; + q *= other; + return q; + } + + const this_type &operator /=(const T &fac) { + this->w /= fac; + this->x /= fac; + this->y /= fac; + this->z /= fac; + + return *this; + } + + this_type operator /(const T &fac) const { + this_type q{*this}; + q /= fac; + return q; + } + + const this_type operator -() const { + return this_type{-this->w, -this->x, -this->y, -this->z}; + } + + bool operator ==(const this_type &other) const { + return ((this->w == other.w) and + (this->x == other.x) and + (this->y == other.y) and + (this->z == other.z)); + } + + bool operator !=(const this_type &other) const { + return not (*this == other); + } + + friend std::ostream &operator <<(std::ostream &o, const this_type &q) { + o << "Quaternion(" << q.w << ", " << q.x; + o << ", " << q.y << ", " << q.z << ")"; + return o; + } + +protected: + /** + * Stored quaternion number components. + */ + T w, x, y, z; +}; + +}} // openage::util + +#endif diff --git a/libopenage/util/quaternion_test.cpp b/libopenage/util/quaternion_test.cpp new file mode 100644 index 0000000000..880a16aba3 --- /dev/null +++ b/libopenage/util/quaternion_test.cpp @@ -0,0 +1,248 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#include "quaternion.h" + +#include + +#include "../log/log.h" +#include "../testing/testing.h" + +namespace openage { +namespace util { +namespace tests { + +void quaternion() { + { + // Quaternion construction tests + const Quaternion<> id{}; + Quaternion<> id_explicit{1.0, 0.0, 0.0, 0.0}; + + id.equals(id_explicit) or TESTFAIL; + id.equals_deg(id_explicit, 1e-5f) or TESTFAIL; + + Quaternion<> wrong{0.0, 0.0, 1.0, 0.0}; + id.equals(wrong) and TESTFAIL; + + Matrix3 id_mat3 = Matrix3::identity(); + Quaternion<> q_id_mat3{id_mat3}; + id.equals(q_id_mat3) or TESTFAIL; + + Matrix4 id_mat4 = Matrix4::identity(); + Quaternion<> q_id_mat4{id_mat4}; + id.equals(q_id_mat4) or TESTFAIL; + + Matrix4 neg_mat4{ + -1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, -1, 0, + 0, 0, 0, 1, + }; + + Quaternion<> q_neg{neg_mat4}; + Quaternion<> q_nex_exp{0, 0, 1, 0}; + + q_neg.equals(q_nex_exp) or TESTFAIL; + } + { + // member functions + Quaternion<> q0{1, 2, 3, 4}; + Quaternion<> q1{5, 6, 7, 8}; + + TESTEQUALS_FLOAT(q0.dot(q1), 5 + 12 + 21 + 32, 1e-5f); + TESTEQUALS_FLOAT(q0.norm(), 5.4772255 /*= sqrt(30) */, 1e-4f); + + Quaternion<> q2{2, 8, 4, 16}; + + Quaternion<> q2_normd = q2.normalized(); + q2_normd.equals(q2) or TESTFAIL; + + Quaternion<> q2conj_exp{2, -8, -4, -16}; + q2conj_exp.equals(q2.conjugated()) or TESTFAIL; + + Quaternion<> q2inv_exp{ + 0.0058823529411764705, + -0.023529411764705882, + -0.011764705882352941, + -0.047058823529411764 + }; + + Quaternion<> q2inv = q2.inversed(); + q2inv.normalized().equals(q2inv_exp.normalized()) or TESTFAIL; + + q2.normalize(); + Quaternion<> q2norm_exp{ + 0.10846522, 0.433860915, 0.216930457, 0.8677218312 + }; + q2norm_exp.equals(q2) or TESTFAIL; + } + { + // Operator tests. + Quaternion<> id{}; + Quaternion<> bla0{13, 37, 42, 235}; + bla0.equals(id * bla0) or TESTFAIL; + + Quaternion<> bla1{8, 16, 24, 32}; + Quaternion<> bla0_1_exp{21, 45, 50, 243}; + bla0_1_exp.equals(bla0 + bla1) or TESTFAIL; + + Quaternion<> bla1_2_exp{2, 8, 12, 16}; + bla1_2_exp.equals(bla1 / 4) or TESTFAIL; + + Quaternion<> bla1_3_exp{16, 64, 96, 128}; + bla1_3_exp.equals(bla1 * 8) or TESTFAIL; + + Quaternion<> bla1_4_exp{21, 53, 66, 267}; + bla1_4_exp.equals(bla0 + bla1) or TESTFAIL; + + Quaternion<> bla1_5_exp{5, 21, 18, 203}; + bla1_5_exp.equals(bla0 - bla1) or TESTFAIL; + + Quaternion<> bla2_exp{ + 13 * 21 - 37 * 45 - 42 * 50 - 235 * 243, + 13 * 45 + 37 * 21 + 42 * 243 - 235 * 50, + 13 * 50 - 37 * 243 + 42 * 21 + 235 * 45, + 13 * 243 + 37 * 50 - 42 * 45 + 235 * 21 + }; + bla2_exp.equals(bla0 * bla0_1_exp) or TESTFAIL; + + (bla2_exp == bla2_exp) or TESTFAIL; + (bla2_exp != bla0) or TESTFAIL; + + Quaternion<> bla0_neg_exp{-13, -37, -42, -235}; + bla0_neg_exp.equals(-bla0) or TESTFAIL; + } + { + enum class axis { + x, y, z + }; + + // Rotation tests + auto rot_mat = [&](axis a, float am, bool deg=true) -> Matrix3 { + if (deg) { + am = (am * math::PI) / 180.0; + } + + switch (a) { + + case axis::x: + return { + 1, 0, 0, + 0, std::cos(am), -std::sin(am), + 0, std::sin(am), std::cos(am) + }; + + case axis::y: + return { + std::cos(am), 0, std::sin(am), + 0, 1, 0, + -std::sin(am), 0, std::cos(am) + }; + + case axis::z: + return { + std::cos(am), -std::sin(am), 0, + std::sin(am), std::cos(am), 0, + 0, 0, 1, + }; + } + }; + + // zero rotation = identity. + Matrix3 rot = rot_mat(axis::x, 0, false); + rot.equals(Matrix3::identity()) or TESTFAIL; + Quaternion<> q_rot{rot}; + Quaternion<> q_rot_deg = Quaternion<>::from_rad(0, {1, 0, 0}); + q_rot.equals_deg(q_rot_deg) or TESTFAIL; + + // 10 rad rotation + rot = rot_mat(axis::x, 10, false); + q_rot = Quaternion<>{rot}; + q_rot_deg = Quaternion<>::from_rad(10, {1, 0, 0}); + q_rot.equals_rad(q_rot_deg) or TESTFAIL; + + // wrong 90-rad-rotation: + q_rot_deg = Quaternion<>::from_rad(90, {1, 0, 0}); + q_rot.equals_rad(q_rot_deg) and TESTFAIL; + + // 10 deg rotation + rot = rot_mat(axis::x, 10); + q_rot = Quaternion<>{rot}; + q_rot_deg = Quaternion<>::from_deg(10, {1, 0, 0}); + q_rot.equals_deg(q_rot_deg) or TESTFAIL; + + // 90 deg rotation + rot = rot_mat(axis::x, 90); + q_rot = Quaternion<>{rot}; + q_rot_deg = Quaternion<>::from_deg(90, {1, 0, 0}); + q_rot.equals_deg(q_rot_deg) or TESTFAIL; + + rot = rot_mat(axis::y, 90); + q_rot = Quaternion<>{rot}; + q_rot_deg = Quaternion<>::from_deg(90, {0, 1, 0}); + q_rot.equals_deg(q_rot_deg) or TESTFAIL; + + rot = rot_mat(axis::z, 90); + q_rot = Quaternion<>{rot}; + q_rot_deg = Quaternion<>::from_deg(90, {0, 0, 1}); + q_rot.equals_deg(q_rot_deg) or TESTFAIL; + + // -90 deg rotation + rot = rot_mat(axis::y, -90); + q_rot = Quaternion<>{rot}; + q_rot_deg = Quaternion<>::from_deg(-90, {0, 1, 0}); + q_rot.equals_deg(q_rot_deg) or TESTFAIL; + + // 45 deg rotation + rot = rot_mat(axis::z, 45); + q_rot = Quaternion<>{rot}; + q_rot_deg = Quaternion<>::from_deg(45, {0, 0, 1}); + q_rot.equals_deg(q_rot_deg) or TESTFAIL; + + // rotation combination + rot = rot_mat(axis::z, 45) * rot_mat(axis::y, 60); + q_rot = Quaternion<>{rot}; + q_rot_deg = (Quaternion<>::from_deg(45, {0, 0, 1}) * + Quaternion<>::from_deg(60, {0, 1, 0})); + q_rot.equals_deg(q_rot_deg) or TESTFAIL; + + rot = rot_mat(axis::z, 45) * + rot_mat(axis::y, 60) * + rot_mat(axis::x, -200); + q_rot = Quaternion<>{rot}; + q_rot_deg = (Quaternion<>::from_deg(45, {0, 0, 1}) * + Quaternion<>::from_deg(60, {0, 1, 0}) * + Quaternion<>::from_deg(-200, {1, 0, 0})); + q_rot.equals_deg(q_rot_deg) or TESTFAIL; + + // to_matrix tests + rot = rot_mat(axis::x, 235); + q_rot = Quaternion<>::from_deg(235, {1, 0, 0}); + rot.equals(q_rot.to_matrix()) or TESTFAIL; + + rot = rot_mat(axis::y, -55); + q_rot = Quaternion<>::from_deg(-55, {0, 1, 0}); + rot.equals(q_rot.to_matrix()) or TESTFAIL; + + rot = rot_mat(axis::z, 64); + q_rot = Quaternion<>::from_deg(64, {0, 0, 1}); + rot.equals(q_rot.to_matrix()) or TESTFAIL; + } + { + Vector3 vec{5, 0, 0}; + Vector3 turned = Quaternion<>::from_deg(180, {0, 0, 1}) * vec; + Vector3 turned_exp{-5, 0, 0}; + turned.equals(turned_exp) or TESTFAIL; + + // intentional fail: + turned_exp = Vector3{-42, -42, -42}; + turned.equals(turned_exp) and TESTFAIL; + + // another turn + vec = Vector3{1337, 42, 235}; + turned = Quaternion<>::from_deg(90, {1, 0, 0}) * vec; + turned_exp = Vector3{1337, -235, 42}; + turned.equals(turned_exp) or TESTFAIL; + } +} + +}}} // openage::util::tests diff --git a/libopenage/util/vector.h b/libopenage/util/vector.h index c688bcc8e5..c7b4583f7e 100644 --- a/libopenage/util/vector.h +++ b/libopenage/util/vector.h @@ -2,11 +2,14 @@ #pragma once -#include -#include #include +#include +#include +#include #include +#include "../log/log.h" + namespace openage { namespace util { @@ -19,13 +22,19 @@ class Vector : public std::array { static_assert(N > 0, "0-dimensional vector not allowed"); /** - * Default constructor + * Default comparison epsilon. */ - Vector() = default; + static constexpr float default_eps = 1e-4; /** - * Default destructor + * Default, random-value constructor. */ + Vector() { + for (size_t i = 0; i < N; i++) { + (*this)[i] = 0; + } + } + ~Vector() = default; /** @@ -33,8 +42,24 @@ class Vector : public std::array { */ template Vector(T ... args) - : - std::array {{static_cast(args)...}} {} + : + std::array {{static_cast(args)...}} { + + static_assert(sizeof...(args) == N, "not all values supplied."); + } + + /** + * Equality test with given precision. + */ + bool equals(const Vector &other, float eps=default_eps) { + for (size_t i = 0; i < N; i++) { + float diff = std::abs((*this)[i] - other[i]); + if (diff >= eps) { + return false; + } + } + return true; + } /** * Vector addition with assignment @@ -115,7 +140,7 @@ class Vector : public std::array { /** * Dot product of two Vectors */ - float dot_product(const Vector &other) const { + float dot(const Vector &other) const { float res = 0; for (size_t i = 0; i < N; i++) { res += (*this)[i] * other[i]; @@ -127,7 +152,7 @@ class Vector : public std::array { * Euclidian norm aka length */ float norm() const { - return sqrtf((*this).dot_product(*this)); + return std::sqrt(this->dot(*this)); } /** @@ -151,28 +176,25 @@ class Vector : public std::array { ); } -}; - -/** - * Scalar multiplication with swapped arguments - */ -template -Vector operator *(float a, const Vector &v) { - return v * a; -} + /** + * Scalar multiplication with swapped arguments + */ + friend Vector operator *(float a, const Vector &v) { + return v * a; + } -/** - * Print to output stream using '<<' - */ -template -std::ostream &operator <<(std::ostream &o, const Vector &v) { - o << "("; - for (size_t i = 0; i < N-1; i++) { - o << v[i] << ", "; - } - o << v[N-1] << ")"; - return o; -} + /** + * Print to output stream using '<<' + */ + friend std::ostream &operator <<(std::ostream &o, const Vector &v) { + o << "("; + for (size_t i = 0; i < N-1; i++) { + o << v[i] << ", "; + } + o << v[N-1] << ")"; + return o; + } +}; using Vector2 = Vector<2>; diff --git a/libopenage/util/vector_test.cpp b/libopenage/util/vector_test.cpp index 971ca364c7..760509401a 100644 --- a/libopenage/util/vector_test.cpp +++ b/libopenage/util/vector_test.cpp @@ -9,6 +9,13 @@ namespace util { namespace tests { void vector() { + { + // zero-initialization test. + Vector<5> zero_explicit{0, 0, 0, 0, 0}; + Vector<5> zero; + + zero.equals(zero_explicit) or TESTFAIL; + } { // tests in 2 dimensions. // we want to be able to reuse the variable names later. @@ -16,27 +23,27 @@ void vector() { const Vector<2> b(3.0, 4.0); Vector<2> c; + // test basic operators. c = a + b; - TESTEQUALS(c[0], 4.0); - TESTEQUALS(c[1], 6.0); + c.equals({4.0, 6.0}) or TESTFAIL; c = a - b; - TESTEQUALS(c[0], -2.0); - TESTEQUALS(c[1], -2.0); + c.equals({-2.0, -2.0}) or TESTFAIL; c = 5 * a; - TESTEQUALS(c[0], 5.0); - TESTEQUALS(c[1], 10.0); + c.equals({5.0, 10.0}) or TESTFAIL; - // division by 8 should be precise c = a / 8; - TESTEQUALS(c[0], 0.125); - TESTEQUALS(c[1], 0.25); + c.equals({0.125, 0.25}) or TESTFAIL; - TESTEQUALS(a.dot_product(b), 11); + c.equals({13, 37}) and TESTFAIL; + + // test dot product, norm and normalization. + TESTEQUALS_FLOAT(a.dot(b), 11, 1e-7); c = b; - TESTEQUALS(c.norm(), 5); + TESTEQUALS_FLOAT(c.norm(), 5, 1e-7); + c.normalize(); TESTEQUALS_FLOAT(c.norm(), 1, 1e-7); } @@ -46,9 +53,8 @@ void vector() { const Vector<3> a(1.0, 2.0, 3.0); const Vector<3> b(4.0, 5.0, 6.0); Vector<3> c = a.cross_product(b); - TESTEQUALS(c[0], -3.0); - TESTEQUALS(c[1], 6.0); - TESTEQUALS(c[2], -3.0); + + c.equals({-3.0, 6.0, -3.0}) or TESTFAIL; } } diff --git a/openage/testing/testlist.py b/openage/testing/testlist.py index d4bb30edc4..ac9abaca15 100644 --- a/openage/testing/testlist.py +++ b/openage/testing/testlist.py @@ -68,6 +68,7 @@ def tests_cpp(): yield "openage::util::tests::enum_" yield "openage::util::tests::init" yield "openage::util::tests::matrix" + yield "openage::util::tests::quaternion" yield "openage::util::tests::vector" yield "openage::input::tests::parse_event_string", "keybinds parsing" yield "openage::watch::tests::run" From 8446bd5668156fa1370459897473f0bb1e377a86 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Fri, 2 Oct 2015 17:57:06 +0200 Subject: [PATCH 26/32] renderer: made error checking context-dependent --- libopenage/engine.cpp | 5 +- libopenage/renderer/CMakeLists.txt | 1 + libopenage/renderer/context.cpp | 4 +- libopenage/renderer/context.h | 18 +++++-- libopenage/renderer/context_state.cpp | 13 +++++ libopenage/renderer/context_state.h | 30 +++++++++++ libopenage/renderer/geometry.h | 25 +++++++++ libopenage/renderer/material.h | 4 +- libopenage/renderer/object.h | 24 --------- libopenage/renderer/opengl/context.cpp | 63 ++++++++++++++++++++++ libopenage/renderer/opengl/context.h | 5 ++ libopenage/renderer/opengl/program.cpp | 5 +- libopenage/renderer/opengl/program.h | 2 +- libopenage/renderer/pipeline.cpp | 12 ++--- libopenage/renderer/pipeline.h | 4 +- libopenage/renderer/program.cpp | 12 ++++- libopenage/renderer/program.h | 11 +++- libopenage/renderer/renderer.cpp | 8 +++ libopenage/renderer/renderer.h | 5 ++ libopenage/renderer/target.h | 10 ++-- libopenage/renderer/task.cpp | 9 ++-- libopenage/renderer/task.h | 44 +++++++++------ libopenage/renderer/tests.cpp | 19 +++---- libopenage/util/CMakeLists.txt | 1 - libopenage/util/opengl.cpp | 74 -------------------------- libopenage/util/opengl.h | 16 ------ 26 files changed, 242 insertions(+), 182 deletions(-) create mode 100644 libopenage/renderer/context_state.cpp create mode 100644 libopenage/renderer/context_state.h create mode 100644 libopenage/renderer/geometry.h delete mode 100644 libopenage/renderer/object.h delete mode 100644 libopenage/util/opengl.cpp delete mode 100644 libopenage/util/opengl.h diff --git a/libopenage/engine.cpp b/libopenage/engine.cpp index 7cc3194dea..0c73ea2d6d 100644 --- a/libopenage/engine.cpp +++ b/libopenage/engine.cpp @@ -29,7 +29,6 @@ #include "texture.h" #include "util/color.h" #include "util/fps.h" -#include "util/opengl.h" #include "util/strings.h" #include "util/timer.h" @@ -357,7 +356,7 @@ void Engine::loop() { } glPopMatrix(); - util::gl_check_error(); + this->renderer->check_error(); glPushMatrix(); { // the hud coordinate system is automatically established @@ -381,7 +380,7 @@ void Engine::loop() { } glPopMatrix(); - util::gl_check_error(); + this->renderer->check_error(); this->profiler.end_measure("rendering"); diff --git a/libopenage/renderer/CMakeLists.txt b/libopenage/renderer/CMakeLists.txt index 271ae46f4d..d5832bdea9 100644 --- a/libopenage/renderer/CMakeLists.txt +++ b/libopenage/renderer/CMakeLists.txt @@ -2,6 +2,7 @@ add_sources(libopenage buffer.cpp color.cpp context.cpp + context_state.cpp pipeline.cpp program.cpp renderer.cpp diff --git a/libopenage/renderer/context.cpp b/libopenage/renderer/context.cpp index 4bfc932327..56e1fea602 100644 --- a/libopenage/renderer/context.cpp +++ b/libopenage/renderer/context.cpp @@ -20,9 +20,7 @@ namespace renderer { Context::Context(context_type type) : - type{type}, - pipeline{nullptr} { -} + type{type} {} std::unique_ptr Context::generate(context_type t) { context_type ctx_requested = t; diff --git a/libopenage/renderer/context.h b/libopenage/renderer/context.h index e28a51a62a..dd2397e6c2 100644 --- a/libopenage/renderer/context.h +++ b/libopenage/renderer/context.h @@ -6,6 +6,7 @@ #include #include +#include "context_state.h" #include "../coord/window.h" namespace openage { @@ -62,6 +63,7 @@ struct context_capability { * * GPUs are state machines, this context stores/manages the state for * the requested backend driver. + * This represents, for example: OpenGL, Vulkan, OpenGLES, WebGL, ... */ class Context { public: @@ -118,6 +120,11 @@ class Context { */ virtual void screenshot(const std::string &filename) = 0; + /** + * Perform error checking to detect backend context problems. + */ + virtual void check_error() = 0; + /** * Register some texture data to the context. * @returns the newly created Texture handle. @@ -146,6 +153,12 @@ class Context { */ void resize(const coord::window &new_size); + /** + * Context state tracking. Contains state that is common + * to all actual context backends. + */ + ContextState state; + protected: /** * Perform context-specific calls to resize the drawing canvas. @@ -161,11 +174,6 @@ class Context { * Render surface size. */ coord::window canvas_size; - - /** - * Active pipeline program. - */ - Program *pipeline; }; }} // namespace openage::renderer diff --git a/libopenage/renderer/context_state.cpp b/libopenage/renderer/context_state.cpp new file mode 100644 index 0000000000..764d6b9619 --- /dev/null +++ b/libopenage/renderer/context_state.cpp @@ -0,0 +1,13 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#include "context_state.h" + +namespace openage { +namespace renderer { + +ContextState::ContextState() + : + program{nullptr}, + vertex_state{nullptr} {} + +}} // openage::renderer diff --git a/libopenage/renderer/context_state.h b/libopenage/renderer/context_state.h new file mode 100644 index 0000000000..03c20e2c4c --- /dev/null +++ b/libopenage/renderer/context_state.h @@ -0,0 +1,30 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_CONTEXT_STATE_H_ +#define OPENAGE_RENDERER_CONTEXT_STATE_H_ + +namespace openage { +namespace renderer { + +class Program; +class VertexState; + +class ContextState { +public: + ContextState(); + ~ContextState() = default; + + /** + * Active pipeline program. + */ + Program *program; + + /** + * Active vertex size, type and buffer configuration. + */ + VertexState *vertex_state; +}; + +}} // openage::renderer + +#endif diff --git a/libopenage/renderer/geometry.h b/libopenage/renderer/geometry.h new file mode 100644 index 0000000000..88a8e18389 --- /dev/null +++ b/libopenage/renderer/geometry.h @@ -0,0 +1,25 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_GEOMETRY_H_ +#define OPENAGE_RENDERER_GEOMETRY_H_ + + +namespace openage { +namespace renderer { + +class Material; + +class Geometry { +public: + Geometry(); + virtual ~Geometry(); + +private: + std::string name; + + Material *material; +}; + +}} // openage::renderer + +#endif diff --git a/libopenage/renderer/material.h b/libopenage/renderer/material.h index 05ef164613..c750113894 100644 --- a/libopenage/renderer/material.h +++ b/libopenage/renderer/material.h @@ -13,8 +13,8 @@ namespace renderer { class Material { protected: - Texture* txt; - Program* code; + Texture *txt; + Pipeline *code; }; }} // namespace openage::renderer diff --git a/libopenage/renderer/object.h b/libopenage/renderer/object.h deleted file mode 100644 index 1e3587d7e8..0000000000 --- a/libopenage/renderer/object.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. - -#ifndef OPENAGE_RENDERER_OBJECT_H_ -#define OPENAGE_RENDERER_OBJECT_H_ - - -namespace openage { -namespace renderer { - - -class Object { -public: - Object(); - ~Object(); - -private: - std::string name; - - position -> Texture -}; -} -} - -#endif diff --git a/libopenage/renderer/opengl/context.cpp b/libopenage/renderer/opengl/context.cpp index eadbf90b17..d044d00381 100644 --- a/libopenage/renderer/opengl/context.cpp +++ b/libopenage/renderer/opengl/context.cpp @@ -194,6 +194,69 @@ void Context::screenshot(const std::string &filename) { } +void Context::check_error() { + int glerrorstate = 0; + + glerrorstate = glGetError(); + if (glerrorstate != GL_NO_ERROR) { + + const char *errormsg; + + //generate error message + switch (glerrorstate) { + case GL_INVALID_ENUM: + // An unacceptable value is specified for an enumerated argument. + // The offending command is ignored + // and has no other side effect than to set the error flag. + errormsg = "invalid enum passed to opengl call"; + break; + case GL_INVALID_VALUE: + // A numeric argument is out of range. + // The offending command is ignored + // and has no other side effect than to set the error flag. + errormsg = "invalid value passed to opengl call"; + break; + case GL_INVALID_OPERATION: + // The specified operation is not allowed in the current state. + // The offending command is ignored + // and has no other side effect than to set the error flag. + errormsg = "invalid operation performed during some state"; + break; + case GL_INVALID_FRAMEBUFFER_OPERATION: + // The framebuffer object is not complete. The offending command + // is ignored and has no other side effect than to set the error flag. + errormsg = "invalid framebuffer operation"; + break; + case GL_OUT_OF_MEMORY: + // There is not enough memory left to execute the command. + // The state of the GL is undefined, + // except for the state of the error flags, + // after this error is recorded. + errormsg = "out of memory, wtf?"; + break; + case GL_STACK_UNDERFLOW: + // An attempt has been made to perform an operation that would + // cause an internal stack to underflow. + errormsg = "stack underflow"; + break; + case GL_STACK_OVERFLOW: + // An attempt has been made to perform an operation that would + // cause an internal stack to overflow. + errormsg = "stack overflow"; + break; + default: + // unknown error state + errormsg = "unknown error"; + } + throw Error( + MSG(err) << + "OpenGL error state id: " << glerrorstate + << "\n\t" << errormsg + ); + } +} + + std::unique_ptr Context::register_texture(const TextureData &data) { std::unique_ptr txt = std::make_unique(this, data); return txt; diff --git a/libopenage/renderer/opengl/context.h b/libopenage/renderer/opengl/context.h index d3bce00c66..dac0a22b32 100644 --- a/libopenage/renderer/opengl/context.h +++ b/libopenage/renderer/opengl/context.h @@ -60,6 +60,11 @@ class Context : public renderer::Context { */ void screenshot(const std::string &filename) override; + /** + * Check if OpenGL detected any state machine errors. + */ + void check_error() override; + /** * Creates the opengl texture in this context. * @returns a handle to it. diff --git a/libopenage/renderer/opengl/program.cpp b/libopenage/renderer/opengl/program.cpp index 56554edff9..2c25d1a7ed 100644 --- a/libopenage/renderer/opengl/program.cpp +++ b/libopenage/renderer/opengl/program.cpp @@ -106,7 +106,7 @@ void Program::check(GLenum what_to_check) { } } -void Program::use() { +void Program::activate() { if (unlikely(not this->is_linked)) { throw Error{MSG(err) << "using program before it was linked!"}; } @@ -220,7 +220,8 @@ void Program::set_uniform_2dtexture(const char *name, const renderer::Texture &t // set the sampler "value" to the texture slot id. GLint location = this->get_uniform_id(name); - // TODO: use multiple slots! + // TODO: use multiple slots! use context state tracking! + // TODO: slot assignage algorithm. int slot = 0; glUniform1i(location, slot); texture.bind_to(slot); diff --git a/libopenage/renderer/opengl/program.h b/libopenage/renderer/opengl/program.h index 457ac0b560..840fb8fae4 100644 --- a/libopenage/renderer/opengl/program.h +++ b/libopenage/renderer/opengl/program.h @@ -28,7 +28,7 @@ class Program : public renderer::Program { /** * Activate the program on the GPU. */ - void use() override; + void activate() override; /** * Return the opengl handle id for a given pipeline uniform variable. diff --git a/libopenage/renderer/pipeline.cpp b/libopenage/renderer/pipeline.cpp index dc68812caf..f25b4c3453 100644 --- a/libopenage/renderer/pipeline.cpp +++ b/libopenage/renderer/pipeline.cpp @@ -50,19 +50,11 @@ Pipeline::Pipeline(Program *prg) void Pipeline::add_var(PipelineVariable *var) { - log::log(MSG(dbg) << "registering pipeline variable '" - << var->get_name() << "'"); - - log::log(MSG(dbg) << " has type: " << util::demangle(typeid(*var).name())); - if (BaseAttribute *attr = dynamic_cast(var)) { // just add the variable to the known list - log::log(MSG(dbg) << " adding vertex attribute"); - this->attributes.push_back(attr); } else { // non-attribute variable, ignore it. - log::log(MSG(dbg) << " ignoring"); } } @@ -74,7 +66,7 @@ VertexBuffer Pipeline::create_attribute_buffer() { // fill the buffer with the current vertex data. this->update_buffer(&vbuf); - return std::move(vbuf); + return vbuf; } @@ -93,6 +85,8 @@ void Pipeline::update_buffer(VertexBuffer *vbuf) { size_t entry_count = var->entry_count(); // the first attribute determines the expected size. + // all other attribute-definitions will have to provide the same + // number of entries (so that all vertices can get the attributes). if (unlikely(vertex_count == 0)) { vertex_count = entry_count; } diff --git a/libopenage/renderer/pipeline.h b/libopenage/renderer/pipeline.h index 97df016a81..515ef41261 100644 --- a/libopenage/renderer/pipeline.h +++ b/libopenage/renderer/pipeline.h @@ -151,8 +151,8 @@ class Attribute : public BaseAttribute { // as we wanna copy the values to the gpu, they need to be // easily copyable. - static_assert(std::is_pod::value, - "only plain old datatypes supported as attributes"); + static_assert(std::is_trivially_copyable::value, + "only trivially copyable types supported as attributes"); /** * Set this attribute to some value array. diff --git a/libopenage/renderer/program.cpp b/libopenage/renderer/program.cpp index 22a3adccce..aee2bede96 100644 --- a/libopenage/renderer/program.cpp +++ b/libopenage/renderer/program.cpp @@ -6,6 +6,8 @@ #include "program.h" +#include "context.h" + namespace openage { namespace renderer { @@ -31,7 +33,15 @@ void ProgramSource::attach_shader(const ShaderSource &shader) { Program::Program(Context *context) : - context{context} { + context{context} {} + + +void Program::use() { + if (this->context->state.program != this) { + this->activate(); + this->context->state.program = this; + } } + }} // openage::renderer diff --git a/libopenage/renderer/program.h b/libopenage/renderer/program.h index e14dd506cc..e16e2aab56 100644 --- a/libopenage/renderer/program.h +++ b/libopenage/renderer/program.h @@ -60,12 +60,21 @@ class Program { public: virtual ~Program() {}; + /** + * Return the associated graphics context. + */ Context *get_context(); + /** + * Try to use the program on the GPU. + * Does nothing if the context already uses this program. + */ + void use(); + /** * Use this program now on the GPU. */ - virtual void use() = 0; + virtual void activate() = 0; /** * Dump vertex attribute variables. diff --git a/libopenage/renderer/renderer.cpp b/libopenage/renderer/renderer.cpp index 49e30d32b0..4ace25bd05 100644 --- a/libopenage/renderer/renderer.cpp +++ b/libopenage/renderer/renderer.cpp @@ -3,6 +3,8 @@ #include "renderer.h" #include "context.h" +#include "program.h" +#include "texture.h" namespace openage { namespace renderer { @@ -37,6 +39,12 @@ void Renderer::screenshot(const std::string &filename) { this->context->screenshot(filename); } + +void Renderer::check_error() { + this->context->check_error(); +} + + bool Renderer::on_resize(coord::window new_size) { this->context->resize(new_size); return true; diff --git a/libopenage/renderer/renderer.h b/libopenage/renderer/renderer.h index f4a0244c11..e23104e6a7 100644 --- a/libopenage/renderer/renderer.h +++ b/libopenage/renderer/renderer.h @@ -78,6 +78,11 @@ class Renderer : public ResizeHandler { */ void screenshot(const std::string &filename); + /** + * Test if the renderer has an invalid state. + */ + void check_error(); + /** * Resize the renderer because the surrounding window size was updated. */ diff --git a/libopenage/renderer/target.h b/libopenage/renderer/target.h index 15553df732..74fa608d83 100644 --- a/libopenage/renderer/target.h +++ b/libopenage/renderer/target.h @@ -3,21 +3,19 @@ #ifndef OPENAGE_RENDERER_TARGET_H_ #define OPENAGE_RENDERER_TARGET_H_ -#include - #include "renderer.h" namespace openage { namespace renderer { /** - * Inherit from this to have methods that the renderer - * uses to fetch render tasks. + * A render output destination. */ class Target { - std::vector get_render_tasks(); +public: + // TODO. }; -}} // namespace openage::renderer +}} // openage::renderer #endif diff --git a/libopenage/renderer/task.cpp b/libopenage/renderer/task.cpp index e9b3281cef..a04d8a0656 100644 --- a/libopenage/renderer/task.cpp +++ b/libopenage/renderer/task.cpp @@ -1,7 +1,8 @@ // Copyright 2015-2015 the openage authors. See copying.md for legal info. /** @file - * common code for all render tasks. + * render task implementation to tell the renderer to + * perform any drawing on the screen. */ #include "task.h" @@ -12,11 +13,7 @@ namespace renderer { bool Task::operator <(const Task &other) const { - if (this->position < other.position) { - return true; - } else { - return false; - } + return true; } TaskState::TaskState() {} diff --git a/libopenage/renderer/task.h b/libopenage/renderer/task.h index e142400e84..fe8fd0c4c0 100644 --- a/libopenage/renderer/task.h +++ b/libopenage/renderer/task.h @@ -3,29 +3,27 @@ #ifndef OPENAGE_RENDERER_TASK_H_ #define OPENAGE_RENDERER_TASK_H_ -#include "material.h" +#include "../coord/phys3.h" +#include "../util/quaternion.h" namespace openage { namespace renderer { -/** - * render layer, their order is important. - * layers from bottom to top: later in enum = drawn later - */ -enum class layer { - terrain, - unit, - sky, - hud, -}; +class Geometry; +class Material; +class Renderer; + /** - * struct to submit to the renderer + * Instructs the renderer to draw something. */ class Task { public: - layer position; - std::vector materials; + Material *material; + Geometry *geometry; + + coord::phys3 position; + util::Quaternion<> rotation; bool operator <(const Task &other) const; }; @@ -46,7 +44,23 @@ class TaskState { private: Task *task; - class Renderer *renderer; + Renderer *renderer; +}; + +/** + * Groups some tasks together. + * The order of tasks in this group is optimized. + * + * E.g. all unit draw actions are in one group. + */ +class TaskGroup { +public: + TaskGroup(); + ~TaskGroup(); + +protected: + std::vector tasks; // heap? + size_t id; }; diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index 0c46f766cc..340b0a2b95 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -6,7 +6,6 @@ #include #include "../log/log.h" -#include "../util/opengl.h" #include "opengl/shader.h" #include "opengl/program.h" #include "vertex_state.h" @@ -14,6 +13,7 @@ #include "renderer.h" #include "shader.h" #include "shaders/simpletexture.h" +#include "texture.h" namespace openage { namespace renderer { @@ -149,7 +149,7 @@ void renderer_demo_0() { glDisableVertexAttribArray(posattr_id); - util::gl_check_error(); + renderer.check_error(); }, // resize [&](const coord::window &new_size) { @@ -180,7 +180,7 @@ void renderer_demo_1() { ShaderSourceCode fshader_src( shader_type::fragment, "#version 330\n" - "out vec4 color;\n" + "out vec4 color;" "smooth in vec2 texpos;" "uniform sampler2D tex;" "void main() {" @@ -190,19 +190,14 @@ void renderer_demo_1() { ProgramSource simpletex_src({&vshader_src, &fshader_src}); std::unique_ptr simpletex = renderer.add_program(simpletex_src); - simpletex->dump_attributes(); - FileTextureData gaben_data{"assets/gaben.png"}; - std::unique_ptr gaben = renderer.add_texture(gaben_data); - SimpleTexturePipeline tex_pipeline{simpletex.get()}; - + FileTextureData gaben_data{"assets/gaben.png"}; + std::unique_ptr gaben = renderer.add_texture(gaben_data); std::unique_ptr vao = window.get_context()->create_vertex_state(); - vao->bind(); - render_demo test1{ // init @@ -239,6 +234,8 @@ void renderer_demo_1() { glClearColor(0.0, 0.0, 0.2, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + vao->bind(); + VertexBuffer vbo = tex_pipeline.create_attribute_buffer(); vao->attach_buffer(vbo); @@ -246,7 +243,7 @@ void renderer_demo_1() { vao->detach_buffer(vbo); - util::gl_check_error(); + renderer.check_error(); }, // resize [&](const coord::window &new_size) { diff --git a/libopenage/util/CMakeLists.txt b/libopenage/util/CMakeLists.txt index 5f1ba2c13b..a2143c8e6b 100644 --- a/libopenage/util/CMakeLists.txt +++ b/libopenage/util/CMakeLists.txt @@ -17,7 +17,6 @@ add_sources(libopenage matrix.cpp matrix_test.cpp misc.cpp - opengl.cpp os.cpp profiler.cpp quaternion.cpp diff --git a/libopenage/util/opengl.cpp b/libopenage/util/opengl.cpp deleted file mode 100644 index eb8e2e6178..0000000000 --- a/libopenage/util/opengl.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2014-2016 the openage authors. See copying.md for legal info. - -#include "opengl.h" - -#include - -#include "../error/error.h" - -namespace openage { -namespace util { - -void gl_check_error() { - int glerrorstate = 0; - - glerrorstate = glGetError(); - if (glerrorstate != GL_NO_ERROR) { - - const char *errormsg; - - //generate error message - switch (glerrorstate) { - case GL_INVALID_ENUM: - // An unacceptable value is specified for an enumerated argument. - // The offending command is ignored - // and has no other side effect than to set the error flag. - errormsg = "invalid enum passed to opengl call"; - break; - case GL_INVALID_VALUE: - // A numeric argument is out of range. - // The offending command is ignored - // and has no other side effect than to set the error flag. - errormsg = "invalid value passed to opengl call"; - break; - case GL_INVALID_OPERATION: - // The specified operation is not allowed in the current state. - // The offending command is ignored - // and has no other side effect than to set the error flag. - errormsg = "invalid operation performed during some state"; - break; - case GL_INVALID_FRAMEBUFFER_OPERATION: - // The framebuffer object is not complete. The offending command - // is ignored and has no other side effect than to set the error flag. - errormsg = "invalid framebuffer operation"; - break; - case GL_OUT_OF_MEMORY: - // There is not enough memory left to execute the command. - // The state of the GL is undefined, - // except for the state of the error flags, - // after this error is recorded. - errormsg = "out of memory, wtf?"; - break; - case GL_STACK_UNDERFLOW: - // An attempt has been made to perform an operation that would - // cause an internal stack to underflow. - errormsg = "stack underflow"; - break; - case GL_STACK_OVERFLOW: - // An attempt has been made to perform an operation that would - // cause an internal stack to overflow. - errormsg = "stack overflow"; - break; - default: - // unknown error state - errormsg = "unknown error"; - } - - throw Error(MSG(err) << - "OpenGL error state after running draw method: " << glerrorstate << "\n" - "\t" << errormsg << "\n" - << "Run the game with --gl-debug to get more information: './run game --gl-debug'."); - } -} - -}} // openage::util diff --git a/libopenage/util/opengl.h b/libopenage/util/opengl.h deleted file mode 100644 index d1c3a2e300..0000000000 --- a/libopenage/util/opengl.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2014-2016 the openage authors. See copying.md for legal info. - -#pragma once - -namespace openage { -namespace util { - -/** - * query the current opengl context for any errors. - * - * raises exceptions on any error. - */ -void gl_check_error(); - -} -} From 88e7393a5625ddba660a710fc3d3bbc6b16aadb8 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Wed, 21 Oct 2015 10:26:52 +0200 Subject: [PATCH 27/32] renderer: only upload gpu buffer when changed. --- libopenage/renderer/CMakeLists.txt | 1 + libopenage/renderer/buffer.cpp | 9 +++- libopenage/renderer/buffer.h | 7 +++- libopenage/renderer/color.cpp | 42 +++++-------------- libopenage/renderer/color.h | 25 +++++------ libopenage/renderer/geometry.h | 19 +++++++-- libopenage/renderer/material.cpp | 13 ++++++ libopenage/renderer/material.h | 26 ++++++++---- libopenage/renderer/opengl/buffer.cpp | 9 ++-- libopenage/renderer/opengl/vertex_state.cpp | 11 +++-- libopenage/renderer/opengl/vertex_state.h | 7 ++-- libopenage/renderer/pipeline.cpp | 6 ++- libopenage/renderer/renderer.cpp | 4 +- libopenage/renderer/renderer.h | 2 +- libopenage/renderer/shaders/simpletexture.cpp | 12 ++++-- libopenage/renderer/shaders/simpletexture.h | 16 ++++--- libopenage/renderer/task.cpp | 5 ++- libopenage/renderer/task.h | 37 +++++++--------- libopenage/renderer/tests.cpp | 19 ++++----- libopenage/renderer/vertex_buffer.cpp | 6 +-- libopenage/renderer/vertex_buffer.h | 4 +- libopenage/renderer/vertex_state.h | 7 +++- libopenage/util/quaternion_test.cpp | 36 +++++++++------- 23 files changed, 189 insertions(+), 134 deletions(-) create mode 100644 libopenage/renderer/material.cpp diff --git a/libopenage/renderer/CMakeLists.txt b/libopenage/renderer/CMakeLists.txt index d5832bdea9..0a6bd15a74 100644 --- a/libopenage/renderer/CMakeLists.txt +++ b/libopenage/renderer/CMakeLists.txt @@ -3,6 +3,7 @@ add_sources(libopenage color.cpp context.cpp context_state.cpp + material.cpp pipeline.cpp program.cpp renderer.cpp diff --git a/libopenage/renderer/buffer.cpp b/libopenage/renderer/buffer.cpp index d374b500e8..f01047d38e 100644 --- a/libopenage/renderer/buffer.cpp +++ b/libopenage/renderer/buffer.cpp @@ -20,10 +20,17 @@ size_t Buffer::size() const { return this->allocd; } -char *Buffer::get() const { +char *Buffer::get(bool mark_dirty) { + if (mark_dirty) { + this->mark_dirty(); + } return this->buffer.get(); } +void Buffer::mark_dirty() { + this->on_gpu = false; +} + void Buffer::create(size_t size) { this->buffer = std::make_unique(size); this->allocd = size; diff --git a/libopenage/renderer/buffer.h b/libopenage/renderer/buffer.h index 1d6817e344..70eb543be8 100644 --- a/libopenage/renderer/buffer.h +++ b/libopenage/renderer/buffer.h @@ -45,7 +45,12 @@ class Buffer { /** * Returns the raw pointer to the buffer memory area. */ - char *get() const; + char *get(bool mark_dirty=false); + + /** + * Mark the buffer as changed so it will be reuploaded. + */ + void mark_dirty(); /** * Create an empty buffer of the given size. diff --git a/libopenage/renderer/color.cpp b/libopenage/renderer/color.cpp index d8b53660b5..035bcf710c 100644 --- a/libopenage/renderer/color.cpp +++ b/libopenage/renderer/color.cpp @@ -10,45 +10,25 @@ Color::Color() r{0}, g{0}, b{0}, - a{255} { - // Empty -} + a{255} {} -Color::Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) +Color::Color(color_channel_t r, color_channel_t g, + color_channel_t b, color_channel_t a) : r{r}, g{g}, b{b}, - a{a} { - // Empty -} - -Color::Color(const Color &other) - : - r{other.r}, - g{other.g}, - b{other.b}, - a{other.a} { - // Empty -} - -Color &Color::operator=(const Color &other) { - if (this != &other) { - this->r = other.r; - this->g = other.g; - this->b = other.b; - this->a = other.a; - } - - return *this; -} + a{a} {} -bool Color::operator==(const Color &other) const { - return this->r == other.r && this->g == other.g && this->b == other.b && this->a == other.a; +bool Color::operator ==(const Color &other) const { + return (this->r == other.r && + this->g == other.g && + this->b == other.b && + this->a == other.a); } -bool Color::operator!=(const Color &other) const { - return this->r != other.r || this->g != other.g || this->b != other.b || this->a != other.a; +bool Color::operator !=(const Color &other) const { + return not (*this == other); } }} // openage::renderer diff --git a/libopenage/renderer/color.h b/libopenage/renderer/color.h index 93bd7406cf..8341c5841a 100644 --- a/libopenage/renderer/color.h +++ b/libopenage/renderer/color.h @@ -8,24 +8,25 @@ namespace openage { namespace renderer { class Color { + using color_channel_t = uint8_t; + public: Color(); + Color(color_channel_t r, color_channel_t g, color_channel_t b, color_channel_t a); - Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a); - - Color(const Color &other); - - Color &operator=(const Color &other); - - bool operator==(const Color &other) const; + Color(const Color &other) = default; + Color(Color &&other) = default; + Color &operator =(const Color &other) = default; + Color &operator =(Color &&other) = default; - bool operator!=(const Color &other) const; + bool operator ==(const Color &other) const; - uint8_t r; - uint8_t g; - uint8_t b; - uint8_t a; + bool operator !=(const Color &other) const; + color_channel_t r; + color_channel_t g; + color_channel_t b; + color_channel_t a; }; }} // openage::renderer diff --git a/libopenage/renderer/geometry.h b/libopenage/renderer/geometry.h index 88a8e18389..95980a7aaf 100644 --- a/libopenage/renderer/geometry.h +++ b/libopenage/renderer/geometry.h @@ -3,21 +3,34 @@ #ifndef OPENAGE_RENDERER_GEOMETRY_H_ #define OPENAGE_RENDERER_GEOMETRY_H_ +#include "vertex_buffer.h" namespace openage { namespace renderer { +class Context; class Material; +/** + * Drawable geometry, initialized by a pipeline. + */ class Geometry { public: - Geometry(); + Geometry(Context *context, Material *material); + Geometry(Context *context, Material *material, mesh_t tri_vertices); virtual ~Geometry(); -private: - std::string name; + /** + * Create a render task from this geometry. + * This is then submitted to the renderer. + */ + Task get_task(); +protected: + Context *const context; Material *material; + + VertexBuffer vbuf; }; }} // openage::renderer diff --git a/libopenage/renderer/material.cpp b/libopenage/renderer/material.cpp new file mode 100644 index 0000000000..25e13d9bc9 --- /dev/null +++ b/libopenage/renderer/material.cpp @@ -0,0 +1,13 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + + +#include "material.h" + +namespace openage { +namespace renderer { + +Material::Material(Program *prg) + : + Pipeline{prg} {} + +}} // openage::renderer diff --git a/libopenage/renderer/material.h b/libopenage/renderer/material.h index c750113894..482f085191 100644 --- a/libopenage/renderer/material.h +++ b/libopenage/renderer/material.h @@ -3,20 +3,30 @@ #ifndef OPENAGE_RENDERER_MATERIAL_H_ #define OPENAGE_RENDERER_MATERIAL_H_ -#include +#include -#include "texture.h" -#include "program.h" +#include "pipeline.h" +#include "../util/vector.h" namespace openage { namespace renderer { -class Material { -protected: - Texture *txt; - Pipeline *code; +/** + * A material is a pipeline configuration that allows to setting predefined + * properties. + */ +class Material : public Pipeline { +public: + // standard position point position. + using position_t = util::Vector<4>; + using mesh_t = std::vector; + + Material(Program *prg); + virtual ~Material() = default; + + virtual void set_positions(mesh_t positions) = 0; }; -}} // namespace openage::renderer +}} // openage::renderer #endif diff --git a/libopenage/renderer/opengl/buffer.cpp b/libopenage/renderer/opengl/buffer.cpp index 2067d852be..dcaeed879d 100644 --- a/libopenage/renderer/opengl/buffer.cpp +++ b/libopenage/renderer/opengl/buffer.cpp @@ -28,10 +28,11 @@ void Buffer::upload(bind_target target, usage usage) { GLenum gl_usage = this->get_usage(usage); GLenum gl_slot = this->get_target(target); - this->bind(target); - glBufferData(gl_slot, this->allocd, this->get(), gl_usage); - - this->on_gpu = true; + if (not this->on_gpu) { + this->bind(target); + glBufferData(gl_slot, this->allocd, this->get(), gl_usage); + this->on_gpu = true; + } } void Buffer::bind(bind_target target) const { diff --git a/libopenage/renderer/opengl/vertex_state.cpp b/libopenage/renderer/opengl/vertex_state.cpp index f5bae4a803..f3bb12351a 100644 --- a/libopenage/renderer/opengl/vertex_state.cpp +++ b/libopenage/renderer/opengl/vertex_state.cpp @@ -23,11 +23,11 @@ VertexState::~VertexState() { glDeleteVertexArrays(1, &this->id); } -void VertexState::attach_buffer(const VertexBuffer &buf) { +void VertexState::attach_buffer(VertexBuffer &buf) { // make the vao active, it will store the section assignment this->bind(); - // make the buffer active. + // push the buffer to the gpu, if neccessary. buf.upload(); // assign the sections, set the pointers @@ -43,12 +43,17 @@ void VertexState::attach_buffer(const VertexBuffer &buf) { reinterpret_cast(section.offset) ); + if (this->bound_attributes.count(section.attr_id) > 0) { + throw Error(MSG(err) << "attribute " << section.attr_id + << " already set!"); + } + // add to the list of active attributes this->bound_attributes.insert(section.attr_id); } } -void VertexState::detach_buffer(const VertexBuffer &buf) { +void VertexState::detach_buffer(VertexBuffer &buf) { // make the vao active, it will store the section removal this->bind(); diff --git a/libopenage/renderer/opengl/vertex_state.h b/libopenage/renderer/opengl/vertex_state.h index cf9354587c..db0a4fc108 100644 --- a/libopenage/renderer/opengl/vertex_state.h +++ b/libopenage/renderer/opengl/vertex_state.h @@ -23,14 +23,15 @@ class VertexState : public renderer::VertexState { /** * Do attribute buffer section assignments. - * This sets pointers to the given buffer. + * This uploads the buffer to the gpu. + * This sets pointers to the given buffer to prepare usage. */ - void attach_buffer(const VertexBuffer &buf) override; + void attach_buffer(VertexBuffer &buf) override; /** * Remove attribute buffer section assignments. */ - void detach_buffer(const VertexBuffer &buf) override; + void detach_buffer(VertexBuffer &buf) override; /** * Make this vertex array object the current one. diff --git a/libopenage/renderer/pipeline.cpp b/libopenage/renderer/pipeline.cpp index f25b4c3453..61a0e723b0 100644 --- a/libopenage/renderer/pipeline.cpp +++ b/libopenage/renderer/pipeline.cpp @@ -110,6 +110,7 @@ void Pipeline::update_buffer(VertexBuffer *vbuf) { } // allocate a buffer to hold all the values. + // the next loop will fill into that memory. vbuf->alloc(buf_size); auto sections = vbuf->get_sections(); @@ -119,8 +120,11 @@ void Pipeline::update_buffer(VertexBuffer *vbuf) { BaseAttribute *var = this->attributes[i]; VertexBuffer::vbo_section *section = §ions[i]; + // raw pointer to to-be-submitted data buffer. + char *pos = vbuf->get(true); + pos += section->offset; + // store the attribute section to the buffer - char *pos = vbuf->get() + section->offset; var->pack(pos, var->entry_size() * vertex_count); } } diff --git a/libopenage/renderer/renderer.cpp b/libopenage/renderer/renderer.cpp index 4ace25bd05..88f574aba8 100644 --- a/libopenage/renderer/renderer.cpp +++ b/libopenage/renderer/renderer.cpp @@ -15,12 +15,12 @@ Renderer::Renderer(const std::shared_ptr &ctx) Renderer::~Renderer() {} -TaskState Renderer::add_task(const Task &task) { +TaskState Renderer::add_task(Task &task) { // sort by: // layer, texture, shader this->tasks.push(task); - TaskState ret; + TaskState ret{&task, this}; return ret; } diff --git a/libopenage/renderer/renderer.h b/libopenage/renderer/renderer.h index e23104e6a7..6fd501239f 100644 --- a/libopenage/renderer/renderer.h +++ b/libopenage/renderer/renderer.h @@ -52,7 +52,7 @@ class Renderer : public ResizeHandler { * * @returns the state handle for the added task. */ - TaskState add_task(const Task &task); + TaskState add_task(Task &task); /** * Register a pipeline program to this renderer. diff --git a/libopenage/renderer/shaders/simpletexture.cpp b/libopenage/renderer/shaders/simpletexture.cpp index 3fd75dd032..e92d674f2a 100644 --- a/libopenage/renderer/shaders/simpletexture.cpp +++ b/libopenage/renderer/shaders/simpletexture.cpp @@ -5,18 +5,22 @@ namespace openage { namespace renderer { -SimpleTexturePipeline::SimpleTexturePipeline(Program *prg) +SimpleTextureMaterial::SimpleTextureMaterial(Program *prg) : - Pipeline{prg}, + Material{prg}, tex{"tex", this}, position{"position", this}, texcoord{"texcoord", this} { - // we can't register the variables in the constructor of a - // PipelineVariable, as this would store the wrong type. + // register the variables to the "active" list this->add_var(&this->tex); this->add_var(&this->position); this->add_var(&this->texcoord); } +void SimpleTextureMaterial::set_positions(std::vector positions) { + this->position.set(positions); +} + + }} // openage::renderer diff --git a/libopenage/renderer/shaders/simpletexture.h b/libopenage/renderer/shaders/simpletexture.h index d7c52fa52d..8c3b9763b5 100644 --- a/libopenage/renderer/shaders/simpletexture.h +++ b/libopenage/renderer/shaders/simpletexture.h @@ -5,19 +5,25 @@ #include -#include "../pipeline.h" +#include "../material.h" #include "../../util/vector.h" namespace openage { namespace renderer { -class SimpleTexturePipeline : public Pipeline { +/** + * A simple plain texture material. + */ +class SimpleTextureMaterial : public Material { public: - SimpleTexturePipeline(Program *prg); - virtual ~SimpleTexturePipeline() = default; + SimpleTextureMaterial(Program *prg); + virtual ~SimpleTextureMaterial() = default; + void set_positions(mesh_t positions) override; + + // shader variables: Uniform tex; - Attribute, vertex_attribute_type::float_32, 4> position; + Attribute position; Attribute, vertex_attribute_type::float_32, 2> texcoord; }; diff --git a/libopenage/renderer/task.cpp b/libopenage/renderer/task.cpp index a04d8a0656..4a8f170c3b 100644 --- a/libopenage/renderer/task.cpp +++ b/libopenage/renderer/task.cpp @@ -16,6 +16,9 @@ bool Task::operator <(const Task &other) const { return true; } -TaskState::TaskState() {} +TaskState::TaskState(Task *task, Renderer *renderer) + : + task{task}, + renderer{renderer} {} }} // openage::renderer diff --git a/libopenage/renderer/task.h b/libopenage/renderer/task.h index fe8fd0c4c0..76458df0e9 100644 --- a/libopenage/renderer/task.h +++ b/libopenage/renderer/task.h @@ -19,12 +19,21 @@ class Renderer; */ class Task { public: - Material *material; - Geometry *geometry; + Geometry *const geometry; + /** + * Position to draw the geometry at. + */ coord::phys3 position; + + /** + * Rotation for the geometry. + */ util::Quaternion<> rotation; + /** + * Ordering condition to draw tasks in the correct order. + */ bool operator <(const Task &other) const; }; @@ -34,7 +43,7 @@ class Task { */ class TaskState { public: - TaskState(); + TaskState(Task *t, Renderer *r); ~TaskState(); /** @@ -42,28 +51,12 @@ class TaskState { */ bool rendered; -private: - Task *task; - Renderer *renderer; -}; - -/** - * Groups some tasks together. - * The order of tasks in this group is optimized. - * - * E.g. all unit draw actions are in one group. - */ -class TaskGroup { -public: - TaskGroup(); - ~TaskGroup(); - protected: - std::vector tasks; // heap? - size_t id; + Task *const task; + Renderer *const renderer; }; -}} // namespace openage::renderer +}} // openage::renderer #endif diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index 340b0a2b95..e1c484e46e 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -192,13 +192,14 @@ void renderer_demo_1() { std::unique_ptr simpletex = renderer.add_program(simpletex_src); simpletex->dump_attributes(); - SimpleTexturePipeline tex_pipeline{simpletex.get()}; + SimpleTextureMaterial tex_pipeline{simpletex.get()}; + + VertexBuffer vbo{window.get_context().get()}; FileTextureData gaben_data{"assets/gaben.png"}; std::unique_ptr gaben = renderer.add_texture(gaben_data); std::unique_ptr vao = window.get_context()->create_vertex_state(); - render_demo test1{ // init [&](Window */*window*/) { @@ -209,7 +210,7 @@ void renderer_demo_1() { tex_pipeline.texcoord.set_layout(1); float val = 0.9f; - tex_pipeline.position.set({ + tex_pipeline.set_positions({ {-val, -val, .0f, 1.0f}, {val, -val, .0f, 1.0f}, {-val, val, .0f, 1.0f}, @@ -228,21 +229,19 @@ void renderer_demo_1() { {0.0f, 0.0f}, {1.0f, 0.0f}, }); + + tex_pipeline.update_buffer(&vbo); + vao->attach_buffer(vbo); // upload buffer + + vao->bind(); }, // frame [&]() { glClearColor(0.0, 0.0, 0.2, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - vao->bind(); - - VertexBuffer vbo = tex_pipeline.create_attribute_buffer(); - vao->attach_buffer(vbo); - glDrawArrays(GL_TRIANGLES, 0, 6); - vao->detach_buffer(vbo); - renderer.check_error(); }, // resize diff --git a/libopenage/renderer/vertex_buffer.cpp b/libopenage/renderer/vertex_buffer.cpp index 0f787d0944..bb57bf8268 100644 --- a/libopenage/renderer/vertex_buffer.cpp +++ b/libopenage/renderer/vertex_buffer.cpp @@ -27,7 +27,7 @@ const VertexBuffer &VertexBuffer::operator =(VertexBuffer &&other) { return *this; } -void VertexBuffer::upload() const { +void VertexBuffer::upload() { this->buffer->upload(Buffer::bind_target::vertex_attributes, this->usage); } @@ -43,8 +43,8 @@ void VertexBuffer::add_section(const vbo_section §ion) { this->sections.push_back(section); } -char *VertexBuffer::get() { - return this->buffer->get(); +char *VertexBuffer::get(bool will_modify) { + return this->buffer->get(will_modify); } Buffer *VertexBuffer::get_buffer() { diff --git a/libopenage/renderer/vertex_buffer.h b/libopenage/renderer/vertex_buffer.h index 3d200a4b03..a33708f13a 100644 --- a/libopenage/renderer/vertex_buffer.h +++ b/libopenage/renderer/vertex_buffer.h @@ -69,7 +69,7 @@ class VertexBuffer { * Upload this buffer to the GPU and bind it to the * required vertex attribute buffer slot. */ - void upload() const; + void upload(); /** * Allocate a buffer of given size. @@ -90,7 +90,7 @@ class VertexBuffer { /** * Return the raw pointer to the allocated buffer. */ - char *get(); + char *get(bool will_modify=false); /** * Return the buffer metadata describing layout and position diff --git a/libopenage/renderer/vertex_state.h b/libopenage/renderer/vertex_state.h index bf706f5799..6c444d445d 100644 --- a/libopenage/renderer/vertex_state.h +++ b/libopenage/renderer/vertex_state.h @@ -35,14 +35,17 @@ class VertexState { * Attach the given vertex buffer to this vertex state. * Buffer contents are assigned to their corresponding * attribute layout ids. + * + * This just enables the buffer to be used when this vertex + * state is active. */ - virtual void attach_buffer(const VertexBuffer &buf) = 0; + virtual void attach_buffer(VertexBuffer &buf) = 0; /** * Remove the attributes of the given vertex buffer from the * active list. */ - virtual void detach_buffer(const VertexBuffer &buf) = 0; + virtual void detach_buffer(VertexBuffer &buf) = 0; /** * Make this vertex state the current one. diff --git a/libopenage/util/quaternion_test.cpp b/libopenage/util/quaternion_test.cpp index 880a16aba3..0ea067d7fa 100644 --- a/libopenage/util/quaternion_test.cpp +++ b/libopenage/util/quaternion_test.cpp @@ -125,26 +125,32 @@ void quaternion() { switch (a) { case axis::x: - return { - 1, 0, 0, - 0, std::cos(am), -std::sin(am), - 0, std::sin(am), std::cos(am) - }; + return { + 1, 0, 0, + 0, std::cos(am), -std::sin(am), + 0, std::sin(am), std::cos(am) + }; case axis::y: - return { - std::cos(am), 0, std::sin(am), - 0, 1, 0, - -std::sin(am), 0, std::cos(am) - }; + return { + std::cos(am), 0, std::sin(am), + 0, 1, 0, + -std::sin(am), 0, std::cos(am) + }; case axis::z: - return { - std::cos(am), -std::sin(am), 0, - std::sin(am), std::cos(am), 0, - 0, 0, 1, + return { + std::cos(am), -std::sin(am), 0, + std::sin(am), std::cos(am), 0, + 0, 0, 1, + }; + default: + return { + 1, 0, 0, + 0, 1, 0, + 0, 0, 1, + }; }; - } }; // zero rotation = identity. From 459d73502decb6d70b6f657ccebd5a61027d1c21 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Fri, 6 Nov 2015 00:38:03 +0100 Subject: [PATCH 28/32] renderer: towards render tasks by storing uniform state --- libopenage/renderer/geometry.cpp | 18 +++ libopenage/renderer/geometry.h | 21 ++-- libopenage/renderer/material.h | 5 + libopenage/renderer/opengl/program.cpp | 10 +- libopenage/renderer/opengl/program.h | 7 +- libopenage/renderer/pipeline.cpp | 92 +++++++++++++-- libopenage/renderer/pipeline.h | 110 ++++++++++++++++-- libopenage/renderer/program.h | 6 +- libopenage/renderer/shaders/CMakeLists.txt | 1 + libopenage/renderer/shaders/alphamask.cpp | 31 +++++ libopenage/renderer/shaders/alphamask.h | 38 ++++++ libopenage/renderer/shaders/simpletexture.cpp | 2 +- libopenage/renderer/shaders/simpletexture.h | 6 +- libopenage/renderer/task.h | 10 ++ libopenage/renderer/tests.cpp | 12 +- libopenage/renderer/vertex_buffer.h | 5 +- libopenage/renderer/vertex_state.h | 2 + 17 files changed, 339 insertions(+), 37 deletions(-) create mode 100644 libopenage/renderer/geometry.cpp create mode 100644 libopenage/renderer/shaders/alphamask.cpp create mode 100644 libopenage/renderer/shaders/alphamask.h diff --git a/libopenage/renderer/geometry.cpp b/libopenage/renderer/geometry.cpp new file mode 100644 index 0000000000..1602aa0112 --- /dev/null +++ b/libopenage/renderer/geometry.cpp @@ -0,0 +1,18 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#include "geometry.h" + +namespace openage { +namespace renderer { + +Geometry::Geometry(mesh_t vertices) + : + vertices{vertices} {} + +mesh_t get_mesh() { + return this->vertices() +} + +}} // openage::renderer + +#endif diff --git a/libopenage/renderer/geometry.h b/libopenage/renderer/geometry.h index 95980a7aaf..fae018ccd3 100644 --- a/libopenage/renderer/geometry.h +++ b/libopenage/renderer/geometry.h @@ -4,6 +4,7 @@ #define OPENAGE_RENDERER_GEOMETRY_H_ #include "vertex_buffer.h" +#include "vertex_state.h" namespace openage { namespace renderer { @@ -12,25 +13,23 @@ class Context; class Material; /** - * Drawable geometry, initialized by a pipeline. + * Drawable geometry, stores all triangles vertices. */ class Geometry { public: - Geometry(Context *context, Material *material); - Geometry(Context *context, Material *material, mesh_t tri_vertices); - virtual ~Geometry(); + Geometry(mesh_t vertices={}); + virtual ~Geometry() = default; /** - * Create a render task from this geometry. - * This is then submitted to the renderer. + * Return the associated vertex list. */ - Task get_task(); + const mesh_t &get_mesh(); protected: - Context *const context; - Material *material; - - VertexBuffer vbuf; + /** + * Triangle vertex storage. + */ + mesh_t vertices; }; }} // openage::renderer diff --git a/libopenage/renderer/material.h b/libopenage/renderer/material.h index 482f085191..c327ce7d8b 100644 --- a/libopenage/renderer/material.h +++ b/libopenage/renderer/material.h @@ -21,6 +21,11 @@ class Material : public Pipeline { using position_t = util::Vector<4>; using mesh_t = std::vector; + /** + * Creates a "type wrapper" around the pipeline program. + * This "Material" is then used to create pipeline + * properties and rendering algorithms. + */ Material(Program *prg); virtual ~Material() = default; diff --git a/libopenage/renderer/opengl/program.cpp b/libopenage/renderer/opengl/program.cpp index 2c25d1a7ed..d30f853076 100644 --- a/libopenage/renderer/opengl/program.cpp +++ b/libopenage/renderer/opengl/program.cpp @@ -213,8 +213,14 @@ void Program::set_uniform_1i(const char *name, const int &value) { glUniform1i(location, value); } +void Program::set_uniform_1ui(const char *name, const unsigned &value) { + this->use(); + GLint location = this->get_uniform_id(name); + glUniform1ui(location, value); +} + -void Program::set_uniform_2dtexture(const char *name, const renderer::Texture &texture) { +void Program::set_uniform_2dtexture(const char *name, const renderer::Texture *texture) { this->use(); // set the sampler "value" to the texture slot id. @@ -224,7 +230,7 @@ void Program::set_uniform_2dtexture(const char *name, const renderer::Texture &t // TODO: slot assignage algorithm. int slot = 0; glUniform1i(location, slot); - texture.bind_to(slot); + texture->bind_to(slot); } }}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/program.h b/libopenage/renderer/opengl/program.h index 840fb8fae4..6eea29bd53 100644 --- a/libopenage/renderer/opengl/program.h +++ b/libopenage/renderer/opengl/program.h @@ -69,10 +69,15 @@ class Program : public renderer::Program { */ void set_uniform_1i(const char *name, const int &value) override; + /** + * Set a single unsigned integer value + */ + void set_uniform_1ui(const char *name, const unsigned &value) override; + /** * Set 2d texture data. */ - void set_uniform_2dtexture(const char *name, const renderer::Texture &value) override; + void set_uniform_2dtexture(const char *name, const renderer::Texture *value) override; protected: diff --git a/libopenage/renderer/pipeline.cpp b/libopenage/renderer/pipeline.cpp index 61a0e723b0..8a0bcc0eaa 100644 --- a/libopenage/renderer/pipeline.cpp +++ b/libopenage/renderer/pipeline.cpp @@ -34,13 +34,21 @@ const char *PipelineVariable::get_name_cstr() { template<> -void Uniform::set(const Texture &value) { - this->pipeline->program->set_uniform_2dtexture(this->get_name_cstr(), value); +void Uniform::upload_value() { + this->pipeline->get_program()->set_uniform_2dtexture(this->get_name_cstr(), this->value); } + +template<> +void Uniform::upload_value() { + this->pipeline->get_program()->set_uniform_1i(this->get_name_cstr(), this->value); +} + + template<> -void Uniform::set(const int &value) { - this->pipeline->program->set_uniform_1i(this->get_name_cstr(), value); +void Uniform::upload_value() { + unsigned val = this->value ? 1 : 0; + this->pipeline->get_program()->set_uniform_1ui(this->get_name_cstr(), val); } @@ -49,12 +57,45 @@ Pipeline::Pipeline(Program *prg) program{prg} {} +Pipeline::Pipeline(Pipeline &&other) + : + program{other.program} { + + this->update_proplists(other); +} + +Pipeline::Pipeline(const Pipeline &other) + : + program{other.program} { + + this->update_proplists(other); +} + +Pipeline &Pipeline::operator =(Pipeline &&other) { + this->program = other.program; + this->update_proplists(other); + return *this; +} + +Pipeline &Pipeline::operator =(const Pipeline &other) { + this->program = other.program; + this->update_proplists(other); + return *this; +} + +Program *Pipeline::get_program() { + return this->program; +} + void Pipeline::add_var(PipelineVariable *var) { if (BaseAttribute *attr = dynamic_cast(var)) { - // just add the variable to the known list this->attributes.push_back(attr); - } else { - // non-attribute variable, ignore it. + } + else if (BaseUniform *unif = dynamic_cast(var)) { + this->uniforms.push_back(unif); + } + else { + // unknown variable, ignore it. } } @@ -71,6 +112,9 @@ VertexBuffer Pipeline::create_attribute_buffer() { void Pipeline::update_buffer(VertexBuffer *vbuf) { + // TODO: use buffersubdata to only transfer the modified + // parts of the buffer. + // determine vertex attribute buffer size size_t buf_size = 0; @@ -129,4 +173,38 @@ void Pipeline::update_buffer(VertexBuffer *vbuf) { } } + +void Pipeline::upload_uniforms() { + for (auto &uniform : this->uniforms) { + uniform->upload(); + } +} + + +void Pipeline::update_proplists(const Pipeline &source) { + this->update_proplistptr<>(&this->attributes, &source, source.attributes); + this->update_proplistptr<>(&this->uniforms, &source, source.uniforms); +} + + +template +void Pipeline::update_proplistptr(std::vector *proplist_dest, + const Pipeline *source, + const std::vector &proplist_src) { + // the proplist stores pointers to class members. + // now we're a new class: the pointers need to be updated + // to point to the members of the new class. + // The offsets are the same, the base address is different (a new 'this'). + proplist_dest->clear(); + + // beware: ultra dirty hackery. + // the new variable offset is calculated from the old one. + // all because c++ has no reflection to iterate over member variables. + size_t new_base = reinterpret_cast(this); + for (auto &entry : proplist_src) { + size_t distance = reinterpret_cast(entry) - reinterpret_cast(source); + proplist_dest->push_back(reinterpret_cast(new_base + distance)); + } +} + }} // openage::renderer diff --git a/libopenage/renderer/pipeline.h b/libopenage/renderer/pipeline.h index 515ef41261..44017527d9 100644 --- a/libopenage/renderer/pipeline.h +++ b/libopenage/renderer/pipeline.h @@ -53,24 +53,76 @@ class PipelineVariable { }; +/** + * Base class for all uniform properties of the pipeline. + */ +class BaseUniform : public PipelineVariable { +public: + BaseUniform(const std::string &name, Pipeline *pipeline) + : + PipelineVariable{name, pipeline}, + dirty{true} {} + + virtual ~BaseUniform() = default; + + /** + * Push the value to the GPU, if necessary. + * + * TODO: another pipeline could have changed it for the same program. + */ + void upload() { + if (this->dirty) { + this->upload_value(); + this->dirty = false; + } + } + +protected: + /** + * Per-type specialization of how to set the uniform value. + * Calls the backend-dependent function to push the data to the gpu. + */ + virtual void upload_value() = 0; + + /** + * True when the value has changed and needs to be reuploaded. + */ + bool dirty; +}; + + /** * Pipeline uniform variable, * which is a global value for all pipeline stages. */ template -class Uniform : public PipelineVariable { +class Uniform : public BaseUniform { public: Uniform(const std::string &name, Pipeline *pipeline) : - PipelineVariable{name, pipeline} {} + BaseUniform{name, pipeline} {} virtual ~Uniform() = default; + /** + * Store the uniform value so it can be applied when requested. + */ + void set(const T &value) { + this->value = value; + this->dirty = true; + } + +protected: /** * Per-type specialization of how to set the uniform value. * Calls the backend-dependent function to push the data to the gpu. */ - void set(const T &value); + void upload_value() override; + + /** + * Stored value to be uploaded to the gpu. + */ + T value; }; @@ -138,8 +190,8 @@ class BaseAttribute : public PipelineVariable { * that provides the availability of a specific packing method. */ template + size_t dimensions, + vertex_attribute_type attribute_type=vertex_attribute_type::float_32> class Attribute : public BaseAttribute { public: Attribute(const std::string &name, Pipeline *pipeline) @@ -175,7 +227,7 @@ class Attribute : public BaseAttribute { */ int get_attr_id() override { if (unlikely(this->attr_id < 0)) { - this->attr_id = this->pipeline->program->get_attribute_id(this->get_name_cstr()); + this->attr_id = this->pipeline->get_program()->get_attribute_id(this->get_name_cstr()); } return this->attr_id; @@ -243,8 +295,19 @@ class Attribute : public BaseAttribute { class Pipeline { public: Pipeline(Program *prg); + + Pipeline(Pipeline &&other); + Pipeline(const Pipeline &other); + Pipeline &operator =(Pipeline &&other); + Pipeline &operator =(const Pipeline &other); + virtual ~Pipeline() = default; + /** + * Fetch the program associated with this pipeline. + */ + Program *get_program(); + /** * Add the given program variable to the list of maintained * pipeline attributes. @@ -264,19 +327,48 @@ class Pipeline { void update_buffer(VertexBuffer *vbuf); /** - * The pipeline program associated with this property definition class. + * Apply all the uniform values */ - Program *const program; + void upload_uniforms(); protected: + /** + * The pipeline program associated with this property definition class. + */ + Program *program; /** - * Syncs attribute entry lengths. + * Keeps track of all registered attribute properties. + * + * Used to sync attribute entry lengths. * Each attribute has to be supplied for each vertex once. * e.g. vec3 1337 color entries require 1337 vec4 positions. * These have different per-attribute sizes but the same lengths. */ std::vector attributes; + + /** + * Keeps track of all uniform properties. + */ + std::vector uniforms; + +private: + /** + * Update all membervariable-pointer-lists with offsets + * from another pipeline to have their base to 'this'. + * + * As C++ has no reflection, this needs to be done each time + * a Pipeline object is relocated somewhere else. + */ + void update_proplists(const Pipeline &source); + + /** + * Update the oneproperty list from another pipeline. + */ + template + void update_proplistptr(std::vector *proplist_dest, + const Pipeline *source, + const std::vector &proplist_src); }; }} // openage::renderer diff --git a/libopenage/renderer/program.h b/libopenage/renderer/program.h index e16e2aab56..e74b96584c 100644 --- a/libopenage/renderer/program.h +++ b/libopenage/renderer/program.h @@ -99,10 +99,14 @@ class Program { */ virtual void set_uniform_1i(const char *name, const int &value) = 0; + /** + * Set a single unsigned integer value + */ + virtual void set_uniform_1ui(const char *name, const unsigned &value) = 0; /** * Set 2d texture data. */ - virtual void set_uniform_2dtexture(const char *name, const Texture &value) = 0; + virtual void set_uniform_2dtexture(const char *name, const Texture *value) = 0; /* ========================================== */ diff --git a/libopenage/renderer/shaders/CMakeLists.txt b/libopenage/renderer/shaders/CMakeLists.txt index 19aecc1b2b..c8bc7d39bb 100644 --- a/libopenage/renderer/shaders/CMakeLists.txt +++ b/libopenage/renderer/shaders/CMakeLists.txt @@ -1,3 +1,4 @@ add_sources(libopenage + alphamask.cpp simpletexture.cpp ) diff --git a/libopenage/renderer/shaders/alphamask.cpp b/libopenage/renderer/shaders/alphamask.cpp new file mode 100644 index 0000000000..c6106aedcc --- /dev/null +++ b/libopenage/renderer/shaders/alphamask.cpp @@ -0,0 +1,31 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#include "alphamask.h" + +namespace openage { +namespace renderer { + +AlphamaskMaterial::AlphamaskMaterial(Program *prg) + : + Material{prg}, + position{"position", this}, + base_texture{"base_tex", this}, + mask_texture{"mask_tex", this}, + show_mask{"show_mask", this}, + base_tex_coordinates{"base_tex_coordinates", this}, + mask_tex_coordinates{"mask_tex_coordinates", this} { + + this->add_var(&this->position); + this->add_var(&this->base_texture); + this->add_var(&this->mask_texture); + this->add_var(&this->show_mask); + this->add_var(&this->base_tex_coordinates); + this->add_var(&this->mask_tex_coordinates); +} + +void AlphamaskMaterial::set_positions(mesh_t positions) { + this->position.set(positions); +} + + +}} // openage::renderer diff --git a/libopenage/renderer/shaders/alphamask.h b/libopenage/renderer/shaders/alphamask.h new file mode 100644 index 0000000000..a38529ba19 --- /dev/null +++ b/libopenage/renderer/shaders/alphamask.h @@ -0,0 +1,38 @@ +// Copyright 2015-2015 the openage authors. See copying.md for legal info. + +#ifndef OPENAGE_RENDERER_SHADERS_ALPHAMASK_H_ +#define OPENAGE_RENDERER_SHADERS_ALPHAMASK_H_ + +#include + +#include "../material.h" +#include "../../util/vector.h" + +namespace openage { +namespace renderer { + +/** + * A simple plain texture material. + */ +class AlphamaskMaterial : public Material { +public: + AlphamaskMaterial(Program *prg); + virtual ~AlphamaskMaterial() = default; + + void set_positions(mesh_t positions) override; + + // shader variables: + Attribute position; + Uniform base_texture; + Uniform mask_texture; + + Uniform show_mask; + Attribute, 2> base_tex_coordinates; + Attribute, 2> mask_tex_coordinates; +}; + + +}} // openage::renderer + + +#endif diff --git a/libopenage/renderer/shaders/simpletexture.cpp b/libopenage/renderer/shaders/simpletexture.cpp index e92d674f2a..ee2e791d03 100644 --- a/libopenage/renderer/shaders/simpletexture.cpp +++ b/libopenage/renderer/shaders/simpletexture.cpp @@ -18,7 +18,7 @@ SimpleTextureMaterial::SimpleTextureMaterial(Program *prg) this->add_var(&this->texcoord); } -void SimpleTextureMaterial::set_positions(std::vector positions) { +void SimpleTextureMaterial::set_positions(mesh_t positions) { this->position.set(positions); } diff --git a/libopenage/renderer/shaders/simpletexture.h b/libopenage/renderer/shaders/simpletexture.h index 8c3b9763b5..a84cff8353 100644 --- a/libopenage/renderer/shaders/simpletexture.h +++ b/libopenage/renderer/shaders/simpletexture.h @@ -22,9 +22,9 @@ class SimpleTextureMaterial : public Material { void set_positions(mesh_t positions) override; // shader variables: - Uniform tex; - Attribute position; - Attribute, vertex_attribute_type::float_32, 2> texcoord; + Uniform tex; + Attribute position; + Attribute, 2> texcoord; }; diff --git a/libopenage/renderer/task.h b/libopenage/renderer/task.h index 76458df0e9..ecb6a51634 100644 --- a/libopenage/renderer/task.h +++ b/libopenage/renderer/task.h @@ -19,8 +19,18 @@ class Renderer; */ class Task { public: + /** + * The geometry to draw. + */ Geometry *const geometry; + /** + * The material to use for drawing the geometry. + * Stores to-be-set uniforms. + * Can create vertex buffers. + */ + //MaterialApplication material; + /** * Position to draw the geometry at. */ diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index e1c484e46e..d5bf1e869e 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -205,7 +205,7 @@ void renderer_demo_1() { [&](Window */*window*/) { log::log(MSG(dbg) << "preparing test"); - tex_pipeline.tex.set(*gaben.get()); + tex_pipeline.tex.set(gaben.get()); tex_pipeline.position.set_layout(0); tex_pipeline.texcoord.set_layout(1); @@ -230,6 +230,8 @@ void renderer_demo_1() { {1.0f, 0.0f}, }); + // apply the pipeline properties + tex_pipeline.upload_uniforms(); tex_pipeline.update_buffer(&vbo); vao->attach_buffer(vbo); // upload buffer @@ -240,6 +242,14 @@ void renderer_demo_1() { glClearColor(0.0, 0.0, 0.2, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // combination of geometry and material with translations + // = draw task. + + // geometry: manager owns. + // material: manager owns? + + //Task t = material.draw(geometry); glDrawArrays(GL_TRIANGLES, 0, 6); renderer.check_error(); diff --git a/libopenage/renderer/vertex_buffer.h b/libopenage/renderer/vertex_buffer.h index a33708f13a..d811a543d1 100644 --- a/libopenage/renderer/vertex_buffer.h +++ b/libopenage/renderer/vertex_buffer.h @@ -41,7 +41,10 @@ constexpr auto vertex_attribute_size = datastructure::create_const_map Date: Tue, 8 Nov 2016 22:58:43 +0000 Subject: [PATCH 29/32] renderer: opengl version checking --- libopenage/renderer/opengl/context.cpp | 88 +++++++++++++++++++------- libopenage/renderer/opengl/context.h | 7 +- 2 files changed, 71 insertions(+), 24 deletions(-) diff --git a/libopenage/renderer/opengl/context.cpp b/libopenage/renderer/opengl/context.cpp index d044d00381..da6ed8f90f 100644 --- a/libopenage/renderer/opengl/context.cpp +++ b/libopenage/renderer/opengl/context.cpp @@ -1,10 +1,13 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. +// Copyright 2015-2016 the openage authors. See copying.md for legal info. #include "../../config.h" #if WITH_OPENGL #include "context.h" +#include +#include + #include #include #include @@ -20,10 +23,6 @@ namespace openage { namespace renderer { namespace opengl { -// TODO: get max available gl version -constexpr int opengl_version_major = 3; -constexpr int opengl_version_minor = 3; - Context::Context() : renderer::Context{context_type::opengl} {} @@ -34,13 +33,65 @@ uint32_t Context::get_window_flags() const { return SDL_WINDOW_OPENGL; } +// first element is the min supported version +// last element is the max supported version +constexpr std::array, 6> gl_versions = {{{3,3}, {4,0}, {4,1}, {4,2}, {4,3}, {4,4}}}; + void Context::prepare() { + // set initially to maximum supported version so that if the for loop doesn't fail the max one is used + int opengl_version_major = gl_versions[gl_versions.size() - 1].first; + int opengl_version_minor = gl_versions[gl_versions.size() - 1].second; + + SDL_Window *test_window = SDL_CreateWindow("test", 0, 0, 2, 2, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); + if (test_window == nullptr) { + throw Error(MSG(err) << "Failed creating window for OpenGL context testing. SDL Error: " << SDL_GetError()); + } + SDL_GLContext test_context; + + // check each version for availability SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, opengl_version_major); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, opengl_version_minor); SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + + for (size_t i_ver = 0; i_ver < gl_versions.size(); ++i_ver) { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, gl_versions[i_ver].first); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, gl_versions[i_ver].second); + test_context = SDL_GL_CreateContext(test_window); + + if (test_context == nullptr) { + if (i_ver == 0) { + throw Error(MSG(err) << "OpenGL version " << gl_versions[0].first << "." << gl_versions[0].second << " is not available. It is the minimal required version."); + } + else { + opengl_version_major = gl_versions[i_ver - 1].first; + opengl_version_minor = gl_versions[i_ver - 1].second; + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, opengl_version_major); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, opengl_version_minor); + break; + } + } + + SDL_GL_DeleteContext(test_context); + } + + test_context = SDL_GL_CreateContext(test_window); + if (test_context == nullptr) { + throw Error(MSG(err) << "Failed to create OpenGL context which previously succeeded. This should not happen! SDL Error: " << SDL_GetError()); + } + + auto &cap = this->capability; + + cap.type = this->type; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &cap.max_texture_size); + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &cap.max_texture_slots); + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &cap.max_vertex_attributes); + + glGetIntegerv(GL_MAJOR_VERSION, &cap.major_version); + glGetIntegerv(GL_MINOR_VERSION, &cap.minor_version); + + SDL_GL_DeleteContext(test_context); + SDL_DestroyWindow(test_window); } void Context::create(SDL_Window *window) { @@ -51,19 +102,21 @@ void Context::create(SDL_Window *window) { } // check the OpenGL version, for shaders n stuff - int epoxy_glv = opengl_version_major * 10 + opengl_version_minor; + // TODO: this doesn't look necessary. libepoxy supports up to 4.4, which is the maximum supported version in gl_versions + // and if GL 3.3 is not available than the context above will fail anyway and it won't get to this point + int epoxy_glv = this->capability.major_version * 10 + this->capability.minor_version; if (not epoxy_is_desktop_gl() or epoxy_gl_version() < epoxy_glv) { throw Error(MSG(err) << "OpenGL " - << opengl_version_major << "." << opengl_version_minor + << this->capability.major_version << "." << this->capability.minor_version << " not available"); } - log::log(MSG(info) << "Using OpenGL " << opengl_version_major << "." << opengl_version_minor); + log::log(MSG(info) << "Using OpenGL " << this->capability.major_version << "." << this->capability.minor_version); } void Context::setup() { // TODO: context capability checking - context_capability caps = this->get_capabilities(); + auto &caps = this->capability; // to quote the standard doc: 'The value gives a rough estimate of the // largest texture that the GL can handle' @@ -102,18 +155,7 @@ void Context::setup() { context_capability Context::get_capabilities() { - context_capability ret; - - ret.type = this->type; - - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &ret.max_texture_size); - glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &ret.max_texture_slots); - glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &ret.max_vertex_attributes); - - glGetIntegerv(GL_MAJOR_VERSION, &ret.major_version); - glGetIntegerv(GL_MINOR_VERSION, &ret.minor_version); - - return ret; + return this->capability; } diff --git a/libopenage/renderer/opengl/context.h b/libopenage/renderer/opengl/context.h index dac0a22b32..edb6e9d0a6 100644 --- a/libopenage/renderer/opengl/context.h +++ b/libopenage/renderer/opengl/context.h @@ -1,4 +1,4 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. +// Copyright 2015-2016 the openage authors. See copying.md for legal info. #ifndef OPENAGE_RENDERER_OPENGL_CONTEXT_H_ #define OPENAGE_RENDERER_OPENGL_CONTEXT_H_ @@ -104,6 +104,11 @@ class Context : public renderer::Context { * SDL opengl context state. */ SDL_GLContext glcontext; + + /** + * The capability of this context. + */ + context_capability capability; }; }}} // namespace openage::renderer From b38089914af5d1a4e1cf312f16de893bf999676f Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Tue, 8 Nov 2016 23:00:56 +0000 Subject: [PATCH 30/32] misc: add YouCompleteMe metadata to .gitignore and add self to authors --- .gitignore | 1 + copying.md | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 8e0ede845a..58d85fe013 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,4 @@ Makefile callgrind.out.* perf.data* .gdb_history +.ycm_extra_conf.py diff --git a/copying.md b/copying.md index c2ab7e5c6f..6b74f8d382 100644 --- a/copying.md +++ b/copying.md @@ -89,6 +89,7 @@ _the openage authors_ are: | Johannes Walcher | tomatower | johannes.walcher@stusta.de | | Akritas Akritidis | MaanooAk | akritasak@gmail.com | | Edgard Mota | edgardmota | edgardmota@gmail.com | +| Wojciech Nawrocki | Vtec234 | wjnawrocki@protonmail.com | If you're a first-time commiter, add yourself to the above list. This is not just for legal reasons, but also to keep an overview of all those nicknames. From 880fdb8c5416b3d5dda8f0b1969ce96f469b40df Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Tue, 8 Nov 2016 23:02:03 +0000 Subject: [PATCH 31/32] engine: adjust new upstream changes to renderer branch changes --- libopenage/engine.cpp | 2 +- libopenage/gui_basic.cpp | 32 +++++++++++++++++++++++--------- libopenage/renderer/window.cpp | 4 ++++ libopenage/renderer/window.h | 9 +++++++++ 4 files changed, 37 insertions(+), 10 deletions(-) diff --git a/libopenage/engine.cpp b/libopenage/engine.cpp index 0c73ea2d6d..f952bf23d0 100644 --- a/libopenage/engine.cpp +++ b/libopenage/engine.cpp @@ -122,7 +122,7 @@ Engine::Engine(util::Dir *data_dir, int32_t fps_limit, bool gl_debug, const char using namespace std::string_literals; auto qml_search_paths = {this->data_dir->basedir, "libopenage/gui"s}; - this->gui = std::make_unique(this->window, "qml/main.qml", &this->singletons_info, qml_search_paths); + this->gui = std::make_unique(this->window->get_raw_window(), "qml/main.qml", &this->singletons_info, qml_search_paths); this->register_resize_action(this->gui.get()); this->register_input_action(this->gui.get()); this->register_drawhud_action(this->gui.get()); diff --git a/libopenage/gui_basic.cpp b/libopenage/gui_basic.cpp index 6dea980021..4478649829 100644 --- a/libopenage/gui_basic.cpp +++ b/libopenage/gui_basic.cpp @@ -26,15 +26,29 @@ GuiBasic::GuiBasic(SDL_Window *window, const std::string &source, qtsdl::GuiSing const char *shader_header_code = "#version 120\n"; - char *texture_vert_code; - util::read_whole_file(&texture_vert_code, static_cast(singleton_items_info)->data_dir + "/shaders/identity.vert.glsl"); - auto plaintexture_vert = std::make_unique(GL_VERTEX_SHADER, std::initializer_list{ shader_header_code, texture_vert_code }); - delete[] texture_vert_code; - - char *texture_frag_code; - util::read_whole_file(&texture_frag_code, static_cast(singleton_items_info)->data_dir + "/shaders/maptexture.frag.glsl"); - auto plaintexture_frag = std::make_unique(GL_FRAGMENT_SHADER, std::initializer_list{ shader_header_code, texture_frag_code }); - delete[] texture_frag_code; + std::string texture_vert_code = util::read_whole_file( + static_cast(singleton_items_info)->data_dir + + "/shaders/identity.vert.glsl" + ); + auto plaintexture_vert = std::make_unique( + GL_VERTEX_SHADER, + std::initializer_list{ + shader_header_code, + texture_vert_code.c_str() + } + ); + + std::string texture_frag_code = util::read_whole_file( + static_cast(singleton_items_info)->data_dir + + "/shaders/maptexture.frag.glsl" + ); + auto plaintexture_frag = std::make_unique( + GL_FRAGMENT_SHADER, + std::initializer_list{ + shader_header_code, + texture_frag_code.c_str() + } + ); this->textured_screen_quad_shader = std::make_unique(&*plaintexture_vert, &*plaintexture_frag); this->textured_screen_quad_shader->link(); diff --git a/libopenage/renderer/window.cpp b/libopenage/renderer/window.cpp index 736b8d2d1d..767dd16647 100644 --- a/libopenage/renderer/window.cpp +++ b/libopenage/renderer/window.cpp @@ -90,4 +90,8 @@ std::shared_ptr Window::get_context() { return this->context; } +SDL_Window* Window::get_raw_window() const { + return this->window; +} + }} // namespace openage::renderer diff --git a/libopenage/renderer/window.h b/libopenage/renderer/window.h index f7efa6d8ca..63300c1b6d 100644 --- a/libopenage/renderer/window.h +++ b/libopenage/renderer/window.h @@ -48,6 +48,15 @@ class Window { */ std::shared_ptr get_context(); + // TODO: this shouldn't be exposed. + // It is only necessary because GuiBasic requires a raw SDL_Window handle, since the QtQuick + // gui needs the native context of our SDL window to work, so it (the QtQuick gui) is inherently + // coupled to our window. This probably means that the gui should be integrated either _into_ + // this Window class or _together_ with it in a conceptual unit that manages the GL context. + /** + * Return the raw SDL window handle. + */ + SDL_Window* get_raw_window() const; private: coord::window size; From 47c89b00d134c3979efc5e8662fcd5ce183851d7 Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Tue, 8 Nov 2016 23:02:30 +0000 Subject: [PATCH 32/32] build: fix build on GCC 5.3.0 Gentoo Linux --- libopenage/assetmanager.cpp | 5 +++++ libopenage/assetmanager.h | 2 ++ libopenage/engine.h | 2 +- libopenage/watch/linux.h | 3 +++ 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/libopenage/assetmanager.cpp b/libopenage/assetmanager.cpp index 726392beb6..798886d991 100644 --- a/libopenage/assetmanager.cpp +++ b/libopenage/assetmanager.cpp @@ -2,6 +2,10 @@ #include "assetmanager.h" +#ifdef WITH_INOTIFY +#include +#endif + #include "error/error.h" #include "log/log.h" #include "texture.h" @@ -9,6 +13,7 @@ #include "util/file.h" #include "watch/watch.h" + namespace openage { AssetManager::AssetManager(qtsdl::GuiItemLink *gui_link) diff --git a/libopenage/assetmanager.h b/libopenage/assetmanager.h index ffe2b74f49..94ca766ace 100644 --- a/libopenage/assetmanager.h +++ b/libopenage/assetmanager.h @@ -8,6 +8,8 @@ #include "util/dir.h" #include "watch/watch.h" +#include "config.h" + namespace qtsdl { class GuiItemLink; diff --git a/libopenage/engine.h b/libopenage/engine.h index 15f843dd0b..5d0426e73b 100644 --- a/libopenage/engine.h +++ b/libopenage/engine.h @@ -441,7 +441,7 @@ class Engine : public ResizeHandler, public options::OptionNode { */ std::unique_ptr gui; - /* + /** * The renderer. Accepts all tasks to be drawn on screen. */ std::unique_ptr renderer; diff --git a/libopenage/watch/linux.h b/libopenage/watch/linux.h index 64ddd2216e..5d6bfbbce8 100644 --- a/libopenage/watch/linux.h +++ b/libopenage/watch/linux.h @@ -11,6 +11,9 @@ #include "watch.h" +// GCC stdlib defines linux as 1. Why would anybody use lowercase macros? +#undef linux + namespace openage { namespace watch {