From ec56851cc9a6fda5627cb9d17bcfeeb61d72177f Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Sun, 19 Apr 2015 18:18:05 +0200 Subject: [PATCH 01/52] 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 47023b729de6fe90bdeba7b65c1a338d77843cda Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Sun, 19 Apr 2015 18:20:03 +0200 Subject: [PATCH 02/52] 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 fa0452231f6469c58c6b2c9ea08f62f8ad45ade1 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Sun, 19 Apr 2015 18:21:06 +0200 Subject: [PATCH 03/52] buildsystem: integrated vulkan and opengl detection --- CMakeLists.txt | 8 ++++++++ configure | 3 ++- libopenage/CMakeLists.txt | 29 +++++++++++++++++++++++++++++ libopenage/config.h.in | 2 ++ 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 23835f593e..35a497c22d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,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() diff --git a/configure b/configure index 5736afb756..4de0db57ab 100755 --- a/configure +++ b/configure @@ -69,6 +69,8 @@ def getenv_bool(varname): OPTIONS = { "backtrace": "if_available", "inotify": "if_available", + "opengl": "if_available", + "vulkan": "if_available", "gperftools-tcmalloc": False, "gperftools-profiler": "if_available", } @@ -81,7 +83,6 @@ def features(args, parser): the defaults below will be used. """ - def sanitize_option_name(option): """ Check if the given feature exists """ if option not in OPTIONS: diff --git a/libopenage/CMakeLists.txt b/libopenage/CMakeLists.txt index de5ad13240..8325ccc9ae 100644 --- a/libopenage/CMakeLists.txt +++ b/libopenage/CMakeLists.txt @@ -150,6 +150,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 403b3d97c5..5315990538 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 6c228e77c16e09c7b387db19559328d39b094584 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Tue, 21 Apr 2015 01:57:40 +0200 Subject: [PATCH 04/52] renderer: modular render window and context creation --- libopenage/CMakeLists.txt | 3 +- libopenage/engine.cpp | 7 +- libopenage/engine.h | 2 +- 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, 425 insertions(+), 8 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 8325ccc9ae..21da12e840 100644 --- a/libopenage/CMakeLists.txt +++ b/libopenage/CMakeLists.txt @@ -37,8 +37,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") @@ -49,7 +49,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 88fbd01bd7..81a90eefa7 100644 --- a/libopenage/engine.cpp +++ b/libopenage/engine.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include "config.h" #include "error/error.h" @@ -161,7 +160,7 @@ Engine::Engine(const util::Path &root_dir, // what gets drawn last is displayed on top. glDisable(GL_DEPTH_TEST); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + this->window = std::make_unique(windowtitle); //// -- initialize the gui util::Path qml_root = this->root_dir / "assets" / "qml"; @@ -441,7 +440,6 @@ void Engine::loop() { if (this->drawing_debug_overlay.value) { this->draw_debug_overlay(); - } if (this->drawing_huds.value) { @@ -465,7 +463,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(); @@ -474,6 +472,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 5656b63ab8..c56ee3f16b 100644 --- a/libopenage/engine.h +++ b/libopenage/engine.h @@ -432,7 +432,7 @@ class Engine : public ResizeHandler, public options::OptionNode { * This is actually a void * but sdl2 thought it was a good idea to * name it like a differently. */ - SDL_GLContext glcontext; + std::unique_ptr window; /** * the gui binding 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 2f230b38ff..d22643c0f5 100644 --- a/openage/testing/testlist.py +++ b/openage/testing/testlist.py @@ -105,6 +105,8 @@ 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") def benchmark_cpp(): From d1d59c02d4682fd4fbffa0675461d73d8085cf6b Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Fri, 26 Jun 2015 01:13:38 +0200 Subject: [PATCH 05/52] 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 6454fc8f4ef147b16bd29065d1292adb627f6185 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Mon, 29 Jun 2015 17:04:32 +0200 Subject: [PATCH 06/52] 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 418838ffb93b0fb59fe969a2122616fc7848e4cb Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Mon, 29 Jun 2015 17:04:49 +0200 Subject: [PATCH 07/52] 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 e16b5166dd..83a00de905 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Technology | Component **Qt5** | Graphical user interface **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 12d46f6f5c..9464b77826 100644 --- a/openage/CMakeLists.txt +++ b/openage/CMakeLists.txt @@ -21,4 +21,5 @@ add_subdirectory(cvar) add_subdirectory(game) add_subdirectory(log) 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 8e170d7db0fa55d69ddc557748ff6b83a5aa346b Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Mon, 10 Aug 2015 06:06:38 +0200 Subject: [PATCH 08/52] 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 ed60e491795957d43551f05cb98cdeca7ea92383 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Tue, 11 Aug 2015 20:52:32 +0200 Subject: [PATCH 09/52] 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 0902c8361403cd6ad5f7698cbd31cd141cbc8dd4 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Tue, 11 Aug 2015 20:55:43 +0200 Subject: [PATCH 10/52] 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 9509f1b87f4378509f69291065b858d78828192e Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Thu, 13 Aug 2015 01:13:43 +0200 Subject: [PATCH 11/52] renderer: initially integrated into the engine used for screenshots and resizing currently --- libopenage/audio/dynamic_resource.cpp | 1 + libopenage/engine.cpp | 7 +- libopenage/engine.h | 31 ++++--- 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 | 110 ++++--------------------- libopenage/screenshot.h | 33 +++++--- 13 files changed, 194 insertions(+), 134 deletions(-) diff --git a/libopenage/audio/dynamic_resource.cpp b/libopenage/audio/dynamic_resource.cpp index 831749a207..547908cd0a 100644 --- a/libopenage/audio/dynamic_resource.cpp +++ b/libopenage/audio/dynamic_resource.cpp @@ -7,6 +7,7 @@ #include "../engine.h" #include "../job/job_manager.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 81a90eefa7..89228f3a9a 100644 --- a/libopenage/engine.cpp +++ b/libopenage/engine.cpp @@ -26,6 +26,7 @@ #include "util/strings.h" #include "util/timer.h" + /** * Main openage namespace to store all things that make the have to do with the game. * @@ -204,7 +205,7 @@ Engine::Engine(const util::Path &root_dir, 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_coord_data()->window_size); + this->get_screenshot_manager()->save_screenshot(this->get_coord_data()->window_size); }); 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; @@ -515,8 +516,8 @@ 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 c56ee3f16b..d598d1ec09 100644 --- a/libopenage/engine.h +++ b/libopenage/engine.h @@ -33,6 +33,7 @@ #include "util/strings.h" #include "screenshot.h" + namespace openage { namespace gui { @@ -40,24 +41,29 @@ class 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; + /** @@ -237,7 +243,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. @@ -440,9 +446,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; /** * ttf font loading manager @@ -476,6 +482,11 @@ class Engine : public ResizeHandler, public options::OptionNode { * Link to the Qt GUI. */ gui::GuiItemLink *gui_link; + + /** + * the engines profiler + */ + util::Profiler profiler; }; } // namespace openage 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 f71b59f0b8..c3878ef96d 100644 --- a/libopenage/screenshot.cpp +++ b/libopenage/screenshot.cpp @@ -1,36 +1,33 @@ // Copyright 2014-2017 the openage authors. See copying.md for legal info. -#include "screenshot.h" - #include #include #include -#include -#include #include -#include -#include #include "coord/window.h" #include "job/job_manager.h" #include "log/log.h" + +#include +#include "screenshot.h" #include "util/strings.h" +#include "renderer/renderer.h" namespace openage { -ScreenshotManager::ScreenshotManager(job::JobManager *job_mgr) +ScreenshotManager::ScreenshotManager(job::JobManager *job_mgr, renderer::Renderer *renderer) : count{0}, - job_manager{job_mgr} { -} + job_manager{job_mgr}, + renderer{renderer} {} ScreenshotManager::~ScreenshotManager() {} std::string ScreenshotManager::gen_next_filename() { - std::time_t t = std::time(NULL); if (t == this->last_time) { @@ -40,93 +37,22 @@ 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)); - - return util::sformat("/tmp/openage_%s_%02d.png", timestamp, this->count); -} + 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)); -void ScreenshotManager::save_screenshot(coord::window size) { - coord::pixel_t width = size.x, - height = size.y; - - std::shared_ptr pxdata(new uint8_t[4*width*height], std::default_delete()); - glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pxdata.get()); - - auto encode_function = [this, pxdata, size] () { - return this->encode_png(pxdata, size); - }; - this->job_manager->enqueue(encode_function); + return util::sformat("/tmp/openage_%s_%02d.png", timestamp, this->count); } -bool ScreenshotManager::encode_png(std::shared_ptr pxdata, - coord::window size) { - std::FILE *fout = NULL; - coord::pixel_t width = size.x, - height = size.y; - auto warn_fn = [] (png_structp /*png_ptr*/, png_const_charp message) { - log::log(MSG(err) << "Creating screenshot failed: libpng error: " << message); - }; - auto err_fn = [] (png_structp png_ptr, png_const_charp message) { - log::log(MSG(err) << "Creating screenshot failed: libpng error: " << message); - longjmp(png_jmpbuf(png_ptr), 1); - }; - - png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, - (png_voidp) NULL, - err_fn, warn_fn); - if (!png_ptr) - return false; - - png_infop info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) { - png_destroy_write_struct(&png_ptr, (png_infopp) NULL); - return false; - } - - if (setjmp(png_jmpbuf(png_ptr))) { - std::fclose(fout); - png_destroy_write_struct(&png_ptr, &info_ptr); - return false; - } - - std::string filename = this->gen_next_filename(); - fout = std::fopen(filename.c_str(), "wb"); - if (fout == NULL) { - png_destroy_write_struct(&png_ptr, &info_ptr); - log::log(MSG(err) << "Could not open '"<< filename << "': " - << std::string(strerror(errno))); - return false; - } - - png_init_io(png_ptr, fout); - - png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, - PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, - PNG_FILTER_TYPE_DEFAULT); - - // Put image row pointer into info_ptr so that we can use the high-level - // write interface. - std::vector row_ptrs; - - // Invert rows. - row_ptrs.reserve(height); - for (int i = 1; i <= height; i++) { - row_ptrs.push_back(pxdata.get() + (height - i) * 4 * width); - } - png_set_rows(png_ptr, info_ptr, &row_ptrs[0]); - - //TODO: print ingame message. - log::log(MSG(info) << "Saving screenshot to '" << filename << "'."); - - png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_STRIP_FILLER_AFTER, NULL); - - std::fclose(fout); - png_destroy_write_struct(&png_ptr, &info_ptr); - return true; +void ScreenshotManager::save_screenshot() { + this->job_manager->enqueue([this] () { + std::string filename = this->gen_next_filename(); + this->renderer->screenshot(filename); + return true; + }); } } // openage diff --git a/libopenage/screenshot.h b/libopenage/screenshot.h index 26a9170737..9629681f26 100644 --- a/libopenage/screenshot.h +++ b/libopenage/screenshot.h @@ -9,6 +9,9 @@ #include "coord/window.h" namespace openage { +namespace renderer { +class Renderer; +} namespace job { class JobManager; @@ -19,32 +22,38 @@ class ScreenshotManager { /** * Initializes the screenshot manager with the given job manager. */ - ScreenshotManager(job::JobManager* job_mgr); + ScreenshotManager(job::JobManager* job_mgr, renderer::Renderer *renderer); ~ScreenshotManager(); - /** To be called to save a screenshot. */ - void save_screenshot(coord::window size); - - /** To be called by the job manager. Returns true on success, false otherwise. */ - bool encode_png(std::shared_ptr pxdata, - coord::window size); - + /** + * To be called to save a screenshot. + */ + void save_screenshot(); 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 job manager this screenshot manager uses */ job::JobManager *job_manager; + /** + * The renderer where to take screenshots from. + */ + class renderer::Renderer *renderer; }; } // openage From fdb85f62c8066cff5d43e603b81969443752c787 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Thu, 13 Aug 2015 03:13:44 +0200 Subject: [PATCH 12/52] 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 629a86e6397b643542b5362e3f1b2b9fa39bdc92 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Thu, 13 Aug 2015 20:48:09 +0200 Subject: [PATCH 13/52] 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 c53f4cdcd950203c1153707394b56343083f9669 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Wed, 26 Aug 2015 00:47:19 +0200 Subject: [PATCH 14/52] 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 594544f3abac353ca3e2551688b867b87021f2f4 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Tue, 15 Sep 2015 16:02:02 +0200 Subject: [PATCH 15/52] 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/renderer/CMakeLists.txt | 2 +- openage/renderer/tests.py | 5 --- openage/renderer/tests.pyx | 26 ++++++++++++++ openage/testing/testlist.py | 2 ++ 16 files changed, 156 insertions(+), 33 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/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 d22643c0f5..a203b871b2 100644 --- a/openage/testing/testlist.py +++ b/openage/testing/testlist.py @@ -46,6 +46,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 benchmark_py(): From 30940671a1359a4aedd811ef66c62e0227d5580b Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Tue, 15 Sep 2015 17:49:51 +0200 Subject: [PATCH 16/52] 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 a16d98938964c72dcce33dda64fa25a9e68124f9 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Wed, 16 Sep 2015 13:44:53 +0200 Subject: [PATCH 17/52] 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 b1b62b40f637d7f8cb8f74133fdaf7ef549b0c60 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Fri, 18 Sep 2015 02:03:19 +0200 Subject: [PATCH 18/52] 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 c903e313aa728a433ed0ecf5b90e7101b8fe9dd7 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Sun, 20 Sep 2015 03:56:58 +0200 Subject: [PATCH 19/52] 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 | 12 ++- libopenage/util/enum.h | 4 +- 28 files changed, 622 insertions(+), 167 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 802d1c6552..61363ed1b8 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..930e68e9f4 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. @@ -150,7 +161,6 @@ 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 diff --git a/libopenage/util/enum.h b/libopenage/util/enum.h index 82e7e8e37d..888cb30269 100644 --- a/libopenage/util/enum.h +++ b/libopenage/util/enum.h @@ -65,13 +65,13 @@ class OAAPI 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 6b4eaa73cbf533b3c1c46ab1639e0c76bae9cdfe Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Fri, 2 Oct 2015 17:57:06 +0200 Subject: [PATCH 20/52] 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/opengl.cpp | 74 -------------------------- libopenage/util/opengl.h | 16 ------ 25 files changed, 242 insertions(+), 181 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 89228f3a9a..3bd15d7611 100644 --- a/libopenage/engine.cpp +++ b/libopenage/engine.cpp @@ -22,7 +22,6 @@ #include "texture.h" #include "util/color.h" #include "util/fps.h" -#include "util/opengl.h" #include "util/strings.h" #include "util/timer.h" @@ -432,7 +431,7 @@ void Engine::loop() { } glPopMatrix(); - util::gl_check_error(); + this->renderer->check_error(); glPushMatrix(); { // the hud coordinate system is automatically established @@ -456,7 +455,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/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 23f2f3ffeed201f3d3d4ba8c27926b2ca44bd400 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Wed, 21 Oct 2015 10:26:52 +0200 Subject: [PATCH 21/52] 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 | 7 ++-- libopenage/renderer/color.h | 14 ++++--- 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 +++- 22 files changed, 156 insertions(+), 85 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 cac7f9a1ed..657631211f 100644 --- a/libopenage/renderer/color.cpp +++ b/libopenage/renderer/color.cpp @@ -10,11 +10,10 @@ 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}, diff --git a/libopenage/renderer/color.h b/libopenage/renderer/color.h index dfd44666b9..586dd78567 100644 --- a/libopenage/renderer/color.h +++ b/libopenage/renderer/color.h @@ -8,20 +8,24 @@ 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); bool operator==(const Color &other) const; - 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. From 0c1ef26b30e16ddd027adefdbaf2b03e0aa64191 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Fri, 6 Nov 2015 00:38:03 +0100 Subject: [PATCH 22/52] 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 23/52] 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 d48a9debc060a31d20632ceb02bffdf7f8fc30d8 Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Tue, 8 Nov 2016 23:02:03 +0000 Subject: [PATCH 24/52] engine: adjust new upstream changes to renderer branch changes --- libopenage/renderer/window.cpp | 4 ++++ libopenage/renderer/window.h | 9 +++++++++ 2 files changed, 13 insertions(+) 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 4f7993b70155537ffad25ba0c69e9a610d3907e4 Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Sat, 14 Jan 2017 15:28:32 +0100 Subject: [PATCH 25/52] renderer: resource loading refactor --- .../renderer/resources/shader_source.cpp | 36 ++++++ libopenage/renderer/resources/shader_source.h | 53 ++++++++ .../renderer/resources/texture_data.cpp | 117 ++++++++++++++++++ libopenage/renderer/resources/texture_data.h | 65 ++++++++++ libopenage/renderer/shader.cpp | 54 -------- libopenage/renderer/shader.h | 89 ------------- libopenage/renderer/texture.cpp | 91 -------------- libopenage/renderer/texture.h | 87 ------------- 8 files changed, 271 insertions(+), 321 deletions(-) create mode 100644 libopenage/renderer/resources/shader_source.cpp create mode 100644 libopenage/renderer/resources/shader_source.h create mode 100644 libopenage/renderer/resources/texture_data.cpp create mode 100644 libopenage/renderer/resources/texture_data.h delete mode 100644 libopenage/renderer/shader.cpp delete mode 100644 libopenage/renderer/shader.h diff --git a/libopenage/renderer/resources/shader_source.cpp b/libopenage/renderer/resources/shader_source.cpp new file mode 100644 index 0000000000..f0f9487e5b --- /dev/null +++ b/libopenage/renderer/resources/shader_source.cpp @@ -0,0 +1,36 @@ +// Copyright 2015-2017 the openage authors. See copying.md for legal info. + +/** @file + * common code for all shaders. + */ + +#include "shader_source.h" + +#include "../../error/error.h" +#include "../../util/file.h" + +namespace openage { +namespace renderer { +namespace resources { + +ShaderSource::ShaderSource(shader_type type, std::string &&code) + : type(type) + , code(std::move(code)) {} + +ShaderSource ShaderSource::from_file(shader_type type, std::experimental::string_view path) { + return ShaderSource(type, util::read_whole_file(path.data())); +} + +ShaderSource ShaderSource::from_string(shader_type type, std::experimental::string_view code) { + return ShaderSource(type, std::string(code)); +} + +shader_type ShaderSource::get_type() const { + return this->type; +} + +const char* ShaderSource::get_source() const { + return this->code.c_str(); +} + +}}} // openage::renderer::resources diff --git a/libopenage/renderer/resources/shader_source.h b/libopenage/renderer/resources/shader_source.h new file mode 100644 index 0000000000..df5de89e0c --- /dev/null +++ b/libopenage/renderer/resources/shader_source.h @@ -0,0 +1,53 @@ +// Copyright 2015-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include +#include +#include + + +/** @file + * gpu program creation classes reside in here. + */ +namespace openage { +namespace renderer { +namespace resources { + +/// Available shader types present in modern graphics pipelines. +enum class shader_type { + glsl_vertex, + glsl_geometry, + glsl_tesselation_control, + glsl_tesselation_evaluation, + glsl_fragment, +}; + +/// Shader source code storage. +class ShaderSource { +public: + /// Obtain shader source code from a file. + ShaderSource from_file(shader_type, std::experimental::string_view path); + + /// Obtain shader source code from a string. + ShaderSource from_string(shader_type, std::experimental::string_view code); + + /// @returns the shader source code + const char* get_source() const; + + /// @returns the shader type + shader_type get_type() const; + +private: + ShaderSource(shader_type type, std::string &&code); + +private: + /// The type of the source code. + shader_type type; + + /// Stores the shader source code. + std::string code; +}; + +}}} // openage::renderer::resources + diff --git a/libopenage/renderer/resources/texture_data.cpp b/libopenage/renderer/resources/texture_data.cpp new file mode 100644 index 0000000000..04360c7528 --- /dev/null +++ b/libopenage/renderer/resources/texture_data.cpp @@ -0,0 +1,117 @@ +// Copyright 2015-2017 the openage authors. See copying.md for legal info. + +#include "texture_data.h" + +#include +#include + +#include "../../log/log.h" +#include "../../error/error.h" +#include "../../util/file.h" + + +namespace openage { +namespace renderer { +namespace resources { + +TextureData TextureData::load_from_file(std::experimental::string_view filename, bool use_metafile) { + SDL_Surface *surface = IMG_Load(filename.data()); + + 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); + } + + auto fmt = *surface->format; + + if (fmt.Rmask != 0xf000 || fmt.Gmask != 0x0f00 || fmt.Bmask != 0x00f0) { + throw Error(MSG(err) << "Texture " << filename << " is not in RGB or RGBA format."); + } + + pixel_format format; + if (fmt.Amask == 0) { + if (fmt.BytesPerPixel != 3) { + throw Error(MSG(err) << "Texture " << filename << " is in an unsupported RGB format."); + } + + format = pixel_format::rgb8; + } + else { + format = pixel_format::rgba8; + } + + auto w = surface->w; + auto h = surface->h; + + size_t data_size = surface->format->BytesPerPixel * surface->w * surface->h; + + // copy pixel data from surface + std::vector data(data_size); + memcpy(data.data(), surface->pixels, data_size); + SDL_FreeSurface(surface); + + std::vector subtextures; + if (use_metafile) { + // change the suffix to .docx (lol) + std::string meta_filename(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 + ); + } + + meta_filename.replace(start_pos, 5, ".docx"); + meta_filename = meta_filename.substr(0, start_pos + 5); + + log::log(MSG(info) << "Loading meta file: " << meta_filename); + + // get subtexture information by meta file exported by script + util::read_csv_file(meta_filename, subtextures); + } + else { + // we don't have a texture description file. + // use the whole image as one texture then. + gamedata::subtexture s{0, 0, w, h, w/2, h/2}; + + subtextures.push_back(s); + } + + return TextureData(w, h, format, std::move(data), std::move(subtextures)); +} + +TextureData::TextureData(uint32_t width, uint32_t height, pixel_format fmt, uint8_t *data, std::vector &&subs) + : format{fmt} + , w{width} + , h{height} + , subtextures(std::move(subs)) +{ + size_t data_size = this->w * this->h; + switch (format) { + case pixel_format::rgb8: + data_size *= 3; + break; + case pixel_format::rgba8: + data_size *= 4; + break; + default: + throw Error(MSG(err) << "Unknown pixel format."); + } + + this->data = std::vector(data_size); + memcpy(this->data.data(), data, data_size); +} + +TextureData::TextureData(uint32_t width, uint32_t height, pixel_format fmt, std::vector &&data, std::vector &&subs) + : format{fmt} + , w{width} + , h{height} + , data(std::move(data)) + , subtextures(std::move(subs)) {} + +}}} diff --git a/libopenage/renderer/resources/texture_data.h b/libopenage/renderer/resources/texture_data.h new file mode 100644 index 0000000000..1d9a80be70 --- /dev/null +++ b/libopenage/renderer/resources/texture_data.h @@ -0,0 +1,65 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include +#include +#include +#include + +#include "../../gamedata/texture.gen.h" + + +namespace openage { +namespace renderer { +namespace resources { + +/// How the pixels are represented in the texture. +enum class pixel_format { + /// 24 bits per pixel, no alpha channel + rgb8, + /// 32 bits per pixel + rgba8, +}; + +/// A resource containing texture data. +class TextureData { +public: + /** + * Create a texture from an image file. + * @param[in] use_metafile determines whether the loading should read an accompanying + * metadata file to split the texture into subtextures + * + * Uses SDL Image internally. For supported image file types, + * see the SDL_Image initialization in the engine. + */ + static TextureData load_from_file(std::experimental::string_view filename, bool use_metafile=false); + +private: + /// Doesn't make sense to initialize texture data with nothing. + TextureData() = delete; + + /// Create a texture by copying the data from a C-style buffer. + TextureData(uint32_t width, uint32_t height, pixel_format, uint8_t *data, std::vector&&); + + /// Create a texture by moving the data. + TextureData(uint32_t width, uint32_t height, pixel_format, std::vector &&data, std::vector&&); + +private: + /// The pixel format of this texture. + pixel_format format; + + /// Width and height of this texture. + uint32_t w, h; + + /// Raw texture pixel data. + std::vector data; + + /** + * Some textures are merged together into texture atlases, large images which contain + * more than one individual texture. These are their positions in the atlas. + */ + std::vector subtextures; +}; + +}}} // namespace openage::renderer::resources diff --git a/libopenage/renderer/shader.cpp b/libopenage/renderer/shader.cpp deleted file mode 100644 index 385b159be0..0000000000 --- a/libopenage/renderer/shader.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// 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" -#include "../util/file.h" - -namespace openage { -namespace renderer { - -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} { - - this->code = util::read_whole_file(this->path); -} - - -ShaderSourceCode::ShaderSourceCode(shader_type type, const char *code) - : - ShaderSource{type} { - - this->code = code; -} - - -ShaderSourceCode::ShaderSourceCode(shader_type type, const std::string &code) - : - ShaderSource{type} { - - this->code = code; -} - -}} // openage::renderer diff --git a/libopenage/renderer/shader.h b/libopenage/renderer/shader.h deleted file mode 100644 index 6a9bfaf9bc..0000000000 --- a/libopenage/renderer/shader.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. - -#ifndef OPENAGE_RENDERER_SHADER_H_ -#define OPENAGE_RENDERER_SHADER_H_ - -/** @file - * gpu program creation classes reside in here. - */ - -#include -#include - -namespace openage { -namespace renderer { - -/** - * 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. - */ - const char *get_source() const; - - /** - * Return the shader type. - */ - shader_type get_type() const; - -protected: - /** - * The shader's pipeline stage position. - */ - shader_type type; - - /** - * Stores the shader source code. - */ - std::string code; -}; - - -/** - * Shader source code obtained from a file. - */ -class ShaderSourceFile : public ShaderSource { -public: - ShaderSourceFile(shader_type type, const std::string &path); - virtual ~ShaderSourceFile() {}; - -protected: - /** - * The shader's filesystem location. - * TODO: replace by some path magic - */ - std::string path; -}; - - -/** - * 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() {}; -}; - -}} // openage::renderer - -#endif diff --git a/libopenage/renderer/texture.cpp b/libopenage/renderer/texture.cpp index 37a2aab4b8..2a1c56e124 100644 --- a/libopenage/renderer/texture.cpp +++ b/libopenage/renderer/texture.cpp @@ -54,98 +54,7 @@ const std::tuple Texture::get_subtexture_size(size_t subid) const { } -TextureData::TextureData(int width, int height, char *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); -} - -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) - : - 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 a4c82e082c..e494e43617 100644 --- a/libopenage/renderer/texture.h +++ b/libopenage/renderer/texture.h @@ -16,13 +16,6 @@ namespace renderer { class Context; -/** - * Texture format, used for setting pixel data size. - */ -enum class texture_format { - rgb, - rgba, -}; /** @@ -94,84 +87,4 @@ class 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 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, std::unique_ptr 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; -}; - - -/** - * Create a texture from an image file. - * - * Uses SDL Image internally. - */ -class FileTextureData : public TextureData { -public: - /** - * Create a texture from a existing image file. - * - * For supported image file types, see the SDL_Image initialization in - * the engine. - */ - 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; -}; - -}} // openage::renderer - #endif From a36d4d5ba67a15757b12bd36486b21cd4a9cd444 Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Sun, 12 Mar 2017 00:32:17 +0000 Subject: [PATCH 26/52] renderer: begin major refactor --- libopenage/.clang_complete | 1 + libopenage/.dir-locals.el | 2 + libopenage/engine.cpp | 4 +- libopenage/renderer/CMakeLists.txt | 21 +- libopenage/renderer/buffer.h | 1 + libopenage/renderer/context.cpp | 1 + libopenage/renderer/context.h | 23 -- libopenage/renderer/doc.md | 34 ++ libopenage/renderer/docs/classes.uxf | 76 ++++ libopenage/renderer/geometry.cpp | 18 - libopenage/renderer/geometry.h | 39 +- libopenage/renderer/material.cpp | 13 - libopenage/renderer/material.h | 37 -- libopenage/renderer/opengl/CMakeLists.txt | 9 +- libopenage/renderer/opengl/context.cpp | 320 +++++++--------- libopenage/renderer/opengl/context.h | 128 ++----- libopenage/renderer/opengl/pipeline.cpp | 20 - libopenage/renderer/opengl/pipeline.h | 16 - libopenage/renderer/opengl/program.cpp | 238 ------------ libopenage/renderer/opengl/program.h | 145 ------- libopenage/renderer/opengl/renderer.cpp | 49 +++ libopenage/renderer/opengl/renderer.h | 41 ++ libopenage/renderer/opengl/scrapped.h | 65 ++++ libopenage/renderer/opengl/shader.cpp | 130 ------- libopenage/renderer/opengl/shader.h | 82 ---- libopenage/renderer/opengl/shader_program.cpp | 362 ++++++++++++++++++ libopenage/renderer/opengl/shader_program.h | 85 ++++ libopenage/renderer/opengl/uniform_input.h | 31 ++ libopenage/renderer/program.cpp | 47 --- libopenage/renderer/program.h | 121 ------ libopenage/renderer/quad.h | 21 - libopenage/renderer/renderer.cpp | 62 --- libopenage/renderer/renderer.h | 157 +++----- libopenage/renderer/resources/CMakeLists.txt | 4 + .../renderer/resources/shader_source.cpp | 40 +- libopenage/renderer/resources/shader_source.h | 29 +- libopenage/renderer/shader_program.h | 84 ++++ libopenage/renderer/target.h | 21 - libopenage/renderer/task.h | 56 ++- libopenage/renderer/tests.cpp | 189 ++++----- libopenage/renderer/texture.h | 19 +- libopenage/renderer/vulkan/context.h | 34 +- libopenage/renderer/window.cpp | 99 +++-- libopenage/renderer/window.h | 38 +- libopenage/screenshot.cpp | 2 +- 45 files changed, 1374 insertions(+), 1640 deletions(-) create mode 100644 libopenage/.clang_complete create mode 100644 libopenage/.dir-locals.el create mode 100644 libopenage/renderer/doc.md create mode 100644 libopenage/renderer/docs/classes.uxf delete mode 100644 libopenage/renderer/geometry.cpp delete mode 100644 libopenage/renderer/material.cpp delete mode 100644 libopenage/renderer/material.h delete mode 100644 libopenage/renderer/opengl/pipeline.cpp delete mode 100644 libopenage/renderer/opengl/pipeline.h delete mode 100644 libopenage/renderer/opengl/program.cpp delete mode 100644 libopenage/renderer/opengl/program.h create mode 100644 libopenage/renderer/opengl/renderer.cpp create mode 100644 libopenage/renderer/opengl/renderer.h create mode 100644 libopenage/renderer/opengl/scrapped.h delete mode 100644 libopenage/renderer/opengl/shader.cpp delete mode 100644 libopenage/renderer/opengl/shader.h create mode 100644 libopenage/renderer/opengl/shader_program.cpp create mode 100644 libopenage/renderer/opengl/shader_program.h create mode 100644 libopenage/renderer/opengl/uniform_input.h delete mode 100644 libopenage/renderer/program.cpp delete mode 100644 libopenage/renderer/program.h delete mode 100644 libopenage/renderer/quad.h delete mode 100644 libopenage/renderer/renderer.cpp create mode 100644 libopenage/renderer/resources/CMakeLists.txt create mode 100644 libopenage/renderer/shader_program.h delete mode 100644 libopenage/renderer/target.h diff --git a/libopenage/.clang_complete b/libopenage/.clang_complete new file mode 100644 index 0000000000..df98e0deec --- /dev/null +++ b/libopenage/.clang_complete @@ -0,0 +1 @@ +-std=c++14 diff --git a/libopenage/.dir-locals.el b/libopenage/.dir-locals.el new file mode 100644 index 0000000000..06b67b65fc --- /dev/null +++ b/libopenage/.dir-locals.el @@ -0,0 +1,2 @@ +((c++-mode . ((indent-tabs-mode . t) + (c-default-style . "k&r")))) diff --git a/libopenage/engine.cpp b/libopenage/engine.cpp index 3bd15d7611..ccd13cdf05 100644 --- a/libopenage/engine.cpp +++ b/libopenage/engine.cpp @@ -431,7 +431,7 @@ void Engine::loop() { } glPopMatrix(); - this->renderer->check_error(); + // this->renderer->check_error(); glPushMatrix(); { // the hud coordinate system is automatically established @@ -455,7 +455,7 @@ void Engine::loop() { } glPopMatrix(); - this->renderer->check_error(); + //this->renderer->check_error(); this->profiler.end_measure("rendering"); diff --git a/libopenage/renderer/CMakeLists.txt b/libopenage/renderer/CMakeLists.txt index 0a6bd15a74..38f383a894 100644 --- a/libopenage/renderer/CMakeLists.txt +++ b/libopenage/renderer/CMakeLists.txt @@ -1,20 +1,8 @@ add_sources(libopenage - buffer.cpp - color.cpp - context.cpp - context_state.cpp - material.cpp - pipeline.cpp - program.cpp - renderer.cpp - shader.cpp - task.cpp - tests.cpp - text.cpp - texture.cpp - vertex_buffer.cpp - vertex_state.cpp + tests.cpp window.cpp + text.cpp + color.cpp ) pxdgen( @@ -23,4 +11,5 @@ pxdgen( add_subdirectory(font/) add_subdirectory(opengl/) -add_subdirectory(shaders/) +add_subdirectory(resources/) +#add_subdirectory(shaders/) diff --git a/libopenage/renderer/buffer.h b/libopenage/renderer/buffer.h index 70eb543be8..aafd4bc266 100644 --- a/libopenage/renderer/buffer.h +++ b/libopenage/renderer/buffer.h @@ -34,6 +34,7 @@ class Buffer { dynamic_copy, }; + std::shared_ptr wow; Buffer(Context *ctx, size_t size=0); virtual ~Buffer() = default; diff --git a/libopenage/renderer/context.cpp b/libopenage/renderer/context.cpp index 56e1fea602..27277ba147 100644 --- a/libopenage/renderer/context.cpp +++ b/libopenage/renderer/context.cpp @@ -53,6 +53,7 @@ std::unique_ptr Context::generate(context_type t) { if (not WITH_VULKAN) { throw Error{MSG(err) << "Vulkan support not enabled!"}; } + // NB: vulkan does not actually have contexts #if WITH_VULKAN log::log(MSG(dbg) << "Using Vulkan context..."); return std::make_unique(); diff --git a/libopenage/renderer/context.h b/libopenage/renderer/context.h index dd2397e6c2..7306abee46 100644 --- a/libopenage/renderer/context.h +++ b/libopenage/renderer/context.h @@ -31,29 +31,6 @@ enum class context_type { 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 -}; - - -/** - * 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; -}; /** diff --git a/libopenage/renderer/doc.md b/libopenage/renderer/doc.md new file mode 100644 index 0000000000..01794de4fb --- /dev/null +++ b/libopenage/renderer/doc.md @@ -0,0 +1,34 @@ +The renderer implementation: + + + +First things first, we might want to support multiple APIs. For now just OpenGL, but maybe Vulkan or some others. +We want to abstract over these, but this can't unfortunately be done at the level of graphics primitives like +textures, buffers, etc. Well, it can, but it introduces unnecessary complexity and possible overhead. That is because +the next-gen (Vulkan, Metal, DX12) APIs are vastly different from the old ones - most importantly, they're bindless, +so something like a Vulkan context (GL notion) doesn't even make sense. We therefore choose to abstract on the higher +level of things-to-draw. + +This is the level-1 renderer. We want a separate rendering thread, so the api is thread-safe. It works similarly to +the Unity engine. The first part is resources. The user can submit resources to be uploaded to the gpu and receives +a numeric handle that identifies the uploaded resource. Resources can be added, updated and removed. Currently +supported resource types: mesh, texture. + +Then, we have to deal with shaders. They are more involved than resources, since each shader also carries a set of +uniforms. + +TODO describe MaterialValuations + +On top of that renderer, be build a level-2 renderer. This one has an api that is actually specific to openage, and isn't +threadsafe. That way it can share data with the simulation code and others. The level-2 renderer calls the level-1 renderer +and updates it to match the game state. + +Namespaces: + +openage::renderer - everything related to rendering +openage::renderer::opengl - rendering using OpenGL +openage::renderer::vulkan - rendering using Vulkan + +every successive namespace is an actual directory and all its classes are contained there + + diff --git a/libopenage/renderer/docs/classes.uxf b/libopenage/renderer/docs/classes.uxf new file mode 100644 index 0000000000..aee0e0c45f --- /dev/null +++ b/libopenage/renderer/docs/classes.uxf @@ -0,0 +1,76 @@ + + + 10 + + UMLClass + + 260 + 120 + 130 + 60 + + Window + + + + UMLClass + + 220 + 280 + 220 + 60 + + <<interface>> +/opengl::ContextProvider/ + + + + + Relation + + 320 + 170 + 30 + 130 + + lt=<<- + 10.0;110.0;10.0;10.0 + + + UMLClass + + 520 + 120 + 180 + 60 + + <<interface>> +/3DRenderer/ + + + + + UMLClass + + 520 + 280 + 250 + 60 + + opengl::Renderer +-- +instantiate(ContextProvider) + + + + Relation + + 600 + 170 + 30 + 130 + + lt=<<- + 10.0;10.0;10.0;110.0 + + diff --git a/libopenage/renderer/geometry.cpp b/libopenage/renderer/geometry.cpp deleted file mode 100644 index 1602aa0112..0000000000 --- a/libopenage/renderer/geometry.cpp +++ /dev/null @@ -1,18 +0,0 @@ -// 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 fae018ccd3..9606eaffef 100644 --- a/libopenage/renderer/geometry.h +++ b/libopenage/renderer/geometry.h @@ -1,7 +1,6 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. +// Copyright 2015-2017 the openage authors. See copying.md for legal info. -#ifndef OPENAGE_RENDERER_GEOMETRY_H_ -#define OPENAGE_RENDERER_GEOMETRY_H_ +#pragma once #include "vertex_buffer.h" #include "vertex_state.h" @@ -9,29 +8,21 @@ namespace openage { namespace renderer { -class Context; -class Material; +enum class geometry_t { + quad, + mesh, +}; -/** - * Drawable geometry, stores all triangles vertices. - */ class Geometry { -public: - Geometry(mesh_t vertices={}); - virtual ~Geometry() = default; - - /** - * Return the associated vertex list. - */ - const mesh_t &get_mesh(); - -protected: - /** - * Triangle vertex storage. - */ - mesh_t vertices; + /// The default constructor makes a quad. + Geometry() : type(geometry_t::quad) {} + + // Geometry(mesh..) + + geometry_t get_type() const { return this->type; } + +private: + geometry_t type; }; }} // openage::renderer - -#endif diff --git a/libopenage/renderer/material.cpp b/libopenage/renderer/material.cpp deleted file mode 100644 index 25e13d9bc9..0000000000 --- a/libopenage/renderer/material.cpp +++ /dev/null @@ -1,13 +0,0 @@ -// 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 deleted file mode 100644 index c327ce7d8b..0000000000 --- a/libopenage/renderer/material.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. - -#ifndef OPENAGE_RENDERER_MATERIAL_H_ -#define OPENAGE_RENDERER_MATERIAL_H_ - -#include - -#include "pipeline.h" -#include "../util/vector.h" - -namespace openage { -namespace renderer { - -/** - * 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; - - /** - * 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; - - virtual void set_positions(mesh_t positions) = 0; -}; - -}} // openage::renderer - -#endif diff --git a/libopenage/renderer/opengl/CMakeLists.txt b/libopenage/renderer/opengl/CMakeLists.txt index abff85271b..0b01ecee6d 100644 --- a/libopenage/renderer/opengl/CMakeLists.txt +++ b/libopenage/renderer/opengl/CMakeLists.txt @@ -1,9 +1,6 @@ add_sources(libopenage - buffer.cpp context.cpp - pipeline.cpp - program.cpp - shader.cpp - texture.cpp - vertex_state.cpp + renderable.cpp + renderer.cpp + shader_program.cpp ) diff --git a/libopenage/renderer/opengl/context.cpp b/libopenage/renderer/opengl/context.cpp index da6ed8f90f..6d50fb8958 100644 --- a/libopenage/renderer/opengl/context.cpp +++ b/libopenage/renderer/opengl/context.cpp @@ -1,59 +1,38 @@ -// Copyright 2015-2016 the openage authors. See copying.md for legal info. - -#include "../../config.h" -#if WITH_OPENGL +// Copyright 2015-2017 the openage authors. See copying.md for legal info. #include "context.h" -#include -#include - #include -#include -#include -#include "buffer.h" -#include "program.h" -#include "texture.h" -#include "vertex_state.h" #include "../../log/log.h" #include "../../error/error.h" + namespace openage { namespace renderer { namespace opengl { -Context::Context() - : - renderer::Context{context_type::opengl} {} +/// The first element is the lowest version we need, last is highest version we support. +static constexpr std::array, 1> gl_versions = {{ { 3, 3 } }}; // for now we don't need any higher versions -Context::~Context() {} - -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; +/// Finds out the supported graphics functions and OpenGL version of the device. +static gl_context_capabilities find_capabilities() { + // This is really hacky. We try to create a context starting with + // the lowest GL version and retry until one version is not supported and fails. + // There is no other way to do this. (https://gamedev.stackexchange.com/a/28457) 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 + // Check each version for availability SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GLContext test_context; 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); @@ -61,13 +40,13 @@ void Context::prepare() { 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."); + 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); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, gl_versions[i_ver - 1].first); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, gl_versions[i_ver - 1].second); break; } } @@ -79,62 +58,150 @@ void Context::prepare() { if (test_context == nullptr) { throw Error(MSG(err) << "Failed to create OpenGL context which previously succeeded. This should not happen! SDL Error: " << SDL_GetError()); } + SDL_GL_MakeCurrent(test_window, test_context); - auto &cap = this->capability; + gl_context_capabilities caps; - 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_MAX_TEXTURE_SIZE, &caps.max_texture_size); + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &caps.max_texture_slots); + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &caps.max_vertex_attributes); - glGetIntegerv(GL_MAJOR_VERSION, &cap.major_version); - glGetIntegerv(GL_MINOR_VERSION, &cap.minor_version); + glGetIntegerv(GL_MAJOR_VERSION, &caps.major_version); + glGetIntegerv(GL_MINOR_VERSION, &caps.minor_version); SDL_GL_DeleteContext(test_context); SDL_DestroyWindow(test_window); + + return caps; } -void Context::create(SDL_Window *window) { - this->glcontext = SDL_GL_CreateContext(window); +GlContext::GlContext(SDL_Window *window) { + this->capabilities = find_capabilities(); + auto const &capabilities = this->capabilities; - if (this->glcontext == nullptr) { - throw Error(MSG(err) << "Failed creating OpenGL context: " << SDL_GetError()); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, capabilities.major_version); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, capabilities.minor_version); + + this->gl_context = SDL_GL_CreateContext(window); + + if (this->gl_context == nullptr) { + throw Error(MSG(err) << "OpenGL context creation failed. SDL error: " << SDL_GetError()); } - // check the OpenGL version, for shaders n stuff - // 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 " - << this->capability.major_version << "." << this->capability.minor_version - << " not available"); + // We still have to verify that our version of libepoxy supports this version of OpenGL. + int epoxy_glv = capabilities.major_version * 10 + capabilities.minor_version; + if (!epoxy_is_desktop_gl() || epoxy_gl_version() < epoxy_glv) { + throw Error(MSG(err) << "The used version of libepoxy does not support OpenGL version " + << capabilities.major_version << "." << capabilities.minor_version); } - log::log(MSG(info) << "Using OpenGL " << this->capability.major_version << "." << this->capability.minor_version); -} + log::log(MSG(info) << "Created OpenGL context version " << capabilities.major_version << "." << capabilities.minor_version); -void Context::setup() { - // TODO: context capability checking - auto &caps = this->capability; - - // to quote the standard doc: 'The value gives a rough estimate of the + // 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. 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); + << capabilities.max_texture_size); + if (capabilities.max_texture_size < 1024) { + throw Error(MSG(err) << "Maximum supported texture size is too small: " + << capabilities.max_texture_size); } 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); + << capabilities.max_texture_slots); + if (capabilities.max_texture_slots < 2) { + throw Error(MSG(err) << "Your GPU doesn't have enough texture units: " + << capabilities.max_texture_slots); + } +} + +GlContext::~GlContext() { + if (this->gl_context != nullptr) { + SDL_GL_DeleteContext(this->gl_context); + } +} + +GlContext::GlContext(GlContext &&other) + : gl_context(other.gl_context) + , capabilities(other.capabilities) { + other.gl_context = nullptr; +} + +GlContext& GlContext::operator=(GlContext &&other) { + this->gl_context = other.gl_context; + this->capabilities = other.capabilities; + other.gl_context = nullptr; + + return *this; +} + +SDL_GLContext GlContext::get_raw_context() const { + return this->gl_context; +} + +gl_context_capabilities GlContext::get_capabilities() const { + return this->capabilities; +} + +void GlContext::check_error() { + GLenum error_state = glGetError(); + if (error_state != GL_NO_ERROR) { + const char *msg = [=] { + // generate error message + switch (error_state) { + 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. + return "invalid enum passed to opengl call"; + 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. + return "invalid value passed to opengl call"; + 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. + return "invalid operation performed during some state"; + 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. + return "invalid framebuffer operation"; + 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. + return "out of memory, wtf?"; + case GL_STACK_UNDERFLOW: + // An attempt has been made to perform an operation that would + // cause an internal stack to underflow. + return "stack underflow"; + case GL_STACK_OVERFLOW: + // An attempt has been made to perform an operation that would + // cause an internal stack to overflow. + return "stack overflow"; + default: + // unknown error state + return "unknown error"; + } + }(); + + throw Error( + MSG(err) << "An OpenGL error has occured.\n\t" + << "(" << error_state << "): " << msg + ); } +} +/* +void Context::setup() { + auto &caps = this->capabilities; // vsync on // TODO: maybe move somewhere else or to the window. @@ -153,18 +220,7 @@ void Context::setup() { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } - -context_capability Context::get_capabilities() { - return this->capability; -} - - -void Context::destroy() { - SDL_GL_DeleteContext(this->glcontext); -} - - -void Context::set_feature(context_feature feature, bool on) { +void GlContext::set_feature(context_feature feature, bool on) { // what feature to change? this is the argument to glEnable and glDisable. GLenum what; @@ -234,100 +290,6 @@ void Context::screenshot(const std::string &filename) { IMG_SavePNG(screen, filename.c_str()); SDL_FreeSurface(screen); } +*/ - -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; -} - -std::unique_ptr Context::register_program(const ProgramSource &data) { - 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; -} - -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); - - glViewport(0, 0, new_size.x, new_size.y); -} - - -}}} // namespace openage::renderer::opengl - -#endif // if WITH_OPENGL +}}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/context.h b/libopenage/renderer/opengl/context.h index edb6e9d0a6..edb7dbfd03 100644 --- a/libopenage/renderer/opengl/context.h +++ b/libopenage/renderer/opengl/context.h @@ -1,116 +1,50 @@ -// Copyright 2015-2016 the openage authors. See copying.md for legal info. +// Copyright 2015-2017 the openage authors. See copying.md for legal info. -#ifndef OPENAGE_RENDERER_OPENGL_CONTEXT_H_ -#define OPENAGE_RENDERER_OPENGL_CONTEXT_H_ +#pragma once #include -#include - -#include "../context.h" namespace openage { namespace renderer { - -/** - * OpenGL specific renderer code. - * Is selected if the requested backend is OpenGL. - */ namespace opengl { -class Context : public renderer::Context { -public: - Context(); - ~Context(); - - /** - * Called before the drawing window is created. - */ - void prepare() override; - - /** - * Returns the SDL window flags for the opengl window. - */ - uint32_t get_window_flags() const override; - - /** - * Actually creates the OpenGL context for the given SDL window. - */ - void create(SDL_Window *window) override; - - /** - * Setup calls for the newly created context. - * - * Enables opengl functions like blending etc. - */ - void setup() override; - - /** - * Deinitializes and unregisters the gl context from SDL2. - */ - void destroy() override; +/// Stores information about context capabilities and limitations. +struct gl_context_capabilities { + int max_vertex_attributes; + int max_texture_slots; + int max_texture_size; - /** - * use glEnable and glDisable to toggle a given feature. - */ - 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; - - /** - * Check if OpenGL detected any state machine errors. - */ - void check_error() override; - - /** - * Creates the opengl texture in this context. - * @returns a handle to it. - */ - 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::unique_ptr register_program(const ProgramSource &data) override; + int major_version; + int minor_version; +}; - /** - * Create a opengl buffer handle - * @returns the newly created state object. - */ - std::unique_ptr create_buffer(size_t size=0) override; +class GlContext { +public: + /// Create a GL context in the given SDL window. + explicit GlContext(SDL_Window*); + ~GlContext(); - /** - * Create an opengl vertex state tracker - */ - std::unique_ptr create_vertex_state() override; + /// It doesn't make sense to have more than one instance of the same context. + GlContext(const GlContext&) = delete; + GlContext& operator=(const GlContext&) = delete; + /// We have to support moving to avoid a clusterfuck somewhere else. + GlContext(GlContext&&); + GlContext& operator=(GlContext&&); - /** - * Return the available OpenGL context properties and limitations. - */ - context_capability get_capabilities() override; + SDL_GLContext get_raw_context() const; + gl_context_capabilities get_capabilities() const; -protected: - /** - * Resize the opengl viewport. - */ - void resize_canvas(const coord::window &new_size) override; + /// Checks whether the current GL context on this thread reported any errors + /// and throws an exception if it did. + static void check_error(); - /** - * SDL opengl context state. - */ - SDL_GLContext glcontext; +private: + /// Pointer to SDL struct representing the GL context. + SDL_GLContext gl_context; - /** - * The capability of this context. - */ - context_capability capability; + gl_context_capabilities capabilities; }; -}}} // namespace openage::renderer - -#endif +}}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/pipeline.cpp b/libopenage/renderer/opengl/pipeline.cpp deleted file mode 100644 index 5081509da7..0000000000 --- a/libopenage/renderer/opengl/pipeline.cpp +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. - -/** @file - * OpenGL specific pipeline code. - */ - -#include "../../config.h" -#if WITH_OPENGL - -#include "pipeline.h" - -namespace openage { -namespace renderer { -namespace opengl { - -// TODO - -}}} // openage::renderer::opengl - -#endif diff --git a/libopenage/renderer/opengl/pipeline.h b/libopenage/renderer/opengl/pipeline.h deleted file mode 100644 index ecd2964ce5..0000000000 --- a/libopenage/renderer/opengl/pipeline.h +++ /dev/null @@ -1,16 +0,0 @@ -// 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 { - -}}} // openage::renderer::opengl - -#endif diff --git a/libopenage/renderer/opengl/program.cpp b/libopenage/renderer/opengl/program.cpp deleted file mode 100644 index d30f853076..0000000000 --- a/libopenage/renderer/opengl/program.cpp +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright 2013-2015 the openage authors. See copying.md for legal info. - -#include "../../config.h" -#if WITH_OPENGL - -#include "program.h" - -#include "texture.h" -#include "../vertex_buffer.h" -#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(renderer::Context *context, const ProgramSource &source) - : - renderer::Program{context}, - 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() { - 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; - - 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::activate() { - if (unlikely(not this->is_linked)) { - throw Error{MSG(err) << "using program before it was linked!"}; - } - - glUseProgram(this->id); -} - -void Program::stopusing() { - glUseProgram((GLuint) 0); -} - -void Program::check_is_linked(const char *info) { - if (unlikely(not this->is_linked)) { - throw Error{MSG(err) << info << " before program was linked!"}; - } -} - -GLint Program::get_uniform_id(const char *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) { - 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_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)) { - throw Error{MSG(err) - << "you assigned attribute '" << name << " = " - << id << "' after program was linked!", true}; - } - else { - glBindAttribLocation(this->id, id, name); - } -} - -void Program::dump_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::set_uniform_3f(const char *name, const util::Vector<3> &value) { - 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_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) { - this->use(); - - // set the sampler "value" to the texture slot id. - GLint location = this->get_uniform_id(name); - - // TODO: use multiple slots! use context state tracking! - // TODO: slot assignage algorithm. - int slot = 0; - glUniform1i(location, slot); - texture->bind_to(slot); -} - -}}} // openage::renderer::opengl - -#endif // WITH_OPENGL diff --git a/libopenage/renderer/opengl/program.h b/libopenage/renderer/opengl/program.h deleted file mode 100644 index 6eea29bd53..0000000000 --- a/libopenage/renderer/opengl/program.h +++ /dev/null @@ -1,145 +0,0 @@ -// 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 - -#include "../program.h" -#include "shader.h" - -namespace openage { -namespace renderer { - -class Context; - -namespace opengl { - -class Program : public renderer::Program { -public: - /** - * A program is created from shader sources. - */ - Program(renderer::Context *context, const ProgramSource &source); - virtual ~Program(); - - /** - * Activate the program on the GPU. - */ - void activate() 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 layout id for a given vertex attribute name. - */ - int get_attribute_id(const char *name) override; - - /** - * 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. - */ - void dump_attributes() override; - - /* ====== */ - // shader variables - - /** - * Set a 3 dimensional float vector - */ - 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 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; - - -protected: - /** - * 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(); - - /** - * Don't use the program any more. - * Return to static pipeline configuration. - */ - void stopusing(); - - /** - * The OpenGL handle id for this program. - */ - GLuint id; - - /** - * True when this program was linked. - */ - bool is_linked; - - /** - * Shaders attached to this 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. - * - * @param what_to_check can be one of 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=""); - - /** - * Get information about the program. - */ - GLint get_info(GLenum pname); -}; - - -}}} // openage::renderer::opengl - -#endif diff --git a/libopenage/renderer/opengl/renderer.cpp b/libopenage/renderer/opengl/renderer.cpp new file mode 100644 index 0000000000..21a83fbbc4 --- /dev/null +++ b/libopenage/renderer/opengl/renderer.cpp @@ -0,0 +1,49 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#include "renderer.h" + +#include "renderable.h" +#include "../../error/error.h" + + +namespace openage { +namespace renderer { +namespace opengl { + +GlRenderer::GlRenderer(GlContext *ctx) + : gl_context(ctx) +{ + log::log(MSG(info) << "Created OpenGL renderer"); +} + +std::unique_ptr add_texture(resources::TextureData const&) { + +} + +std::unique_ptr add_shader(std::vector const& srcs) { + return std::make_unique(srcs); +} + +std::unique_ptr create_texture_target(Texture const*) { + +} + +RenderTarget const* get_framebuffer_target() { + return this->framebuffer.get(); +} + +void render(RenderPass const& pass) { + for (auto obj : pass.renderables) { + this->shaders[0].execute_with(*dynamic_cast(obj.get())); + } +} + + +Renderer::ShaderId GlRenderer::add_shader(const std::vector& srcs) { +} + +bool GlRenderer::remove_shader(Renderer::ShaderId id) { + +} + +}}} diff --git a/libopenage/renderer/opengl/renderer.h b/libopenage/renderer/opengl/renderer.h new file mode 100644 index 0000000000..8460a710af --- /dev/null +++ b/libopenage/renderer/opengl/renderer.h @@ -0,0 +1,41 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include + +#include + +#include "context.h" +#include "context_provider.h" +#include "../renderer.h" +#include "shader_program.h" + + +namespace openage { +namespace renderer { +namespace opengl { + +class GlRenderer : public Renderer { +public: + GlRenderer(GlContext*); + + std::unique_ptr add_texture(resources::TextureData const&) override; + + std::unique_ptr add_shader(std::vector const&) override; + + std::unique_ptr create_texture_target(Texture const*) override; + RenderTarget const* get_framebuffer_target() override; + + void render(RenderPass const&) override; + +private: + /* The GL context. */ + GlContext *gl_context; + + std::vector shaders; + + std::unique_ptr framebuffer; +}; + +}}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/scrapped.h b/libopenage/renderer/opengl/scrapped.h new file mode 100644 index 0000000000..6a7c3ae1fd --- /dev/null +++ b/libopenage/renderer/opengl/scrapped.h @@ -0,0 +1,65 @@ +/* +THIS IS AN EXTENSION TO UNIFORM PARSING +ALSO PARSE VERTEX ATTRIBUTES AND PROVIDE INFORMATION ABOUT THEM + +GLint Program::get_uniformbuffer_id(const char *name) { + this->check_is_linked("Uniform buffer requested"); + return glGetUniformBlockIndex(this->id, name); +} + +/// Return the opengl layout id for a given vertex attribute 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_attributes(); + throw Error{MSG(err) << "Attribute " << name + << " queried but not found or active" + << " (optimized out by the compiler?).", true}; + } + + return aid; +} + +/// Set vertex attribute with given name to have a custom id. +void Program::set_attribute_id(const char *name, GLuint id) { + if (unlikely(this->is_linked)) { + throw Error{MSG(err) + << "you assigned attribute '" << name << " = " + << id << "' after program was linked!", true}; + } + else { + glBindAttribLocation(this->id, id, name); + } +} + +/// Query OpenGL which of the vertex attributes are actually active +/// and haven't been optimized out by the compiler. +void Program::dump_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); +} +*/ diff --git a/libopenage/renderer/opengl/shader.cpp b/libopenage/renderer/opengl/shader.cpp deleted file mode 100644 index 446227e5d7..0000000000 --- a/libopenage/renderer/opengl/shader.cpp +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2013-2015 the openage authors. See copying.md for legal info. - -#include "../../config.h" -#if WITH_OPENGL - -#include "shader.h" - -#include "../../error/error.h" -#include "../../log/log.h" -#include "../../util/compiler.h" -#include "../../util/file.h" -#include "../../util/strings.h" - -namespace openage { -namespace renderer { -namespace opengl { - -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; - - 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); - - // 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); -} - -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) - : - 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 - -#endif // WITH_OPENGL diff --git a/libopenage/renderer/opengl/shader.h b/libopenage/renderer/opengl/shader.h deleted file mode 100644 index 4d0195218d..0000000000 --- a/libopenage/renderer/opengl/shader.h +++ /dev/null @@ -1,82 +0,0 @@ -// 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 - -#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 &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(); - - /** - * Return the shader type name. - */ - 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; -}; - - -class VertexShader : public Shader { -public: - VertexShader(const char *source); - virtual ~VertexShader() {}; -}; - - -class FragmentShader : public Shader { -public: - FragmentShader(const char *source); - virtual ~FragmentShader() {}; -}; - - -class GeometryShader : public Shader { -public: - GeometryShader(const char *source); - virtual ~GeometryShader() {}; -}; - - -}}} // openage::renderer::opengl - -#endif diff --git a/libopenage/renderer/opengl/shader_program.cpp b/libopenage/renderer/opengl/shader_program.cpp new file mode 100644 index 0000000000..72666bc01e --- /dev/null +++ b/libopenage/renderer/opengl/shader_program.cpp @@ -0,0 +1,362 @@ +// Copyright 2013-2017 the openage authors. See copying.md for legal info. + +#include "../../config.h" + +#include "shader_program.h" + +#include +#include +#include +#include + +#include "../../error/error.h" +#include "../../log/log.h" +#include "../../util/compiler.h" +#include "../../util/file.h" +#include "../../util/strings.h" +#include "renderable.h" + + +namespace openage { +namespace renderer { +namespace opengl { + +size_t uniform_size(gl_uniform_t type) { + switch (type) { + case gl_uniform_t::I32: + return 4; + case gl_uniform_t::U32: + return 4; + case gl_uniform_t::F32: + return 4; + case gl_uniform_t::F64: + return 8; + case gl_uniform_t::V2F32: + return 8; + case gl_uniform_t::V3F32: + return 12; + case gl_uniform_t::V4F32: + return 16; + case gl_uniform_t::TEX2D: + return 4; + } +} + +static gl_uniform_t glsl_str_to_type(std::experimental::string_view str) { + if (str == "int") + return gl_uniform_t::I32; + else if (str == "uint") + return gl_uniform_t::U32; + else if (str == "float") + return gl_uniform_t::F32; + else if (str == "double") + return gl_uniform_t::F64; + else if (str == "vec2") + return gl_uniform_t::V2F32; + else if (str == "vec3") + return gl_uniform_t::V3F32; + else if (str == "vec4") + return gl_uniform_t::V4F32; + else if (str == "sampler2D") + return gl_uniform_t::TEX2D; + else + throw Error(MSG(err) << "Unsupported GLSL uniform type " << str); +} + +void parse_glsl(std::unordered_map &uniforms, std::experimental::string_view code) { + // this will match all uniform declarations + std::regex unif_r("uniform\\s+\\w+\\s+\\w+(?=\\s*;)"); + std::regex const word_r("\\w+"); + + std::smatch results; + std::string s(code.data()); + if (regex_search(s, results, unif_r)) { + for (std::string result : results) { + // remove "uniform" + result = result.substr(7); + + std::smatch words; + regex_search(result, words, word_r); + + // first word is the type + gl_uniform_t type = glsl_str_to_type(words.str(0)); + + // second word is the uniform name + uniforms.emplace(words.str(1), type); + } + } +} + +static GLuint src_type_to_gl(resources::shader_source_t type) { + using resources::shader_source_t; + + switch (type) { + case shader_source_t::glsl_vertex: + return GL_VERTEX_SHADER; + case shader_source_t::glsl_geometry: + return GL_GEOMETRY_SHADER; + case shader_source_t::glsl_tesselation_control: + return GL_TESS_CONTROL_SHADER; + case shader_source_t::glsl_tesselation_evaluation: + return GL_TESS_EVALUATION_SHADER; + case shader_source_t::glsl_fragment: + return GL_FRAGMENT_SHADER; + default: + throw Error(MSG(err) << "Unsupported GLSL shader type."); + } +} + +static GLuint compile_shader(const resources::ShaderSource& src) { + // allocate shader in opengl + GLuint id = glCreateShader(src_type_to_gl(src.type())); + + if (unlikely(id == 0)) { + throw Error{MSG(err) << "Unable to create OpenGL shader. WTF?!", true}; + } + + // load shader source + const char* data = src.source().data(); + glShaderSource(id, 1, &data, 0); + + // compile shader source + glCompileShader(id); + + // check compiliation result + GLint status; + glGetShaderiv(id, GL_COMPILE_STATUS, &status); + + if (status != GL_TRUE) { + GLint loglen; + glGetShaderiv(id, GL_INFO_LOG_LENGTH, &loglen); + + std::vector infolog(loglen); + glGetShaderInfoLog(id, loglen, 0, infolog.data()); + + auto errmsg = MSG(err); + errmsg << "Failed to compile shader:\n" << infolog.data(); + + glDeleteShader(id); + + throw Error{errmsg, true}; + } + + return id; +} + +static void check_program_status(GLuint program, GLenum what_to_check) { + GLint status; + glGetProgramiv(program, what_to_check, &status); + + if (status != GL_TRUE) { + GLint loglen; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &loglen); + + std::vector infolog(loglen); + glGetProgramInfoLog(program, loglen, 0, infolog.data()); + + const char *what_str = [=] { + switch (what_to_check) { + case GL_LINK_STATUS: + return "linking"; + case GL_VALIDATE_STATUS: + return "validation"; + case GL_COMPILE_STATUS: + return "compilation"; + default: + return "[insert_task_here]"; + } + }(); + + throw Error(MSG(err) << "OpenGL shader program " << what_str << " failed:\n" << infolog.data(), true); + } +} + +GlShaderProgram::GlShaderProgram(const std::vector &srcs) { + this->id = glCreateProgram(); + + if (unlikely(this->id == 0)) { + throw Error(MSG(err) << "Unable to create OpenGL shader program. WTF?!"); + } + + std::unordered_map uniforms; + std::vector shaders; + for (auto src : srcs) { + parse_glsl(uniforms, src.source()); + shaders.push_back(compile_shader(src)); + } + + for (GLuint shdr : shaders) { + glAttachShader(this->id, shdr); + } + + glLinkProgram(this->id); + check_program_status(this->id, GL_LINK_STATUS); + + glValidateProgram(this->id); + check_program_status(this->id, GL_VALIDATE_STATUS); + + // after linking we can delete the shaders + for (GLuint shdr : shaders) { + glDetachShader(this->id, shdr); + glDeleteShader(shdr); + } + + // find the location of every uniform in the shader program + for (auto pair : uniforms) { + GLint loc = glGetUniformLocation(this->id, pair.first.data()); + + if (unlikely(loc == -1)) { + throw Error(MSG(err) << "Could not determine location of OpenGL shader uniform that was found before. WTF?!"); + } + + GlUniform unif { + pair.second, + loc + }; + + this->uniforms.emplace(pair.first, unif); + } + + // The uniform values are ordered in the byte buffer by however std::map orders their names + size_t offset = 0; + for (auto &pair : this->uniforms) { + pair.second.offset = offset; + offset += uniform_size(pair.second.type); + } + + log::log(MSG(info) << "Created OpenGL shader program"); +} + +GlShaderProgram::~GlShaderProgram() { + if (this->id != 0) { + glDeleteProgram(this->id); + } +} + +GlShaderProgram::GlShaderProgram(GlShaderProgram &&other) + : uniforms(std::move(other.uniforms)) + , id(other.id) { + other.id = 0; +} + +GlShaderProgram& GlShaderProgram::operator=(GlShaderProgram&& other) { + this->uniforms = std::move(other.uniforms); + this->id = other.id; + other.id = 0; + + return *this; +} + +void GlShaderProgram::use() const { + glUseProgram(this->id); +} + +void GlShaderProgram::execute_with(Renderable const& obj) { + assert(obj.unif_in->program == this); + + + auto const& unif_in = *static_cast(obj.unif_in); + uint8_t const* data = unif_in.update_data.data(); + + for (auto pair : unif_in->update_offs) { + uint8_t const* ptr = data + pair.second; + auto loc = this->uniforms[pair.first].location; + switch (this->uniforms[pair.first].type) { + case gl_uniform_t::I32: + glUniform1i(loc, *(int32_t*)ptr); + break; + case gl_uniform_t::U32: + glUniform1ui(loc, *(uint32_t*)ptr); + break; + case gl_uniform_t::F32: + glUniform1f(loc, *(float*)ptr); + break; + case gl_uniform_t::F64: + // TODO requires an extension + glUniform1d(loc, *(double*)ptr); + break; + case gl_uniform_t::V2F32: + glUniform2fv(loc, 1, (float*)ptr); + break; + case gl_uniform_t::V3F32: + glUniform3fv(loc, 1, (float*)ptr); + break; + case gl_uniform_t::V4F32: + glUniform4fv(loc, 1, (float*)ptr); + break; + case gl_uniform_t::TEX2D: + glUniform1i(loc, *(int32_t*)ptr); + break; + default: + throw Error(MSG(err) << "Tried to upload unknown uniform type to GL shader."); + } + } + + // TODO read obj.geometry and obj.blend + family + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); +} + +std::unique_ptr GlShaderProgram::new_unif_in() { + GlUniformInput *in = new GlUniformInput; + in->program = this; + return std::unique_ptr(in); +} + +bool GlShaderProgram::has_unif(const char* name) { + return this->uniforms.count(name) == 1; +} + +void GlShaderProgram::set_unif(UniformInput *in, const char *unif, void const* val) { + // will throw if uniform doesn't exist, that's ok + // TODO rethrow with nicer message? + auto const& unif_data = this->uniforms.at(unif); + + size_t size = uniform_size(unif_data.type); + + if (in->update_offs.count(unif) == 1) { + // already wrote to this uniform since last upload + size_t off = update_offs[unif]; + memcpy(in->update_data.data() + off, val, size); + } + else { + size_t prev_size = in->update_data.size(); + in->update_data.resize(prev_size + size); + memcpy(in->update_data.data() + prev_size, val, size); + in->update_offs.emplace(unif, prev_size); + } +} + +void GlShaderProgram::set_i32(UnifomInput *in, const char *unif, int32_t val) { + this->set_unif(in, unif, &val); +} + +void GlShaderProgram::set_u32(UnifomInput *in, const char *unif, uint32_t val) { + this->set_unif(in, unif, &val); +} + +void GlShaderProgram::set_f32(UnifomInput *in, const char *unif, float val) { + this->set_unif(in, unif, &val); +} + +void GlShaderProgram::set_f64(UnifomInput *in, const char *unif, double val) { + this->set_unif(in, unif, &val); +} + +void GlShaderProgram::set_v2f32(UnifomInput *in, const char *unif, Eigen::Vector2f const& val) { + this->set_unif(in, unif, &val); +} + +void GlShaderProgram::set_v3f32(UnifomInput *in, const char *unif, Eigen::Vector3f const& val) { + this->set_unif(in, unif, &val); +} + +void GlShaderProgram::set_v4f32(UnifomInput *in, const char *unif, Eigen::Vector4f const& val) { + this->set_unif(in, unif, &val); +} + +void GlShaderProgram::set_tex(UnifomInput *in, const char *unif, Texture const* val) { + // TODO special handling needed here + throw "unimplemented"; +} + +}}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/shader_program.h b/libopenage/renderer/opengl/shader_program.h new file mode 100644 index 0000000000..8743cfc73a --- /dev/null +++ b/libopenage/renderer/opengl/shader_program.h @@ -0,0 +1,85 @@ +// Copyright 2015-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include +#include +#include + +#include "../shader_program.h" +#include "../resources/shader_source.h" +#include "../../util/vector.h" +#include "../renderer.h" + + +namespace openage { +namespace renderer { +namespace opengl { + +class GlRenderable; + +/// GLSL uniform types. +enum class gl_uniform_t { + I32, + U32, + F32, + F64, + V2F32, + V3F32, + V4F32, + TEX2D, +}; + +/// @returns the size in bytes of a GLSL uniform type +size_t uniform_size(gl_uniform_t); + +struct GlUniform { + gl_uniform_t type; + GLint location; +}; + +class GlShaderProgram : public ShaderProgram { +public: + using str = std::experimental::string_view; + + /// Tries to create a shader program from the given sources. + /// Throws an exception on compile/link errors. + explicit GlShaderProgram(const std::vector&); + ~GlShaderProgram(); + + GlShaderProgram(const GlShaderProgram&) = delete; + GlShaderProgram& operator=(const GlShaderProgram&) = delete; + + GlShaderProgram(GlShaderProgram&&); + GlShaderProgram& operator=(GlShaderProgram&&); + + /// Bind this program as the currently used one in the OpenGL context. + void use() const; + + /// Renders the specified renderable using this shader program. + void execute_with(const UniformInput*); + + void set_unif(UniformInput*, const char*, void const*); + std::unique_ptr new_unif_in() override; + bool has_unif(const char*) override; + void set_i32(UniformInput*, const char*, int32_t) override; + void set_u32(UniformInput*, const char*, uint32_t) override; + void set_f32(UniformInput*, const char*, float) override; + void set_f64(UniformInput*, const char*, double) override; + void set_v2f32(UniformInput*, const char*, Eigen::Vector2f const&) override; + void set_v3f32(UniformInput*, const char*, Eigen::Vector3f const&) override; + void set_v4f32(UniformInput*, const char*, Eigen::Vector4f const&) override; + void set_tex(UniformInput*, const char*, Texture const*) override; + +private: + std::map uniforms; + + // TODO parse uniform buffer structure ugh + // std::unordered_map uniform_buffers; + // GlVertexInputInfo; + + /// The GL shader program ID + GLuint id; +}; + +}}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/uniform_input.h b/libopenage/renderer/opengl/uniform_input.h new file mode 100644 index 0000000000..5d5e4896e4 --- /dev/null +++ b/libopenage/renderer/opengl/uniform_input.h @@ -0,0 +1,31 @@ +// Copyright 2015-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include +#include + +#include "../renderer.h" + + +namespace openage { +namespace renderer { +namespace opengl { + +class GlShaderProgram; + +struct GlUniformInput : public UniformInput { + GlShaderProgram* program; + // store uniforms updates lazily + // execute_with reads, uploads, and clears + // std::unordered_map updates; + // enable partial valuations - only some uniforms + + /// Map from uniform names to their locations in the update buffer. + std::unordered_map update_offs; + + /// Buffer containing untyped uniform update data. + std::vector update_data; +}; + +}}} diff --git a/libopenage/renderer/program.cpp b/libopenage/renderer/program.cpp deleted file mode 100644 index aee2bede96..0000000000 --- a/libopenage/renderer/program.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. - -/** @file - * common code for all shader programs. - */ - -#include "program.h" - -#include "context.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); -} - - -Program::Program(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 deleted file mode 100644 index e74b96584c..0000000000 --- a/libopenage/renderer/program.h +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. - -#ifndef OPENAGE_RENDERER_PROGRAM_H_ -#define OPENAGE_RENDERER_PROGRAM_H_ - -#include -#include -#include - -#include "../util/vector.h" - -namespace openage { -namespace renderer { - -class Context; -class ShaderSource; -class Texture; -class VertexState; - - -/** - * 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(Context *context); - -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 activate() = 0; - - /** - * Dump vertex attribute variables. - */ - 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 - - /** - * Set a 3 dimensional float vector - */ - 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 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; - - /* ========================================== */ - - /** - * The associated context. - */ - Context *const context; -}; - -}} // openage::renderer - -#endif diff --git a/libopenage/renderer/quad.h b/libopenage/renderer/quad.h deleted file mode 100644 index c4bb2f839c..0000000000 --- a/libopenage/renderer/quad.h +++ /dev/null @@ -1,21 +0,0 @@ -// 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 deleted file mode 100644 index 88f574aba8..0000000000 --- a/libopenage/renderer/renderer.cpp +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. - -#include "renderer.h" - -#include "context.h" -#include "program.h" -#include "texture.h" - -namespace openage { -namespace renderer { - -Renderer::Renderer(const std::shared_ptr &ctx) - : - context{ctx} {} - -Renderer::~Renderer() {} - -TaskState Renderer::add_task(Task &task) { - // sort by: - // layer, texture, shader - this->tasks.push(task); - - TaskState ret{&task, this}; - return ret; -} - - -std::unique_ptr Renderer::add_program(const ProgramSource &source) { - return this->context->register_program(source); -} - - -std::unique_ptr Renderer::add_texture(const TextureData &data) { - return this->context->register_texture(data); -} - - -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; -} - - -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 6fd501239f..8a5205cafc 100644 --- a/libopenage/renderer/renderer.h +++ b/libopenage/renderer/renderer.h @@ -1,112 +1,73 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. +// Copyright 2015-2017 the openage authors. See copying.md for legal info. -#ifndef OPENAGE_RENDERER_RENDERER_H_ -#define OPENAGE_RENDERER_RENDERER_H_ +#pragma once -#include "../datastructure/pairing_heap.h" -#include "../handlers.h" -#include "task.h" +#include +#include +#include -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 openage { namespace renderer { -class Context; -class ProgramSource; -class Program; +namespace resources { + class TextureData; + class ShaderSource; +} + +// In terms of the graphics pipeline, there is no difference between APIs. The difference can be contained internally. +// ShaderProgram is API-independent. +class ShaderProgram; +class UniformInput {}; +class Geometry; class Texture; -class TextureData; -/** - * 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 ResizeHandler { +class RenderTarget { 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; - - /** - * 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(Task &task); - - /** - * 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::unique_ptr add_program(const ProgramSource &source); - - /** - * Register a texture to the renderer. - * Creates the context-aware handling structures. - * - * @returns a texture handle to be used in the code. - */ - std::unique_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); - - /** - * Test if the renderer has an invalid state. - */ - void check_error(); - - /** - * 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. - * When this is done, the front and backbuffer are flipped. - */ - void render() const; + virtual ~RenderTarget(); +}; -protected: - /** - * All tasks the renderer has to to display on the next drawout - */ - datastructure::PairingHeap tasks; - - /** - * The context to which the renderer is attached to. - */ - std::shared_ptr context; + + +// each renderable shoud be UniformInput, Geometry, State +// Geometry could be Billboard, Mesh, for now restrict to Billboard +// each object is a shader valuation + execution of draw with settings and optional mesh +// instancing for same shader & mesh +struct Renderable { + UniformInput const *unif_in; + Geometry const *geometry; + bool alpha_blending; + bool depth_test; + bool back_culling; + bool front_clockwise; +}; + +// what's a render pass? a series of draw calls that output into a texture +// it's a function similarly to a shader +// this time, inputs are shader invocations (with gl state vars such as blending for each invocation) +struct RenderPass { + std::vector renderables; + float default_depth; + uint8_t msaa_level; + RenderTarget const *target; }; -}} // namespace openage::renderer +class Renderer { +protected: + Renderer() {} + +public: + virtual ~Renderer() = default; + + virtual std::unique_ptr add_texture(resources::TextureData const&) = 0; + + virtual std::unique_ptr add_shader(std::vector const&) = 0; + + virtual std::unique_ptr create_texture_target(Texture const*) = 0; + virtual RenderTarget const* get_framebuffer_target() = 0; + + virtual void render(RenderPass const&) = 0; +}; -#endif +}} diff --git a/libopenage/renderer/resources/CMakeLists.txt b/libopenage/renderer/resources/CMakeLists.txt new file mode 100644 index 0000000000..0156ef1eb9 --- /dev/null +++ b/libopenage/renderer/resources/CMakeLists.txt @@ -0,0 +1,4 @@ +add_sources(libopenage + shader_source.cpp + texture_data.cpp +) diff --git a/libopenage/renderer/resources/shader_source.cpp b/libopenage/renderer/resources/shader_source.cpp index f0f9487e5b..f6609f7db6 100644 --- a/libopenage/renderer/resources/shader_source.cpp +++ b/libopenage/renderer/resources/shader_source.cpp @@ -1,36 +1,48 @@ // Copyright 2015-2017 the openage authors. See copying.md for legal info. -/** @file - * common code for all shaders. - */ - #include "shader_source.h" -#include "../../error/error.h" #include "../../util/file.h" + namespace openage { namespace renderer { namespace resources { +std::experimental::string_view shader_source_type_to_str(shader_source_t type) { + switch (type) { + case shader_source_t::glsl_vertex: + return "vertex shader"; + case shader_source_t::glsl_geometry: + return "geometry shader"; + case shader_source_t::glsl_tesselation_control: + return "tesselation control shader"; + case shader_source_t::glsl_tesselation_evaluation: + return "tesselation evaluation shader"; + case shader_source_t::glsl_fragment: + return "fragment shader"; + default: + return "unknown shader type"; + } +} -ShaderSource::ShaderSource(shader_type type, std::string &&code) - : type(type) +ShaderSource::ShaderSource(shader_source_t type, std::string &&code) + : _type(type) , code(std::move(code)) {} -ShaderSource ShaderSource::from_file(shader_type type, std::experimental::string_view path) { +ShaderSource ShaderSource::from_file(shader_source_t type, std::experimental::string_view path) { return ShaderSource(type, util::read_whole_file(path.data())); } -ShaderSource ShaderSource::from_string(shader_type type, std::experimental::string_view code) { - return ShaderSource(type, std::string(code)); +ShaderSource ShaderSource::from_string(shader_source_t type, std::string &&code) { + return ShaderSource(type, std::move(code)); } -shader_type ShaderSource::get_type() const { - return this->type; +shader_source_t ShaderSource::type() const { + return this->_type; } -const char* ShaderSource::get_source() const { - return this->code.c_str(); +std::experimental::string_view ShaderSource::source() const { + return this->code; } }}} // openage::renderer::resources diff --git a/libopenage/renderer/resources/shader_source.h b/libopenage/renderer/resources/shader_source.h index df5de89e0c..5deccd9aec 100644 --- a/libopenage/renderer/resources/shader_source.h +++ b/libopenage/renderer/resources/shader_source.h @@ -3,19 +3,16 @@ #pragma once #include -#include #include -/** @file - * gpu program creation classes reside in here. - */ namespace openage { namespace renderer { namespace resources { /// Available shader types present in modern graphics pipelines. -enum class shader_type { +/// Also contains information about the source language. +enum class shader_source_t { glsl_vertex, glsl_geometry, glsl_tesselation_control, @@ -23,31 +20,29 @@ enum class shader_type { glsl_fragment, }; -/// Shader source code storage. +std::experimental::string_view shader_source_type_to_str(shader_source_t); + class ShaderSource { public: /// Obtain shader source code from a file. - ShaderSource from_file(shader_type, std::experimental::string_view path); + static ShaderSource from_file(shader_source_t, std::experimental::string_view path); /// Obtain shader source code from a string. - ShaderSource from_string(shader_type, std::experimental::string_view code); + static ShaderSource from_string(shader_source_t, std::string &&code); - /// @returns the shader source code - const char* get_source() const; + /// @returns a view of the shader source code + std::experimental::string_view source() const; - /// @returns the shader type - shader_type get_type() const; + shader_source_t type() const; private: - ShaderSource(shader_type type, std::string &&code); + ShaderSource(shader_source_t type, std::string &&code); private: - /// The type of the source code. - shader_type type; + shader_source_t _type; - /// Stores the shader source code. + /// The shader source code. std::string code; }; }}} // openage::renderer::resources - diff --git a/libopenage/renderer/shader_program.h b/libopenage/renderer/shader_program.h new file mode 100644 index 0000000000..57f0e307bd --- /dev/null +++ b/libopenage/renderer/shader_program.h @@ -0,0 +1,84 @@ +// Copyright 2015-2017 the openage authors. See copying.md for legal info. + +#include + +#include + +#include "../error/error.h" + + +namespace openage { +namespace renderer { + +class UniformInput {}; +class Texture; + +class ShaderProgram { +private: + void update_uniform_input(UniformInput *input) {} + + void update_uniform_input(UniformInput *input, const char *unif, int32_t val) { + this->set_i32(input, unif, val); + } + + void update_uniform_input(UniformInput *input, const char *unif, uint32_t val) { + this->set_u32(input, unif, val); + } + + void update_uniform_input(UniformInput *input, const char *unif, float val) { + this->set_f32(input, unif, val); + } + + void update_uniform_input(UniformInput *input, const char *unif, double val) { + this->set_f64(input, unif, val); + } + + void update_uniform_input(UniformInput *input, const char *unif, Eigen::Vector2f const &val) { + this->set_v2f32(input, unif, val); + } + + void update_uniform_input(UniformInput *input, const char *unif, Eigen::Vector3f const &val) { + this->set_v3f32(input, unif, val); + } + + void update_uniform_input(UniformInput *input, const char *unif, Eigen::Vector4f const &val) { + this->set_v4f32(input, unif, val); + } + + template + void update_uniform_input(UniformInput*, const char*, T) { + throw Error(MSG(err) << "Unknown type.."); + } + +public: + virtual bool has_uniform(const char *unif) = 0; + + template + std::unique_ptr new_uniform_input(Ts... vals) { + auto input = this->new_unif_in(); + this->update_uniform_input(input.get(), vals...); + return input; + } + + template + void update_uniform_input(UniformInput *input, const char *unif, T val, Ts... vals) { + this->update_uniform_input(input, unif, val); + this->update_uniform_input(input, vals...); + } + + void new_geometry(/*geometry_t*/) {} + +private: + virtual std::unique_ptr new_unif_in() = 0; + virtual bool has_unif(const char*) = 0; + virtual void set_i32(UniformInput*, const char*, int32_t) = 0; + virtual void set_u32(UniformInput*, const char*, uint32_t) = 0; + virtual void set_f32(UniformInput*, const char*, float) = 0; + virtual void set_f64(UniformInput*, const char*, double) = 0; + virtual void set_v2f32(UniformInput*, const char*, Eigen::Vector2f const&) = 0; + virtual void set_v3f32(UniformInput*, const char*, Eigen::Vector3f const&) = 0; + virtual void set_v4f32(UniformInput*, const char*, Eigen::Vector4f const&) = 0; + virtual void set_tex(UniformInput*, const char*, Texture const*) = 0; +}; + +}} diff --git a/libopenage/renderer/target.h b/libopenage/renderer/target.h deleted file mode 100644 index 74fa608d83..0000000000 --- a/libopenage/renderer/target.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. - -#ifndef OPENAGE_RENDERER_TARGET_H_ -#define OPENAGE_RENDERER_TARGET_H_ - -#include "renderer.h" - -namespace openage { -namespace renderer { - -/** - * A render output destination. - */ -class Target { -public: - // TODO. -}; - -}} // openage::renderer - -#endif diff --git a/libopenage/renderer/task.h b/libopenage/renderer/task.h index ecb6a51634..397247b400 100644 --- a/libopenage/renderer/task.h +++ b/libopenage/renderer/task.h @@ -13,16 +13,68 @@ class Geometry; class Material; class Renderer; +class Uniform { +public: + + +private: + + void* value; +}; + +class UniformMap { +public: + static UniformMap defaults_from_shader(Shader const&); + + + void add_uniform(Uniform, value); + + void remove_uniform(Uniform); + + void set_uniform(); + +private: + map uniforms; + + shader; <- uniforms + + uniform values; <- pos, rot +}; /** * Instructs the renderer to draw something. */ class Task { public: + void set_geometry(Geometry const*); + + /** + * The concept of materials is as follows: + * - a material is defined as a mapping between shader uniforms and their values + * - more than one material can be applied to a model, provided there are no two materials map the same uniform to a value + * Materials can be, for example, used like this: + * one shared texture and color material between many similar-looking objects + * each object has its own transform matrix material + */ + void add_material(Material const*); + + + /** + * This immediately invalidates all attached materials and removes them. */ + void set_shader(Shader const*); + + /** + * This operation preserves all materials that fit the new shader. + */ + void set_shader_preserve_mappings(Shader const*); + + +private: + bool geometry_dirty; /** * The geometry to draw. */ - Geometry *const geometry; + Geometry const* geometry; /** * The material to use for drawing the geometry. @@ -31,11 +83,13 @@ class Task { */ //MaterialApplication material; + bool position_dirty; /** * Position to draw the geometry at. */ coord::phys3 position; + bool rotation_dirty; /** * Rotation for the geometry. */ diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index d5bf1e869e..7f839d2b63 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -6,125 +6,127 @@ #include #include "../log/log.h" -#include "opengl/shader.h" -#include "opengl/program.h" -#include "vertex_state.h" +#include "../error/error.h" +#include "resources/shader_source.h" +#include "opengl/renderer.h" +#include "opengl/renderable.h" #include "window.h" -#include "renderer.h" -#include "shader.h" -#include "shaders/simpletexture.h" -#include "texture.h" + namespace openage { namespace renderer { namespace tests { /** - * render demo function collection. - */ +* render demo function collection. +*/ struct render_demo { - std::function setup; - std::function frame; - std::function resize; +std::function setup; +std::function frame; +std::function resize; }; void render_test(Window &window, const render_demo *actions) { - 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) { - 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 - ); - window.set_size(new_size); - actions->resize(new_size); - break; - }} +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) { + 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 + ); + window.set_size(new_size); + actions->resize(new_size); break; + }} + break; + + case SDL_QUIT: + running = false; + break; - case SDL_QUIT: + case SDL_KEYUP: { + SDL_Keycode sym = reinterpret_cast(&event)->keysym.sym; + switch (sym) { + case SDLK_ESCAPE: running = false; break; - - case SDL_KEYUP: { - SDL_Keycode sym = reinterpret_cast(&event)->keysym.sym; - switch (sym) { - case SDLK_ESCAPE: - running = false; - break; - default: - break; - } + default: break; - }} - } + } + break; + }} + } - actions->frame(); + actions->frame(); - window.swap(); - } + window.swap(); +} } void renderer_demo_0() { - Window window{"openage renderer testing"}; - Renderer renderer{window.get_context()}; + Window window { "openage renderer testing" }; + window.make_context_current(); + auto renderer = opengl::GlRenderer::create(window.get_context()); - ShaderSourceCode vshader_src( - shader_type::vertex, + auto vshader_src = resources::ShaderSource::from_string( + resources::shader_source_t::glsl_vertex, "#version 330\n" - "layout(location = 0) in vec4 position;" - "smooth out vec4 fragpos;" + "smooth out vec2 pos;" "void main() {" - "fragpos = position;" - "gl_Position = position;" + " gl_Position.x = 2.0 * float(gl_VertexID & 1) - 1.0;" + " gl_Position.y = 2.0 * float((gl_VertexID & 2) >> 1) - 1.0;" + " gl_Position.z = 1.0;" + " gl_Position.w = 1.0;" + " gl_Position.xy *= 0.8;" + " pos = (vec2(gl_Position) + 1.0) / 2.0;" "}" ); - ShaderSourceCode fshader_src( - shader_type::fragment, + auto fshader_src = resources::ShaderSource::from_string( + resources::shader_source_t::glsl_fragment, "#version 330\n" - "out vec4 color;\n" - "smooth in vec4 fragpos;" + "smooth in vec2 pos;" + "out vec4 color;" "void main() {" - "color = vec4(1.0f, fragpos.y, fragpos.x, 1.0f);" + " color = vec4(1.0f, pos.y, pos.x, 1.0f);" "}" ); - ProgramSource simplequad_src({&vshader_src, &fshader_src}); - - std::unique_ptr simplequad = renderer.add_program(simplequad_src); - - simplequad->dump_attributes(); - - float val = 0.9f; - const float vpos[] = { - -val, -val, .0f, 1.0f, - val, -val, .0f, 1.0f, - -val, val, .0f, 1.0f, + auto shader = renderer->add_shader( { vshader_src, fshader_src } ); - val, -val, .0f, 1.0f, - -val, val, .0f, 1.0f, - val, val, .0f, 1.0f, + unif_in = shader->new_uniform_input(); + Renderable gaben { + unif_in.get(), + // geometry constructor + true, + true, + true, + true, }; - GLuint vpos_buf, posattr_id = 0; + RenderPass pass { + { gaben }, + 0.0f, + 8, + renderer->get_framebuffer_target(), + }; - GLuint vao; + renderer->execute_pass(pass); render_demo test0{ // init [&](Window */*window*/) { - glEnable(GL_BLEND); + /*glEnable(GL_BLEND); glGenBuffers(1, &vpos_buf); glBindBuffer(GL_ARRAY_BUFFER, vpos_buf); @@ -133,14 +135,13 @@ void renderer_demo_0() { glBindBuffer(GL_ARRAY_BUFFER, 0); glGenVertexArrays(1, &vao); - glBindVertexArray(vao); // stores all the vertex attrib state. + 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); - - simplequad->use(); + renderer->render(pass); + window.swap(); + /*simplequad->use(); glBindBuffer(GL_ARRAY_BUFFER, vpos_buf); glEnableVertexAttribArray(posattr_id); @@ -149,11 +150,12 @@ void renderer_demo_0() { glDisableVertexAttribArray(posattr_id); - renderer.check_error(); + renderer.check_error();*/ }, // resize [&](const coord::window &new_size) { - renderer.on_resize(new_size); + // handle in renderer.. + glViewport(0, 0, new_size.x, new_size.y); } }; @@ -161,9 +163,14 @@ void renderer_demo_0() { } -void renderer_demo_1() { + /*void renderer_demo_1() { Window window{"openage renderer testing"}; Renderer renderer{window.get_context()}; + // instead do + // Renderer renderer(&window); + // also do + // GuiRenderer gui(&window) using qtquick + // probably use shared_ptr for window so as to guarantee lifetimes ShaderSourceCode vshader_src( shader_type::vertex, @@ -202,7 +209,7 @@ void renderer_demo_1() { render_demo test1{ // init - [&](Window */*window*/) { + [&](Window * /*window*//*) { log::log(MSG(dbg) << "preparing test"); tex_pipeline.tex.set(gaben.get()); @@ -261,8 +268,7 @@ void renderer_demo_1() { }; render_test(window, &test1); -} - +}*/ void renderer_demo(int demo_id) { switch (demo_id) { @@ -270,15 +276,10 @@ void renderer_demo(int demo_id) { renderer_demo_0(); break; - case 1: - renderer_demo_1(); - break; - default: log::log(MSG(err) << "unknown renderer demo " << demo_id << " requested."); break; } } - }}} // namespace openage::renderer::tests diff --git a/libopenage/renderer/texture.h b/libopenage/renderer/texture.h index e494e43617..8c9d1d2a10 100644 --- a/libopenage/renderer/texture.h +++ b/libopenage/renderer/texture.h @@ -1,7 +1,6 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. +// Copyright 2015-2017 the openage authors. See copying.md for legal info. -#ifndef OPENAGE_RENDERER_TEXTURE_H_ -#define OPENAGE_RENDERER_TEXTURE_H_ +#pragma once #include @@ -11,13 +10,10 @@ #include "../gamedata/texture.gen.h" + namespace openage { namespace renderer { -class Context; - - - /** * A texture for rendering graphically in 3d space. * Obtained by registering some texture data to the renderer. @@ -67,12 +63,8 @@ class Texture { /** * Bind the texture to the given texture unit slot id. */ - virtual void bind_to(int slot) const = 0; +// virtual void bind_to(int slot) const = 0; - /** - * The associated graphics context. - */ - Context *context; protected: /** @@ -86,5 +78,4 @@ class Texture { size_t w, h; }; - -#endif +}} diff --git a/libopenage/renderer/vulkan/context.h b/libopenage/renderer/vulkan/context.h index bf8c0cc4bd..9fce119c20 100644 --- a/libopenage/renderer/vulkan/context.h +++ b/libopenage/renderer/vulkan/context.h @@ -1,40 +1,18 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. +// Copyright 2015-2017 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" +#pragma once 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. + * A Vulkan graphics device, usually a GPU. */ -class Context : public renderer::Context { +class VkDevice { 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; + VkDevice() {} }; -}} // namespace openage::renderer - -#endif +}}} // namespace openage::renderer::vulkan diff --git a/libopenage/renderer/window.cpp b/libopenage/renderer/window.cpp index 767dd16647..7f7f452c6e 100644 --- a/libopenage/renderer/window.cpp +++ b/libopenage/renderer/window.cpp @@ -1,4 +1,4 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. +// Copyright 2015-2017 the openage authors. See copying.md for legal info. #include "window.h" @@ -6,6 +6,7 @@ #include #include +#include "opengl/context.h" #include "../log/log.h" #include "../error/error.h" @@ -13,23 +14,55 @@ namespace openage { namespace renderer { -Window::Window(const char *title) - : - size{800, 600} { - // TODO: ^ detect screen resolution and determine window size from it. +class SDL { +public: + static void make_available() { + if (!inited) { + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + throw Error{MSG(err) << "SDL video initialization failed: " << SDL_GetError()}; + } + + // Initialize 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 initialize PNG support: " << IMG_GetError()}; + } + } + + sdl = SDL(); + + SDL_version version; + SDL_GetVersion(&version); - // TODO: make the type user-selectable - this->context = Context::generate(context_type::autodetect); + log::log(MSG(info) << "Initialized SDL " << uint32_t(version.major) << "." << uint32_t(version.minor) << "." << uint32_t(version.patch)); + } + + ~SDL() { + if (inited) { + IMG_Quit(); + SDL_Quit(); - 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."); + log::log(MSG(info) << "Exited SDL"); + } } - this->context->prepare(); +private: + static SDL sdl; + static bool inited; +}; - int32_t window_flags = this->context->get_window_flags() | SDL_WINDOW_RESIZABLE | SDL_WINDOW_MAXIMIZED; +bool SDL::inited = false; + +Window::Window(const char *title) + : size{800, 600} { + // TODO: ^ detect screen resolution and determine window size from it. + + SDL::make_available(); + + // Even with Vulkan rendering, we still need GL for QtQuick. + // We need HIGHDPI for eventual support of GUI scaling. (but it requires newer SDL2?) + int32_t window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_MAXIMIZED; //| SDL_WINDOW_HIGHDPI; this->window = SDL_CreateWindow( title, SDL_WINDOWPOS_CENTERED, @@ -41,57 +74,45 @@ Window::Window(const char *title) if (this->window == nullptr) { throw Error{MSG(err) << "Failed to create SDL window: " << SDL_GetError()}; - } else { - log::log(MSG(info) << "Created SDL window."); } - this->context->create(this->window); + log::log(MSG(info) << "Created SDL 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 Error{MSG(err) << "Failed to init PNG support: " << IMG_GetError()}; - } + this->context = opengl::GlContext(this->window); - this->context->setup(); + // TODO catch window events such as resizes } 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(); + log::log(MSG(info) << "Destroyed SDL window"); } void Window::swap() { SDL_GL_SwapWindow(this->window); } +void Window::make_context_current() { + SDL_GL_MakeCurrent(this->window, this->context->get_raw_context()); +} + 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) { +void Window::set_size(const coord::window &new_size) { + if (this->size.x != new_size.x || this->size.y != new_size.y) { + SDL_SetWindowSize(this->window, this->size.x, this->size.y); this->size = new_size; - if (update) { - SDL_SetWindowSize(this->window, this->size.x, this->size.y); - } } } -std::shared_ptr Window::get_context() { - return this->context; +opengl::GlContext* Window::get_context() { + return &this->context.value(); } SDL_Window* Window::get_raw_window() const { return this->window; } -}} // namespace openage::renderer +}} //openage::renderer diff --git a/libopenage/renderer/window.h b/libopenage/renderer/window.h index 63300c1b6d..218ac884c9 100644 --- a/libopenage/renderer/window.h +++ b/libopenage/renderer/window.h @@ -1,19 +1,22 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. +// Copyright 2015-2017 the openage authors. See copying.md for legal info. -#ifndef OPENAGE_RENDERER_WINDOW_H_ -#define OPENAGE_RENDERER_WINDOW_H_ - -#include "context.h" +#pragma once #include +#include +#include +#include + #include +#include "opengl/context_provider.h" +#include "opengl/context.h" #include "../coord/window.h" + namespace openage { namespace renderer { - class Window { public: Window(const Window &other) = delete; @@ -33,20 +36,20 @@ class Window { coord::window get_size(); /** - * Resize the drawing window. + * Resize the window's client area. + * @param[in] update determines what? */ - void set_size(const coord::window &new_size, bool update=false); + void set_size(const coord::window &new_size); /** * Swaps the back and front framebuffers. - * Used to actually display the newly rendered frame. + * Used to actually display the newly rendered frame on the screen. */ void swap(); - /** - * Return the context created for this window. - */ - std::shared_ptr get_context(); + /// Make this context the current rendering context of the window thread. + /// Only use this and most other GL functions on the window thread. + void make_context_current(); // TODO: this shouldn't be exposed. // It is only necessary because GuiBasic requires a raw SDL_Window handle, since the QtQuick @@ -58,13 +61,16 @@ class Window { */ SDL_Window* get_raw_window() const; + /** + * Return a reference to this window's GL context. + */ + opengl::GlContext* get_context(); + private: coord::window size; SDL_Window *window; - std::shared_ptr context; + std::experimental::optional context; }; }} // namespace openage::renderer - -#endif diff --git a/libopenage/screenshot.cpp b/libopenage/screenshot.cpp index c3878ef96d..4a78026fcd 100644 --- a/libopenage/screenshot.cpp +++ b/libopenage/screenshot.cpp @@ -50,7 +50,7 @@ std::string ScreenshotManager::gen_next_filename() { void ScreenshotManager::save_screenshot() { this->job_manager->enqueue([this] () { std::string filename = this->gen_next_filename(); - this->renderer->screenshot(filename); +// this->renderer->screenshot(filename); return true; }); } From 9354698e5d9c0237c28db6a83795d0dcdfdab85e Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Mon, 10 Apr 2017 10:24:16 +0200 Subject: [PATCH 27/52] engine: revert changes --- libopenage/audio/dynamic_resource.cpp | 1 - libopenage/engine.cpp | 22 ++++---- libopenage/engine.h | 33 ++++-------- libopenage/handlers.h | 2 + libopenage/util/compiler.h | 2 +- libopenage/util/enum.h | 4 +- libopenage/util/opengl.cpp | 73 +++++++++++++++++++++++++++ libopenage/util/opengl.h | 16 ++++++ 8 files changed, 118 insertions(+), 35 deletions(-) create mode 100644 libopenage/util/opengl.cpp create mode 100644 libopenage/util/opengl.h diff --git a/libopenage/audio/dynamic_resource.cpp b/libopenage/audio/dynamic_resource.cpp index 547908cd0a..831749a207 100644 --- a/libopenage/audio/dynamic_resource.cpp +++ b/libopenage/audio/dynamic_resource.cpp @@ -7,7 +7,6 @@ #include "../engine.h" #include "../job/job_manager.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 ccd13cdf05..c79fabfa98 100644 --- a/libopenage/engine.cpp +++ b/libopenage/engine.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "config.h" #include "error/error.h" @@ -22,10 +23,10 @@ #include "texture.h" #include "util/color.h" #include "util/fps.h" +#include "util/opengl.h" #include "util/strings.h" #include "util/timer.h" - /** * Main openage namespace to store all things that make the have to do with the game. * @@ -160,7 +161,7 @@ Engine::Engine(const util::Path &root_dir, // what gets drawn last is displayed on top. glDisable(GL_DEPTH_TEST); - this->window = std::make_unique(windowtitle); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //// -- initialize the gui util::Path qml_root = this->root_dir / "assets" / "qml"; @@ -204,7 +205,7 @@ Engine::Engine(const util::Path &root_dir, 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_coord_data()->window_size); + 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; @@ -254,6 +255,9 @@ bool Engine::on_resize(coord::window new_size) { // update engine window size this->coord.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->coord.camgame_window = this->coord.window_size / 2; @@ -431,7 +435,7 @@ void Engine::loop() { } glPopMatrix(); - // this->renderer->check_error(); + util::gl_check_error(); glPushMatrix(); { // the hud coordinate system is automatically established @@ -440,6 +444,7 @@ void Engine::loop() { if (this->drawing_debug_overlay.value) { this->draw_debug_overlay(); + } if (this->drawing_huds.value) { @@ -455,7 +460,7 @@ void Engine::loop() { } glPopMatrix(); - //this->renderer->check_error(); + util::gl_check_error(); this->profiler.end_measure("rendering"); @@ -463,7 +468,7 @@ void Engine::loop() { // the rendering is done // swap the drawing buffers to actually show the frame - this->window->swap(); + SDL_GL_SwapWindow(window); if (this->ns_per_frame != 0) { uint64_t ns_for_current_frame = cap_timer.getval(); @@ -472,7 +477,6 @@ void Engine::loop() { } } - // vsync wait time is over. this->profiler.end_measure("idle"); this->profiler.end_frame_measure(); @@ -515,8 +519,8 @@ audio::AudioManager &Engine::get_audio_manager() { return this->audio_manager; } -ScreenshotManager *Engine::get_screenshot_manager() { - return this->screenshot_manager.get(); +ScreenshotManager &Engine::get_screenshot_manager() { + return this->screenshot_manager; } input::ActionManager &Engine::get_action_manager() { diff --git a/libopenage/engine.h b/libopenage/engine.h index d598d1ec09..5656b63ab8 100644 --- a/libopenage/engine.h +++ b/libopenage/engine.h @@ -33,7 +33,6 @@ #include "util/strings.h" #include "screenshot.h" - namespace openage { namespace gui { @@ -41,29 +40,24 @@ class GUI; } namespace renderer { + class Font; class FontManager; class TextRenderer; -class Renderer; -class Window; -} // openage::renderer -namespace job { -class JobManager; -} // openage::job +} // openage::renderer class DrawHandler; class TickHandler; class ResizeHandler; -class Font; -class GameMain; - -class GameSpec; class Generator; -class Player; -class ScreenshotManager; +class GameSpec; +class GameMain; +namespace gui { +class GuiItemLink; +} // openage::gui /** @@ -243,7 +237,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. @@ -438,7 +432,7 @@ class Engine : public ResizeHandler, public options::OptionNode { * This is actually a void * but sdl2 thought it was a good idea to * name it like a differently. */ - std::unique_ptr window; + SDL_GLContext glcontext; /** * the gui binding @@ -446,9 +440,9 @@ class Engine : public ResizeHandler, public options::OptionNode { std::unique_ptr gui; /* - * The renderer. Accepts all tasks to be drawn on screen. + * the engines profiler */ - std::unique_ptr renderer; + util::Profiler profiler; /** * ttf font loading manager @@ -482,11 +476,6 @@ class Engine : public ResizeHandler, public options::OptionNode { * Link to the Qt GUI. */ gui::GuiItemLink *gui_link; - - /** - * the engines profiler - */ - util::Profiler profiler; }; } // namespace openage diff --git a/libopenage/handlers.h b/libopenage/handlers.h index 876fe16acf..a6b481b4bf 100644 --- a/libopenage/handlers.h +++ b/libopenage/handlers.h @@ -9,6 +9,8 @@ namespace openage { +class Engine; + /** * superclass for all possible drawing operations in the game. */ diff --git a/libopenage/util/compiler.h b/libopenage/util/compiler.h index 61363ed1b8..802d1c6552 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/enum.h b/libopenage/util/enum.h index 888cb30269..82e7e8e37d 100644 --- a/libopenage/util/enum.h +++ b/libopenage/util/enum.h @@ -65,13 +65,13 @@ class OAAPI 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) { diff --git a/libopenage/util/opengl.cpp b/libopenage/util/opengl.cpp new file mode 100644 index 0000000000..1cc8b31ee3 --- /dev/null +++ b/libopenage/util/opengl.cpp @@ -0,0 +1,73 @@ +// 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 new file mode 100644 index 0000000000..d1c3a2e300 --- /dev/null +++ b/libopenage/util/opengl.h @@ -0,0 +1,16 @@ +// 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 77cb2219f6b2d1964364c845115d197508a265ce Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Tue, 11 Apr 2017 08:41:07 +0200 Subject: [PATCH 28/52] renderer: first working version --- libopenage/.clang_complete | 1 + libopenage/engine.cpp | 5 +- libopenage/renderer/CMakeLists.txt | 1 + libopenage/renderer/geometry.h | 1 + libopenage/renderer/opengl/CMakeLists.txt | 4 +- libopenage/renderer/opengl/renderer.cpp | 30 +++-- libopenage/renderer/opengl/renderer.h | 1 - libopenage/renderer/opengl/shader_program.cpp | 89 +++++++------- libopenage/renderer/opengl/shader_program.h | 11 +- libopenage/renderer/opengl/texture.cpp | 38 +++--- libopenage/renderer/opengl/texture.h | 29 +++-- libopenage/renderer/renderer.h | 15 ++- .../renderer/resources/shader_source.cpp | 11 +- libopenage/renderer/resources/shader_source.h | 7 +- .../renderer/resources/texture_data.cpp | 100 +++++++++------- libopenage/renderer/resources/texture_data.h | 83 +++++++++---- libopenage/renderer/shader_program.h | 7 +- libopenage/renderer/tests.cpp | 25 ++-- libopenage/renderer/texture.cpp | 50 +------- libopenage/renderer/texture.h | 56 +-------- libopenage/renderer/window.h | 1 - libopenage/screenshot.cpp | 110 +++++++++++++++--- libopenage/screenshot.h | 33 ++---- 23 files changed, 363 insertions(+), 345 deletions(-) diff --git a/libopenage/.clang_complete b/libopenage/.clang_complete index df98e0deec..9b5869f672 100644 --- a/libopenage/.clang_complete +++ b/libopenage/.clang_complete @@ -1 +1,2 @@ -std=c++14 +-Wno-pragma-once-outside-header \ No newline at end of file diff --git a/libopenage/engine.cpp b/libopenage/engine.cpp index c79fabfa98..88fbd01bd7 100644 --- a/libopenage/engine.cpp +++ b/libopenage/engine.cpp @@ -205,7 +205,7 @@ Engine::Engine(const util::Path &root_dir, 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(this->get_coord_data()->window_size); }); 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; @@ -255,9 +255,6 @@ bool Engine::on_resize(coord::window new_size) { // update engine window size this->coord.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->coord.camgame_window = this->coord.window_size / 2; diff --git a/libopenage/renderer/CMakeLists.txt b/libopenage/renderer/CMakeLists.txt index 38f383a894..1a68c4bdbf 100644 --- a/libopenage/renderer/CMakeLists.txt +++ b/libopenage/renderer/CMakeLists.txt @@ -1,5 +1,6 @@ add_sources(libopenage tests.cpp + texture.cpp window.cpp text.cpp color.cpp diff --git a/libopenage/renderer/geometry.h b/libopenage/renderer/geometry.h index 9606eaffef..659ce632b8 100644 --- a/libopenage/renderer/geometry.h +++ b/libopenage/renderer/geometry.h @@ -14,6 +14,7 @@ enum class geometry_t { }; class Geometry { +public: /// The default constructor makes a quad. Geometry() : type(geometry_t::quad) {} diff --git a/libopenage/renderer/opengl/CMakeLists.txt b/libopenage/renderer/opengl/CMakeLists.txt index 0b01ecee6d..1da53ddc4a 100644 --- a/libopenage/renderer/opengl/CMakeLists.txt +++ b/libopenage/renderer/opengl/CMakeLists.txt @@ -1,6 +1,6 @@ add_sources(libopenage - context.cpp - renderable.cpp + context.cpp renderer.cpp shader_program.cpp + texture.cpp ) diff --git a/libopenage/renderer/opengl/renderer.cpp b/libopenage/renderer/opengl/renderer.cpp index 21a83fbbc4..7507cd58d4 100644 --- a/libopenage/renderer/opengl/renderer.cpp +++ b/libopenage/renderer/opengl/renderer.cpp @@ -2,8 +2,10 @@ #include "renderer.h" -#include "renderable.h" #include "../../error/error.h" +#include "texture.h" +#include "shader_program.h" +#include "uniform_input.h" namespace openage { @@ -16,34 +18,28 @@ GlRenderer::GlRenderer(GlContext *ctx) log::log(MSG(info) << "Created OpenGL renderer"); } -std::unique_ptr add_texture(resources::TextureData const&) { - +std::unique_ptr GlRenderer::add_texture(const resources::TextureData& data) { + return std::make_unique(data); } -std::unique_ptr add_shader(std::vector const& srcs) { +std::unique_ptr GlRenderer::add_shader(std::vector const& srcs) { return std::make_unique(srcs); } -std::unique_ptr create_texture_target(Texture const*) { - +std::unique_ptr GlRenderer::create_texture_target(Texture const* tex) { + return std::unique_ptr(); + //return std::make_unique(tex); } -RenderTarget const* get_framebuffer_target() { +RenderTarget const* GlRenderer::get_framebuffer_target() { return this->framebuffer.get(); } -void render(RenderPass const& pass) { +void GlRenderer::render(RenderPass const& pass) { for (auto obj : pass.renderables) { - this->shaders[0].execute_with(*dynamic_cast(obj.get())); + auto in = dynamic_cast(obj.unif_in); + in->program->execute_with(in, obj.geometry); } } - -Renderer::ShaderId GlRenderer::add_shader(const std::vector& srcs) { -} - -bool GlRenderer::remove_shader(Renderer::ShaderId id) { - -} - }}} diff --git a/libopenage/renderer/opengl/renderer.h b/libopenage/renderer/opengl/renderer.h index 8460a710af..c84cc53557 100644 --- a/libopenage/renderer/opengl/renderer.h +++ b/libopenage/renderer/opengl/renderer.h @@ -7,7 +7,6 @@ #include #include "context.h" -#include "context_provider.h" #include "../renderer.h" #include "shader_program.h" diff --git a/libopenage/renderer/opengl/shader_program.cpp b/libopenage/renderer/opengl/shader_program.cpp index 72666bc01e..c8c1c0932b 100644 --- a/libopenage/renderer/opengl/shader_program.cpp +++ b/libopenage/renderer/opengl/shader_program.cpp @@ -14,7 +14,6 @@ #include "../../util/compiler.h" #include "../../util/file.h" #include "../../util/strings.h" -#include "renderable.h" namespace openage { @@ -63,26 +62,33 @@ static gl_uniform_t glsl_str_to_type(std::experimental::string_view str) { throw Error(MSG(err) << "Unsupported GLSL uniform type " << str); } -void parse_glsl(std::unordered_map &uniforms, std::experimental::string_view code) { +void parse_glsl(std::map &uniforms, const char *code) { // this will match all uniform declarations std::regex unif_r("uniform\\s+\\w+\\s+\\w+(?=\\s*;)"); std::regex const word_r("\\w+"); - std::smatch results; - std::string s(code.data()); - if (regex_search(s, results, unif_r)) { - for (std::string result : results) { + std::cmatch results; + if (regex_search(code, results, unif_r)) { + for (auto result : results) { + std::string sresult(result); + // remove "uniform" - result = result.substr(7); + sresult = sresult.substr(7); std::smatch words; - regex_search(result, words, word_r); + regex_search(sresult, words, word_r); // first word is the type gl_uniform_t type = glsl_str_to_type(words.str(0)); // second word is the uniform name - uniforms.emplace(words.str(1), type); + uniforms.insert(std::pair( + words.str(1), + GlUniform { + type, + 0, + } + )); } } } @@ -115,7 +121,7 @@ static GLuint compile_shader(const resources::ShaderSource& src) { } // load shader source - const char* data = src.source().data(); + const char* data = src.source(); glShaderSource(id, 1, &data, 0); // compile shader source @@ -178,10 +184,9 @@ GlShaderProgram::GlShaderProgram(const std::vector &src throw Error(MSG(err) << "Unable to create OpenGL shader program. WTF?!"); } - std::unordered_map uniforms; std::vector shaders; for (auto src : srcs) { - parse_glsl(uniforms, src.source()); + parse_glsl(this->uniforms, src.source()); shaders.push_back(compile_shader(src)); } @@ -202,26 +207,14 @@ GlShaderProgram::GlShaderProgram(const std::vector &src } // find the location of every uniform in the shader program - for (auto pair : uniforms) { + for (auto& pair : this->uniforms) { GLint loc = glGetUniformLocation(this->id, pair.first.data()); if (unlikely(loc == -1)) { throw Error(MSG(err) << "Could not determine location of OpenGL shader uniform that was found before. WTF?!"); } - GlUniform unif { - pair.second, - loc - }; - - this->uniforms.emplace(pair.first, unif); - } - - // The uniform values are ordered in the byte buffer by however std::map orders their names - size_t offset = 0; - for (auto &pair : this->uniforms) { - pair.second.offset = offset; - offset += uniform_size(pair.second.type); + pair.second.location = loc; } log::log(MSG(info) << "Created OpenGL shader program"); @@ -251,13 +244,12 @@ void GlShaderProgram::use() const { glUseProgram(this->id); } -void GlShaderProgram::execute_with(Renderable const& obj) { - assert(obj.unif_in->program == this); - +void GlShaderProgram::execute_with(const GlUniformInput *unif_in, const Geometry *geom) { + assert(unif_in->program == this); - auto const& unif_in = *static_cast(obj.unif_in); - uint8_t const* data = unif_in.update_data.data(); + this->use(); + uint8_t const* data = unif_in->update_data.data(); for (auto pair : unif_in->update_offs) { uint8_t const* ptr = data + pair.second; auto loc = this->uniforms[pair.first].location; @@ -292,7 +284,7 @@ void GlShaderProgram::execute_with(Renderable const& obj) { } } - // TODO read obj.geometry and obj.blend + family + // TODO read geom and obj.blend + family glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } @@ -302,59 +294,60 @@ std::unique_ptr GlShaderProgram::new_unif_in() { return std::unique_ptr(in); } -bool GlShaderProgram::has_unif(const char* name) { +bool GlShaderProgram::has_uniform(const char* name) { return this->uniforms.count(name) == 1; } void GlShaderProgram::set_unif(UniformInput *in, const char *unif, void const* val) { + GlUniformInput *unif_in = static_cast(in); // will throw if uniform doesn't exist, that's ok // TODO rethrow with nicer message? auto const& unif_data = this->uniforms.at(unif); size_t size = uniform_size(unif_data.type); - if (in->update_offs.count(unif) == 1) { + if (unif_in->update_offs.count(unif) == 1) { // already wrote to this uniform since last upload - size_t off = update_offs[unif]; - memcpy(in->update_data.data() + off, val, size); + size_t off = unif_in->update_offs[unif]; + memcpy(unif_in->update_data.data() + off, val, size); } else { - size_t prev_size = in->update_data.size(); - in->update_data.resize(prev_size + size); - memcpy(in->update_data.data() + prev_size, val, size); - in->update_offs.emplace(unif, prev_size); + size_t prev_size = unif_in->update_data.size(); + unif_in->update_data.resize(prev_size + size); + memcpy(unif_in->update_data.data() + prev_size, val, size); + unif_in->update_offs.emplace(unif, prev_size); } } -void GlShaderProgram::set_i32(UnifomInput *in, const char *unif, int32_t val) { +void GlShaderProgram::set_i32(UniformInput *in, const char *unif, int32_t val) { this->set_unif(in, unif, &val); } -void GlShaderProgram::set_u32(UnifomInput *in, const char *unif, uint32_t val) { +void GlShaderProgram::set_u32(UniformInput *in, const char *unif, uint32_t val) { this->set_unif(in, unif, &val); } -void GlShaderProgram::set_f32(UnifomInput *in, const char *unif, float val) { +void GlShaderProgram::set_f32(UniformInput *in, const char *unif, float val) { this->set_unif(in, unif, &val); } -void GlShaderProgram::set_f64(UnifomInput *in, const char *unif, double val) { +void GlShaderProgram::set_f64(UniformInput *in, const char *unif, double val) { this->set_unif(in, unif, &val); } -void GlShaderProgram::set_v2f32(UnifomInput *in, const char *unif, Eigen::Vector2f const& val) { +void GlShaderProgram::set_v2f32(UniformInput *in, const char *unif, Eigen::Vector2f const& val) { this->set_unif(in, unif, &val); } -void GlShaderProgram::set_v3f32(UnifomInput *in, const char *unif, Eigen::Vector3f const& val) { +void GlShaderProgram::set_v3f32(UniformInput *in, const char *unif, Eigen::Vector3f const& val) { this->set_unif(in, unif, &val); } -void GlShaderProgram::set_v4f32(UnifomInput *in, const char *unif, Eigen::Vector4f const& val) { +void GlShaderProgram::set_v4f32(UniformInput *in, const char *unif, Eigen::Vector4f const& val) { this->set_unif(in, unif, &val); } -void GlShaderProgram::set_tex(UnifomInput *in, const char *unif, Texture const* val) { +void GlShaderProgram::set_tex(UniformInput *in, const char *unif, Texture const* val) { // TODO special handling needed here throw "unimplemented"; } diff --git a/libopenage/renderer/opengl/shader_program.h b/libopenage/renderer/opengl/shader_program.h index 8743cfc73a..e8dd19f91d 100644 --- a/libopenage/renderer/opengl/shader_program.h +++ b/libopenage/renderer/opengl/shader_program.h @@ -10,14 +10,13 @@ #include "../resources/shader_source.h" #include "../../util/vector.h" #include "../renderer.h" +#include "uniform_input.h" namespace openage { namespace renderer { namespace opengl { -class GlRenderable; - /// GLSL uniform types. enum class gl_uniform_t { I32, @@ -40,8 +39,6 @@ struct GlUniform { class GlShaderProgram : public ShaderProgram { public: - using str = std::experimental::string_view; - /// Tries to create a shader program from the given sources. /// Throws an exception on compile/link errors. explicit GlShaderProgram(const std::vector&); @@ -57,11 +54,13 @@ class GlShaderProgram : public ShaderProgram { void use() const; /// Renders the specified renderable using this shader program. - void execute_with(const UniformInput*); + void execute_with(const GlUniformInput*, const Geometry*); + + bool has_uniform(const char*) override; +protected: void set_unif(UniformInput*, const char*, void const*); std::unique_ptr new_unif_in() override; - bool has_unif(const char*) override; void set_i32(UniformInput*, const char*, int32_t) override; void set_u32(UniformInput*, const char*, uint32_t) override; void set_f32(UniformInput*, const char*, float) override; diff --git a/libopenage/renderer/opengl/texture.cpp b/libopenage/renderer/opengl/texture.cpp index f78c08a386..ca2ae750ac 100644 --- a/libopenage/renderer/opengl/texture.cpp +++ b/libopenage/renderer/opengl/texture.cpp @@ -1,37 +1,33 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. - -#include "../../config.h" -#if WITH_OPENGL +// Copyright 2015-2017 the openage authors. See copying.md for legal info. #include "texture.h" #include #include "../../error/error.h" +#include "../resources/texture_data.h" namespace openage { namespace renderer { namespace opengl { - -Texture::Texture(renderer::Context *context, const TextureData &txt) - : - renderer::Texture{context} { - +GlTexture::GlTexture(const resources::TextureData& data) + : Texture(data.get_info()) { // generate opengl texture handle glGenTextures(1, &this->id); glBindTexture(GL_TEXTURE_2D, id); - int input_format, output_format; + GLint input_format; + GLenum output_format; // select pixel format - switch (txt.format) { - case texture_format::rgb: + switch (this->info.get_format()) { + case resources::pixel_format::rgb8: input_format = GL_RGB8; output_format = GL_RGB; break; - case texture_format::rgba: + case resources::pixel_format::rgba8: input_format = GL_RGBA8; output_format = GL_RGBA; break; @@ -40,29 +36,23 @@ Texture::Texture(renderer::Context *context, const TextureData &txt) } // store raw pixels to gpu + auto size = this->info.get_size(); glTexImage2D( GL_TEXTURE_2D, 0, - input_format, txt.w, txt.h, 0, - output_format, GL_UNSIGNED_BYTE, txt.data.get() + input_format, size.first, size.second, 0, + output_format, GL_UNSIGNED_BYTE, data.get_data() ); // drawing settings + // TODO these are outdated, use sampler 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() { +GlTexture::~GlTexture() { glDeleteTextures(1, &this->id); } -void Texture::bind_to(int slot) const { - glActiveTexture(GL_TEXTURE0 + slot); - glBindTexture(GL_TEXTURE_2D, this->id); -} - - }}} // openage::renderer::opengl - -#endif diff --git a/libopenage/renderer/opengl/texture.h b/libopenage/renderer/opengl/texture.h index a84a36131e..b1953e48be 100644 --- a/libopenage/renderer/opengl/texture.h +++ b/libopenage/renderer/opengl/texture.h @@ -1,37 +1,34 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. +// Copyright 2015-2017 the openage authors. See copying.md for legal info. -#ifndef OPENAGE_RENDERER_OPENGL_TEXTURE_H_ -#define OPENAGE_RENDERER_OPENGL_TEXTURE_H_ +#pragma once #include #include "../texture.h" + namespace openage { namespace renderer { + +namespace resources { +class TextureData; +} + namespace opengl { /** - * An OpenGL texture. + * A handle to an OpenGL texture. */ -class Texture : public renderer::Texture { +class GlTexture : public Texture { public: - Texture(renderer::Context *context, const TextureData &data); - ~Texture(); - - /** - * Bind this texture to the given slot id. - */ - void bind_to(int slot) const override; + GlTexture(const resources::TextureData&); + ~GlTexture(); -protected: +private: /** * OpenGL handle id. */ GLuint id; }; - }}} // openage::renderer::opengl - -#endif diff --git a/libopenage/renderer/renderer.h b/libopenage/renderer/renderer.h index 8a5205cafc..2d6b2123d0 100644 --- a/libopenage/renderer/renderer.h +++ b/libopenage/renderer/renderer.h @@ -18,16 +18,24 @@ namespace resources { // In terms of the graphics pipeline, there is no difference between APIs. The difference can be contained internally. // ShaderProgram is API-independent. class ShaderProgram; -class UniformInput {}; class Geometry; class Texture; +class UniformInput { +protected: + UniformInput() {} -class RenderTarget { public: - virtual ~RenderTarget(); + virtual ~UniformInput() {} }; +class RenderTarget { +protected: + RenderTarget() {} + +public: + virtual ~RenderTarget() {} +}; // each renderable shoud be UniformInput, Geometry, State @@ -36,6 +44,7 @@ class RenderTarget { // instancing for same shader & mesh struct Renderable { UniformInput const *unif_in; + // can be nullptr to only set uniforms but do not perform draw call Geometry const *geometry; bool alpha_blending; bool depth_test; diff --git a/libopenage/renderer/resources/shader_source.cpp b/libopenage/renderer/resources/shader_source.cpp index f6609f7db6..d2e9c4a6ce 100644 --- a/libopenage/renderer/resources/shader_source.cpp +++ b/libopenage/renderer/resources/shader_source.cpp @@ -8,7 +8,7 @@ namespace openage { namespace renderer { namespace resources { -std::experimental::string_view shader_source_type_to_str(shader_source_t type) { +const char *shader_source_type_to_str(shader_source_t type) { switch (type) { case shader_source_t::glsl_vertex: return "vertex shader"; @@ -29,8 +29,9 @@ ShaderSource::ShaderSource(shader_source_t type, std::string &&code) : _type(type) , code(std::move(code)) {} -ShaderSource ShaderSource::from_file(shader_source_t type, std::experimental::string_view path) { - return ShaderSource(type, util::read_whole_file(path.data())); +ShaderSource ShaderSource::from_file(shader_source_t type, const char *path) { + util::File file(path); + return ShaderSource(type, file.read()); } ShaderSource ShaderSource::from_string(shader_source_t type, std::string &&code) { @@ -41,8 +42,8 @@ shader_source_t ShaderSource::type() const { return this->_type; } -std::experimental::string_view ShaderSource::source() const { - return this->code; +const char *ShaderSource::source() const { + return this->code.data(); } }}} // openage::renderer::resources diff --git a/libopenage/renderer/resources/shader_source.h b/libopenage/renderer/resources/shader_source.h index 5deccd9aec..99ffb7b41e 100644 --- a/libopenage/renderer/resources/shader_source.h +++ b/libopenage/renderer/resources/shader_source.h @@ -3,7 +3,6 @@ #pragma once #include -#include namespace openage { @@ -20,18 +19,18 @@ enum class shader_source_t { glsl_fragment, }; -std::experimental::string_view shader_source_type_to_str(shader_source_t); +const char *shader_source_type_to_str(shader_source_t); class ShaderSource { public: /// Obtain shader source code from a file. - static ShaderSource from_file(shader_source_t, std::experimental::string_view path); + static ShaderSource from_file(shader_source_t, const char *path); /// Obtain shader source code from a string. static ShaderSource from_string(shader_source_t, std::string &&code); /// @returns a view of the shader source code - std::experimental::string_view source() const; + const char *source() const; shader_source_t type() const; diff --git a/libopenage/renderer/resources/texture_data.cpp b/libopenage/renderer/resources/texture_data.cpp index 04360c7528..f1f4436048 100644 --- a/libopenage/renderer/resources/texture_data.cpp +++ b/libopenage/renderer/resources/texture_data.cpp @@ -7,15 +7,51 @@ #include "../../log/log.h" #include "../../error/error.h" -#include "../../util/file.h" +#include "../../util/csv.h" namespace openage { namespace renderer { namespace resources { -TextureData TextureData::load_from_file(std::experimental::string_view filename, bool use_metafile) { - SDL_Surface *surface = IMG_Load(filename.data()); +pixel_format TextureInfo::get_format() const { + return this->format; +} + +std::pair TextureInfo::get_size() const { + return std::make_pair(this->w, this->h); +} + +size_t TextureInfo::get_subtexture_count() const { + return this->subtextures.size(); +} + +const gamedata::subtexture& TextureInfo::get_subtexture(size_t subid) const { + if (subid < this->subtextures.size()) { + return this->subtextures[subid]; + } + else { + throw Error(MSG(err) << "Unknown subtexture requested: " << subid); + } +} + +std::tuple TextureInfo::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 + ); +} + +std::pair TextureInfo::get_subtexture_size(size_t subid) const { + auto subtex = this->get_subtexture(subid); + return std::make_pair(subtex.w, subtex.h); +} + +TextureData::TextureData(const char *filename, bool use_metafile) { + SDL_Surface *surface = IMG_Load(filename); if (!surface) { throw Error(MSG(err) << @@ -31,29 +67,28 @@ TextureData TextureData::load_from_file(std::experimental::string_view filename, throw Error(MSG(err) << "Texture " << filename << " is not in RGB or RGBA format."); } - pixel_format format; + auto& info = this->info; if (fmt.Amask == 0) { if (fmt.BytesPerPixel != 3) { throw Error(MSG(err) << "Texture " << filename << " is in an unsupported RGB format."); } - format = pixel_format::rgb8; + info.format = pixel_format::rgb8; } else { - format = pixel_format::rgba8; + info.format = pixel_format::rgba8; } - auto w = surface->w; - auto h = surface->h; + info.w = surface->w; + info.h = surface->h; size_t data_size = surface->format->BytesPerPixel * surface->w * surface->h; // copy pixel data from surface - std::vector data(data_size); - memcpy(data.data(), surface->pixels, data_size); + this->data = std::vector(data_size); + memcpy(this->data.data(), surface->pixels, data_size); SDL_FreeSurface(surface); - std::vector subtextures; if (use_metafile) { // change the suffix to .docx (lol) std::string meta_filename(filename); @@ -72,46 +107,27 @@ TextureData TextureData::load_from_file(std::experimental::string_view filename, log::log(MSG(info) << "Loading meta file: " << meta_filename); // get subtexture information by meta file exported by script - util::read_csv_file(meta_filename, subtextures); + // TODO wot + //util::File file(meta_filename); + //info.subtextures = util::read_csv_file(file); } else { // we don't have a texture description file. // use the whole image as one texture then. - gamedata::subtexture s{0, 0, w, h, w/2, h/2}; + gamedata::subtexture s{0, 0, info.w, info.h, info.w/2, info.h/2}; - subtextures.push_back(s); + info.subtextures.push_back(s); } - - return TextureData(w, h, format, std::move(data), std::move(subtextures)); } -TextureData::TextureData(uint32_t width, uint32_t height, pixel_format fmt, uint8_t *data, std::vector &&subs) - : format{fmt} - , w{width} - , h{height} - , subtextures(std::move(subs)) -{ - size_t data_size = this->w * this->h; - switch (format) { - case pixel_format::rgb8: - data_size *= 3; - break; - case pixel_format::rgba8: - data_size *= 4; - break; - default: - throw Error(MSG(err) << "Unknown pixel format."); - } - - this->data = std::vector(data_size); - memcpy(this->data.data(), data, data_size); +const TextureInfo& TextureData::get_info() const { + return this->info; } -TextureData::TextureData(uint32_t width, uint32_t height, pixel_format fmt, std::vector &&data, std::vector &&subs) - : format{fmt} - , w{width} - , h{height} - , data(std::move(data)) - , subtextures(std::move(subs)) {} +const uint8_t *TextureData::get_data() const { + return this->data.data(); +} }}} + + diff --git a/libopenage/renderer/resources/texture_data.h b/libopenage/renderer/resources/texture_data.h index 1d9a80be70..fd5874ccb9 100644 --- a/libopenage/renderer/resources/texture_data.h +++ b/libopenage/renderer/resources/texture_data.h @@ -5,7 +5,6 @@ #include #include #include -#include #include "../../gamedata/texture.gen.h" @@ -14,7 +13,7 @@ namespace openage { namespace renderer { namespace resources { -/// How the pixels are represented in the texture. +/// How the pixels are represented in a texture. enum class pixel_format { /// 24 bits per pixel, no alpha channel rgb8, @@ -22,38 +21,54 @@ enum class pixel_format { rgba8, }; -/// A resource containing texture data. -class TextureData { +/** + * Information for texture processing. + * 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 TextureInfo { + friend class TextureData; public: + pixel_format get_format() const; + /** - * Create a texture from an image file. - * @param[in] use_metafile determines whether the loading should read an accompanying - * metadata file to split the texture into subtextures - * - * Uses SDL Image internally. For supported image file types, - * see the SDL_Image initialization in the engine. + * Return the dimensions of the whole texture bitmap + * @returns tuple(width, height) */ - static TextureData load_from_file(std::experimental::string_view filename, bool use_metafile=false); + std::pair get_size() const; -private: - /// Doesn't make sense to initialize texture data with nothing. - TextureData() = delete; + /** + * @return the number of available subtextures + */ + size_t get_subtexture_count() const; - /// Create a texture by copying the data from a C-style buffer. - TextureData(uint32_t width, uint32_t height, pixel_format, uint8_t *data, std::vector&&); + /** + * Get the subtexture coordinates by its idea. + */ + const gamedata::subtexture& get_subtexture(size_t subid) const; + + /** + * Fetch the size of the given subtexture. + * @param subid: index of the requested subtexture + */ + std::pair get_subtexture_size(size_t subid) const; - /// Create a texture by moving the data. - TextureData(uint32_t width, uint32_t height, pixel_format, std::vector &&data, std::vector&&); + /** + * 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 surface size. + */ + std::tuple get_subtexture_coordinates(size_t subid) const; -private: +protected: /// The pixel format of this texture. pixel_format format; /// Width and height of this texture. - uint32_t w, h; - - /// Raw texture pixel data. - std::vector data; + int32_t w, h; /** * Some textures are merged together into texture atlases, large images which contain @@ -62,4 +77,26 @@ class TextureData { std::vector subtextures; }; +class TextureData { +public: + /** + * Create a texture from an image file. + * @param[in] use_metafile determines whether the loading should read an accompanying + * metadata file to split the texture into subtextures + * + * Uses SDL Image internally. For supported image file types, + * see the SDL_Image initialization in the engine. + */ + TextureData(const char *filename, bool use_metafile = false); + + const TextureInfo& get_info() const; + + const uint8_t *get_data() const; + +private: + TextureInfo info; + std::vector data; + +}; + }}} // namespace openage::renderer::resources diff --git a/libopenage/renderer/shader_program.h b/libopenage/renderer/shader_program.h index 57f0e307bd..d3e12f71a2 100644 --- a/libopenage/renderer/shader_program.h +++ b/libopenage/renderer/shader_program.h @@ -10,12 +10,12 @@ namespace openage { namespace renderer { -class UniformInput {}; +class UniformInput; class Texture; class ShaderProgram { private: - void update_uniform_input(UniformInput *input) {} + void update_uniform_input(UniformInput*) {} void update_uniform_input(UniformInput *input, const char *unif, int32_t val) { this->set_i32(input, unif, val); @@ -68,9 +68,8 @@ class ShaderProgram { void new_geometry(/*geometry_t*/) {} -private: +protected: virtual std::unique_ptr new_unif_in() = 0; - virtual bool has_unif(const char*) = 0; virtual void set_i32(UniformInput*, const char*, int32_t) = 0; virtual void set_u32(UniformInput*, const char*, uint32_t) = 0; virtual void set_f32(UniformInput*, const char*, float) = 0; diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index 7f839d2b63..651d10115b 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -6,10 +6,10 @@ #include #include "../log/log.h" +#include "geometry.h" #include "../error/error.h" #include "resources/shader_source.h" #include "opengl/renderer.h" -#include "opengl/renderable.h" #include "window.h" @@ -44,7 +44,7 @@ while (running) { MSG(info) << "new window size: " << new_size.x << " x " << new_size.y ); - window.set_size(new_size); + //window.set_size(new_size); actions->resize(new_size); break; }} @@ -76,7 +76,7 @@ while (running) { void renderer_demo_0() { Window window { "openage renderer testing" }; window.make_context_current(); - auto renderer = opengl::GlRenderer::create(window.get_context()); + auto renderer = std::make_unique(window.get_context()); auto vshader_src = resources::ShaderSource::from_string( resources::shader_source_t::glsl_vertex, @@ -104,10 +104,11 @@ void renderer_demo_0() { auto shader = renderer->add_shader( { vshader_src, fshader_src } ); - unif_in = shader->new_uniform_input(); + auto unif_in = shader->new_uniform_input(); + Geometry quad; Renderable gaben { unif_in.get(), - // geometry constructor + &quad, true, true, true, @@ -121,26 +122,28 @@ void renderer_demo_0() { renderer->get_framebuffer_target(), }; - renderer->execute_pass(pass); - render_demo test0{ // init [&](Window */*window*/) { - /*glEnable(GL_BLEND); + glEnable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + GLuint vpos_buf; 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); + GLuint vao; glGenVertexArrays(1, &vao); glBindVertexArray(vao); // stores all the vertex attrib state.*/ }, // frame [&]() { + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); renderer->render(pass); window.swap(); + window.get_context()->check_error(); /*simplequad->use(); glBindBuffer(GL_ARRAY_BUFFER, vpos_buf); diff --git a/libopenage/renderer/texture.cpp b/libopenage/renderer/texture.cpp index 2a1c56e124..98f013c576 100644 --- a/libopenage/renderer/texture.cpp +++ b/libopenage/renderer/texture.cpp @@ -1,60 +1,22 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. +// Copyright 2015-2017 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 { -Texture::Texture(Context *ctx) - : - context{ctx} { -} +Texture::Texture(resources::TextureInfo info) + : info(info) {} Texture::~Texture() {} -const 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 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); +const resources::TextureInfo& Texture::get_info() const { + return this->info; } - - - - }} // namespace openage::renderer diff --git a/libopenage/renderer/texture.h b/libopenage/renderer/texture.h index 8c9d1d2a10..3ebd18d9e0 100644 --- a/libopenage/renderer/texture.h +++ b/libopenage/renderer/texture.h @@ -9,73 +9,27 @@ #include #include "../gamedata/texture.gen.h" +#include "resources/texture_data.h" namespace openage { namespace renderer { /** - * A texture for rendering graphically in 3d space. + * A handle to texture data that has been uploaded to the GPU. * 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(Context *ctx); + Texture(resources::TextureInfo); public: virtual ~Texture(); - /** - * Return the dimensions of the whole texture bitmap - * @returns tuple(width, height) - */ - const 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 surface size. - */ - 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) const = 0; - + const resources::TextureInfo& get_info() const; protected: - /** - * Atlas texture positions. - */ - std::vector subtextures; - - /** - * Width and height of this texture. - */ - size_t w, h; + resources::TextureInfo info; }; }} diff --git a/libopenage/renderer/window.h b/libopenage/renderer/window.h index 218ac884c9..11a36aacbf 100644 --- a/libopenage/renderer/window.h +++ b/libopenage/renderer/window.h @@ -9,7 +9,6 @@ #include -#include "opengl/context_provider.h" #include "opengl/context.h" #include "../coord/window.h" diff --git a/libopenage/screenshot.cpp b/libopenage/screenshot.cpp index 4a78026fcd..f71b59f0b8 100644 --- a/libopenage/screenshot.cpp +++ b/libopenage/screenshot.cpp @@ -1,33 +1,36 @@ // Copyright 2014-2017 the openage authors. See copying.md for legal info. +#include "screenshot.h" + #include #include #include +#include +#include #include +#include +#include #include "coord/window.h" #include "job/job_manager.h" #include "log/log.h" - -#include -#include "screenshot.h" #include "util/strings.h" -#include "renderer/renderer.h" namespace openage { -ScreenshotManager::ScreenshotManager(job::JobManager *job_mgr, renderer::Renderer *renderer) +ScreenshotManager::ScreenshotManager(job::JobManager *job_mgr) : count{0}, - job_manager{job_mgr}, - renderer{renderer} {} + job_manager{job_mgr} { +} ScreenshotManager::~ScreenshotManager() {} std::string ScreenshotManager::gen_next_filename() { + std::time_t t = std::time(NULL); if (t == this->last_time) { @@ -37,22 +40,93 @@ std::string ScreenshotManager::gen_next_filename() { this->last_time = 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)); + // 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)); return util::sformat("/tmp/openage_%s_%02d.png", timestamp, this->count); } -void ScreenshotManager::save_screenshot() { - this->job_manager->enqueue([this] () { - std::string filename = this->gen_next_filename(); -// this->renderer->screenshot(filename); - return true; - }); +void ScreenshotManager::save_screenshot(coord::window size) { + coord::pixel_t width = size.x, + height = size.y; + + std::shared_ptr pxdata(new uint8_t[4*width*height], std::default_delete()); + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pxdata.get()); + + auto encode_function = [this, pxdata, size] () { + return this->encode_png(pxdata, size); + }; + this->job_manager->enqueue(encode_function); +} + + +bool ScreenshotManager::encode_png(std::shared_ptr pxdata, + coord::window size) { + std::FILE *fout = NULL; + coord::pixel_t width = size.x, + height = size.y; + auto warn_fn = [] (png_structp /*png_ptr*/, png_const_charp message) { + log::log(MSG(err) << "Creating screenshot failed: libpng error: " << message); + }; + auto err_fn = [] (png_structp png_ptr, png_const_charp message) { + log::log(MSG(err) << "Creating screenshot failed: libpng error: " << message); + longjmp(png_jmpbuf(png_ptr), 1); + }; + + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + (png_voidp) NULL, + err_fn, warn_fn); + if (!png_ptr) + return false; + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_write_struct(&png_ptr, (png_infopp) NULL); + return false; + } + + if (setjmp(png_jmpbuf(png_ptr))) { + std::fclose(fout); + png_destroy_write_struct(&png_ptr, &info_ptr); + return false; + } + + std::string filename = this->gen_next_filename(); + fout = std::fopen(filename.c_str(), "wb"); + if (fout == NULL) { + png_destroy_write_struct(&png_ptr, &info_ptr); + log::log(MSG(err) << "Could not open '"<< filename << "': " + << std::string(strerror(errno))); + return false; + } + + png_init_io(png_ptr, fout); + + png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + // Put image row pointer into info_ptr so that we can use the high-level + // write interface. + std::vector row_ptrs; + + // Invert rows. + row_ptrs.reserve(height); + for (int i = 1; i <= height; i++) { + row_ptrs.push_back(pxdata.get() + (height - i) * 4 * width); + } + png_set_rows(png_ptr, info_ptr, &row_ptrs[0]); + + //TODO: print ingame message. + log::log(MSG(info) << "Saving screenshot to '" << filename << "'."); + + png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_STRIP_FILLER_AFTER, NULL); + + std::fclose(fout); + png_destroy_write_struct(&png_ptr, &info_ptr); + return true; } } // openage diff --git a/libopenage/screenshot.h b/libopenage/screenshot.h index 9629681f26..26a9170737 100644 --- a/libopenage/screenshot.h +++ b/libopenage/screenshot.h @@ -9,9 +9,6 @@ #include "coord/window.h" namespace openage { -namespace renderer { -class Renderer; -} namespace job { class JobManager; @@ -22,38 +19,32 @@ class ScreenshotManager { /** * Initializes the screenshot manager with the given job manager. */ - ScreenshotManager(job::JobManager* job_mgr, renderer::Renderer *renderer); + ScreenshotManager(job::JobManager* job_mgr); ~ScreenshotManager(); - /** - * To be called to save a screenshot. - */ - void save_screenshot(); + /** To be called to save a screenshot. */ + void save_screenshot(coord::window size); + + /** To be called by the job manager. Returns true on success, false otherwise. */ + bool encode_png(std::shared_ptr pxdata, + coord::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 job manager this screenshot manager uses */ job::JobManager *job_manager; - /** - * The renderer where to take screenshots from. - */ - class renderer::Renderer *renderer; }; } // openage From 05b39917814f0553a82b19c63cad7b0b8c07d1f1 Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Wed, 12 Apr 2017 20:45:47 +0200 Subject: [PATCH 29/52] renderer: documentation and progress --- doc/renderer/doc.md | 143 ++++++++++++++++++ libopenage/renderer/doc.md | 34 ----- libopenage/renderer/docs/classes.uxf | 76 ---------- libopenage/renderer/opengl/context.cpp | 64 +++----- libopenage/renderer/opengl/context.h | 10 +- libopenage/renderer/opengl/renderer.cpp | 2 +- libopenage/renderer/opengl/shader_program.cpp | 62 +++++--- libopenage/renderer/opengl/shader_program.h | 8 +- libopenage/renderer/opengl/texture.cpp | 10 +- libopenage/renderer/opengl/texture.h | 9 +- libopenage/renderer/renderer.h | 2 +- .../renderer/resources/shader_source.cpp | 27 ++-- libopenage/renderer/resources/shader_source.h | 9 +- .../renderer/resources/texture_data.cpp | 33 ++-- libopenage/renderer/resources/texture_data.h | 3 +- libopenage/renderer/tests.cpp | 4 +- 16 files changed, 262 insertions(+), 234 deletions(-) create mode 100644 doc/renderer/doc.md delete mode 100644 libopenage/renderer/doc.md delete mode 100644 libopenage/renderer/docs/classes.uxf diff --git a/doc/renderer/doc.md b/doc/renderer/doc.md new file mode 100644 index 0000000000..5caed73dbe --- /dev/null +++ b/doc/renderer/doc.md @@ -0,0 +1,143 @@ +# Openage graphics +The graphics subsystem is implemented in two levels. The first level is an abstraction over graphics APIs (OpenGL, Vulkan) and provides generic shader execution methods. The second level uses the first to draw openage-specific graphics, i.e. the actual world, units, etc. + +### Namespaces: +`openage::renderer` - the level 1 renderer +`openage::renderer::opengl` - the OpenGL implementation +`openage::renderer::vulkan` - the Vulkan implementation +`openage::renderer::resources` - management of graphics assets + +__TODO name__ +`openage::graphics` - the level 2 system + +Every namespace is an actual directory and all its classes are contained there. + +## Level 1: +### Overview +First things first, we might want to support multiple APIs. For now just OpenGL, but maybe Vulkan or some others. We want to abstract over these, but this can't unfortunately be done at the level of graphics primitives like textures, buffers, etc. Well, it can, but it introduces unnecessary complexity and possible overhead. That is because the next-gen (Vulkan, Metal, DX12) APIs are vastly different from the old ones - most importantly, they're bindless, so something like a Vulkan context (GL notion) doesn't even make sense. We therefore choose to abstract on the higher level of things-to-draw. + +It works similarly to the Unity engine. The user can submit resources to be uploaded to the GPU and receives a handle that identifies the uploaded resource. Resources can be added, updated and removed. Currently supported resource types: shader, texture. + +### Thread-safety +This level might or might not be threadsafe depending on the concrete implementation. The OpenGL version is, in typical GL fashion, so not-threadsafe it's almost anti-threadsafe. All code must be executed sequentially on a dedicated window thread, the same one on which the window and renderer were initially created. The plan for the Vulkan version is to make it at least independent of thread-local storage and hopefully completely threadsafe. + +### Shaders + + + +These are more involved than resources, since each shader also carries a set of uniforms. + +A shader program takes in uniform values and vertex inputs and produces a number of bitmaps written into its render targets. Every shader program is then a function of the form $S:P\times\prod\limits^{n_u} U_i\times\left(\prod\limits^{n_a}V_j\right)^n\rightarrow\prod\limits^{n_t}B_k$, where $P$ is the primitive type, $\{U_i\}$, $\{V_j\}$, $\{B_k\}$ are indexed families of uniform, vertex attribute and bitmap types, respectively and the shader takes in $n_u$ uniforms, $n$ vertices with $n_a$ attributes per vertex and outputs to $n_t$ render targets. + +All of these inputs are contained in the $Renderable$ type. There is a catch here, since shaders are stateful, arguments are preserved between calls. + +Keep in mind that the output bitmap is not necessarily what the final output in a render target will be, since multiple draw calls get combined into a single bitmap and the initial value of the target also has an effect. + +With this premise, we can create a renderer interface generic over low-level APIs and usage (although optimized for openage's purposes). The only renderables are sets of values to be passed to shader programs - they may or may not include geometry, textures, matrices, etc. + +Our $ShaderInput$ class conceptually has almost the same member types as the types in the domain of the corresponding shader program function, with some small differences. For example, OpenGL shaders have implicit vertex attributes like $gl\_VertexID$ which we do not valuate manually. + +For the most part, these types are not know statically, so $ShaderInput$ has to simulate them internally with dynamic dispatch while providing an external interface that acts as if its members are the same as the inputs to the shader. + +OTOH, when it is the case that they are know statically, we can generate the appropriate headers from the shader code for better performance (in the future). + +Figure out the interface for $ShaderInput$. + +Parametrize $ShaderInput$ over a $Shader$ type. Have one $DynamicShader$ for all shaders loaded at runtime and some $StaticShaderForSpecificThing$ classes for shaders known beforehand. Make $DynamicShader$ compile for all operations and $StaticShader$.. only have operations it supports implemented, making it check them at compile-time. + +```c++ +template +class ShaderInput { + template + void set_unif(T val) { + // always compiles for dynamic shader, + // only compiles for valid U and T for static shader + (instanceof S).set_unif(val); + } +}; + +template +void func() { + static_assert(N != N, "Invalid shader uniform name."); +} + +template<> +void func() { + cout << "val1" << endl; + return; +} + +template<> +void func() { + cout << "val2" << endl; + return; +} +``` + +How to deal with drawing the scene? +We want stateless drawing, but pure stateless incurs some overhead. We can probably do with some mix of sharing scene between renderer and user. Keep a Scene object containing renderables (just shader valuations) and pass it to the render method. +What shoud Scene contain? +```c++ +struct Scene { + std::vector; + OR + std::vector>; + SceneSettings... + alpha blending, ztest, all of that in Object + is there anything global? + From Unity3D: + far, near plane + default depth + projection type + occlusion culling method + HDR + AA styles + how to deal with unit picking? probably have to make it a separate render pass + that renders unit ids into a buffer. read it on CPU or GPU? GPU faster, but is it possible? + general render-pass related things; have objects for render passes? +}; +``` +alternatively call render_pass(..) for each pass rather than call render(OBJ) once where OBJ contains info about render passes. might be nicer since render pass info can be recovered. problem - have to sync CPU/GPU to wait for command buffer execution finish +QUESTION: + share ownership of scene data at all times or give views to the user that are only accessible when not rendering? + +### Usage +Sample usage: + +```c++ +Window window("title"); +auto renderer = opengl::GlRenderer(window.get_context()); + +resources::TextureData tex_data("/path.tex"); +std::unique_ptr tex = renderer->add_texture(tex_data); + +resources::ShaderSource vsrc = resources::ShaderSource::read_from_file("/path.vert", resources::shader_t::glsl_vertex); +resources::ShaderSource fsrc = resources::ShaderSource::read_from_file("/path.frag", resources::shader_t::glsl_fragment); + +std::unique_ptr prog = renderer->add_shader( { vsrc, fsrc } ); + +auto input = prog->new_uniform_input( + "color", { 0.0f, 1.0f, 0.0f }, + "time", 0.0f, + "num", 1337 +); + +RenderPass pass { + { { + input, + new Geometry(geometry_t::quad), + true, + true, + true, + true, + } }, // list of renderables + renderer->get_framebuffer_target(), + 1.0f, + 8, +}; + +renderer->render(pass); +``` + +## Level 2: +On top of that renderer, we build a level 2 graphics subsystem. It has an API that is actually specific to openage, and is threadsafe. The level-2 renderer calls the level 1 renderer and updates it to match the game state. diff --git a/libopenage/renderer/doc.md b/libopenage/renderer/doc.md deleted file mode 100644 index 01794de4fb..0000000000 --- a/libopenage/renderer/doc.md +++ /dev/null @@ -1,34 +0,0 @@ -The renderer implementation: - - - -First things first, we might want to support multiple APIs. For now just OpenGL, but maybe Vulkan or some others. -We want to abstract over these, but this can't unfortunately be done at the level of graphics primitives like -textures, buffers, etc. Well, it can, but it introduces unnecessary complexity and possible overhead. That is because -the next-gen (Vulkan, Metal, DX12) APIs are vastly different from the old ones - most importantly, they're bindless, -so something like a Vulkan context (GL notion) doesn't even make sense. We therefore choose to abstract on the higher -level of things-to-draw. - -This is the level-1 renderer. We want a separate rendering thread, so the api is thread-safe. It works similarly to -the Unity engine. The first part is resources. The user can submit resources to be uploaded to the gpu and receives -a numeric handle that identifies the uploaded resource. Resources can be added, updated and removed. Currently -supported resource types: mesh, texture. - -Then, we have to deal with shaders. They are more involved than resources, since each shader also carries a set of -uniforms. - -TODO describe MaterialValuations - -On top of that renderer, be build a level-2 renderer. This one has an api that is actually specific to openage, and isn't -threadsafe. That way it can share data with the simulation code and others. The level-2 renderer calls the level-1 renderer -and updates it to match the game state. - -Namespaces: - -openage::renderer - everything related to rendering -openage::renderer::opengl - rendering using OpenGL -openage::renderer::vulkan - rendering using Vulkan - -every successive namespace is an actual directory and all its classes are contained there - - diff --git a/libopenage/renderer/docs/classes.uxf b/libopenage/renderer/docs/classes.uxf deleted file mode 100644 index aee0e0c45f..0000000000 --- a/libopenage/renderer/docs/classes.uxf +++ /dev/null @@ -1,76 +0,0 @@ - - - 10 - - UMLClass - - 260 - 120 - 130 - 60 - - Window - - - - UMLClass - - 220 - 280 - 220 - 60 - - <<interface>> -/opengl::ContextProvider/ - - - - - Relation - - 320 - 170 - 30 - 130 - - lt=<<- - 10.0;110.0;10.0;10.0 - - - UMLClass - - 520 - 120 - 180 - 60 - - <<interface>> -/3DRenderer/ - - - - - UMLClass - - 520 - 280 - 250 - 60 - - opengl::Renderer --- -instantiate(ContextProvider) - - - - Relation - - 600 - 170 - 30 - 130 - - lt=<<- - 10.0;10.0;10.0;110.0 - - diff --git a/libopenage/renderer/opengl/context.cpp b/libopenage/renderer/opengl/context.cpp index 6d50fb8958..985c690900 100644 --- a/libopenage/renderer/opengl/context.cpp +++ b/libopenage/renderer/opengl/context.cpp @@ -62,9 +62,15 @@ static gl_context_capabilities find_capabilities() { gl_context_capabilities caps; - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &caps.max_texture_size); - glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &caps.max_texture_slots); - glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &caps.max_vertex_attributes); + GLint temp; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &temp); + caps.max_texture_size = temp; + // TOOD maybe GL_MAX_TEXTURE_IMAGE_UNITS or maybe GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS + // lol opengl + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &temp); + caps.max_texture_slots = temp; + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &temp); + caps.max_vertex_attributes = temp; glGetIntegerv(GL_MAJOR_VERSION, &caps.major_version); glGetIntegerv(GL_MINOR_VERSION, &caps.minor_version); @@ -199,52 +205,20 @@ void GlContext::check_error() { } } -/* -void Context::setup() { - auto &caps = this->capabilities; - - // vsync on - // TODO: maybe move somewhere else or to the window. - SDL_GL_SetSwapInterval(1); - - // TODO: move to somewhere else, not all contexts may want those: - - // enable alpha blending - this->set_feature(context_feature::blending, true); - - // order of drawing relevant for depth - // what gets drawn last is displayed on top. - this->set_feature(context_feature::depth_test, false); - - // TODO: generalize like set_feature. - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); -} - -void GlContext::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"); - } - +void GlContext::set_vsync(bool on) { if (on) { - glEnable(what); - } else { - glDisable(what); + // try to use swap control tearing (adaptive vsync) + if (SDL_GL_SetSwapInterval(-1) == -1) { + // otherwise fall back to standard vsync + SDL_GL_SetSwapInterval(1); + } + } + else { + SDL_GL_SetSwapInterval(0); } } - +/* void Context::screenshot(const std::string &filename) { log::log(MSG(info) << "Saving screenshot to " << filename); diff --git a/libopenage/renderer/opengl/context.h b/libopenage/renderer/opengl/context.h index edb7dbfd03..5e4cf71741 100644 --- a/libopenage/renderer/opengl/context.h +++ b/libopenage/renderer/opengl/context.h @@ -4,6 +4,8 @@ #include +#include "texture.h" + namespace openage { namespace renderer { @@ -11,9 +13,9 @@ namespace opengl { /// Stores information about context capabilities and limitations. struct gl_context_capabilities { - int max_vertex_attributes; - int max_texture_slots; - int max_texture_size; + size_t max_vertex_attributes; + size_t max_texture_slots; + size_t max_texture_size; int major_version; int minor_version; @@ -36,6 +38,8 @@ class GlContext { SDL_GLContext get_raw_context() const; gl_context_capabilities get_capabilities() const; + void set_vsync(bool on); + /// Checks whether the current GL context on this thread reported any errors /// and throws an exception if it did. static void check_error(); diff --git a/libopenage/renderer/opengl/renderer.cpp b/libopenage/renderer/opengl/renderer.cpp index 7507cd58d4..c3390425b6 100644 --- a/libopenage/renderer/opengl/renderer.cpp +++ b/libopenage/renderer/opengl/renderer.cpp @@ -23,7 +23,7 @@ std::unique_ptr GlRenderer::add_texture(const resources::TextureData& d } std::unique_ptr GlRenderer::add_shader(std::vector const& srcs) { - return std::make_unique(srcs); + return std::make_unique(srcs, this->gl_context->get_capabilities()); } std::unique_ptr GlRenderer::create_texture_target(Texture const* tex) { diff --git a/libopenage/renderer/opengl/shader_program.cpp b/libopenage/renderer/opengl/shader_program.cpp index c8c1c0932b..2fb5d62a37 100644 --- a/libopenage/renderer/opengl/shader_program.cpp +++ b/libopenage/renderer/opengl/shader_program.cpp @@ -14,6 +14,7 @@ #include "../../util/compiler.h" #include "../../util/file.h" #include "../../util/strings.h" +#include "texture.h" namespace openage { @@ -23,21 +24,21 @@ namespace opengl { size_t uniform_size(gl_uniform_t type) { switch (type) { case gl_uniform_t::I32: - return 4; + return sizeof(GLint); case gl_uniform_t::U32: - return 4; + return sizeof(GLuint); case gl_uniform_t::F32: - return 4; + return sizeof(float); case gl_uniform_t::F64: - return 8; + return sizeof(double); case gl_uniform_t::V2F32: - return 8; + return 2 * sizeof(float); case gl_uniform_t::V3F32: - return 12; + return 3 * sizeof(float); case gl_uniform_t::V4F32: - return 16; - case gl_uniform_t::TEX2D: - return 4; + return 4 * sizeof(float); + case gl_uniform_t::SAMPLER2D: + return sizeof(GLint); } } @@ -57,7 +58,7 @@ static gl_uniform_t glsl_str_to_type(std::experimental::string_view str) { else if (str == "vec4") return gl_uniform_t::V4F32; else if (str == "sampler2D") - return gl_uniform_t::TEX2D; + return gl_uniform_t::SAMPLER2D; else throw Error(MSG(err) << "Unsupported GLSL uniform type " << str); } @@ -114,14 +115,14 @@ static GLuint src_type_to_gl(resources::shader_source_t type) { static GLuint compile_shader(const resources::ShaderSource& src) { // allocate shader in opengl - GLuint id = glCreateShader(src_type_to_gl(src.type())); + GLuint id = glCreateShader(src_type_to_gl(src.get_type())); if (unlikely(id == 0)) { throw Error{MSG(err) << "Unable to create OpenGL shader. WTF?!", true}; } // load shader source - const char* data = src.source(); + const char* data = src.get_source(); glShaderSource(id, 1, &data, 0); // compile shader source @@ -169,7 +170,7 @@ static void check_program_status(GLuint program, GLenum what_to_check) { case GL_COMPILE_STATUS: return "compilation"; default: - return "[insert_task_here]"; + return "unknown shader creation task"; } }(); @@ -177,7 +178,7 @@ static void check_program_status(GLuint program, GLenum what_to_check) { } } -GlShaderProgram::GlShaderProgram(const std::vector &srcs) { +GlShaderProgram::GlShaderProgram(const std::vector &srcs, const gl_context_capabilities &caps) { this->id = glCreateProgram(); if (unlikely(this->id == 0)) { @@ -186,7 +187,7 @@ GlShaderProgram::GlShaderProgram(const std::vector &src std::vector shaders; for (auto src : srcs) { - parse_glsl(this->uniforms, src.source()); + parse_glsl(this->uniforms, src.get_source()); shaders.push_back(compile_shader(src)); } @@ -211,7 +212,19 @@ GlShaderProgram::GlShaderProgram(const std::vector &src GLint loc = glGetUniformLocation(this->id, pair.first.data()); if (unlikely(loc == -1)) { - throw Error(MSG(err) << "Could not determine location of OpenGL shader uniform that was found before. WTF?!"); + throw Error(MSG(err) + << "Could not determine the location of OpenGL shader uniform that was found before. WTF?!"); + } + + GLuint tex_unit = 0; + if (pair.second.type == gl_uniform_t::SAMPLER2D) { + if (tex_unit >= caps.max_texture_slots) { + throw Error(MSG(err) + << "Tried to create shader that uses more texture sampler uniforms" + << "than there are texture unit slots available."); + } + this->texunits_per_unifs.insert(std::make_pair(pair.first, tex_unit)); + tex_unit += 1; } pair.second.location = loc; @@ -255,10 +268,10 @@ void GlShaderProgram::execute_with(const GlUniformInput *unif_in, const Geometry auto loc = this->uniforms[pair.first].location; switch (this->uniforms[pair.first].type) { case gl_uniform_t::I32: - glUniform1i(loc, *(int32_t*)ptr); + glUniform1i(loc, *(GLint*)ptr); break; case gl_uniform_t::U32: - glUniform1ui(loc, *(uint32_t*)ptr); + glUniform1ui(loc, *(GLuint*)ptr); break; case gl_uniform_t::F32: glUniform1f(loc, *(float*)ptr); @@ -276,8 +289,12 @@ void GlShaderProgram::execute_with(const GlUniformInput *unif_in, const Geometry case gl_uniform_t::V4F32: glUniform4fv(loc, 1, (float*)ptr); break; - case gl_uniform_t::TEX2D: - glUniform1i(loc, *(int32_t*)ptr); + case gl_uniform_t::SAMPLER2D: + // We do nothing here and bind texture to their units in use() instead. + // That is because the above uniform values persist in the shader state, + // but the texture unit bindings are global to the context. Each time + // the shader switches, it is possible that some other shader overwrote + // these, and so we have to set them more often than just on use(). break; default: throw Error(MSG(err) << "Tried to upload unknown uniform type to GL shader."); @@ -348,8 +365,9 @@ void GlShaderProgram::set_v4f32(UniformInput *in, const char *unif, Eigen::Vecto } void GlShaderProgram::set_tex(UniformInput *in, const char *unif, Texture const* val) { - // TODO special handling needed here - throw "unimplemented"; + auto const& tex = *static_cast(val); + GLuint handle = tex.get_handle(); + this->set_unif(in, unif, &handle); } }}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/shader_program.h b/libopenage/renderer/opengl/shader_program.h index e8dd19f91d..75fe68f748 100644 --- a/libopenage/renderer/opengl/shader_program.h +++ b/libopenage/renderer/opengl/shader_program.h @@ -11,6 +11,7 @@ #include "../../util/vector.h" #include "../renderer.h" #include "uniform_input.h" +#include "context.h" namespace openage { @@ -26,7 +27,7 @@ enum class gl_uniform_t { V2F32, V3F32, V4F32, - TEX2D, + SAMPLER2D, }; /// @returns the size in bytes of a GLSL uniform type @@ -41,7 +42,7 @@ class GlShaderProgram : public ShaderProgram { public: /// Tries to create a shader program from the given sources. /// Throws an exception on compile/link errors. - explicit GlShaderProgram(const std::vector&); + explicit GlShaderProgram(const std::vector&, const gl_context_capabilities&); ~GlShaderProgram(); GlShaderProgram(const GlShaderProgram&) = delete; @@ -79,6 +80,9 @@ class GlShaderProgram : public ShaderProgram { /// The GL shader program ID GLuint id; + + std::unordered_map texunits_per_unifs; + std::unordered_map textures_per_texunits; }; }}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/texture.cpp b/libopenage/renderer/opengl/texture.cpp index ca2ae750ac..f8bec9f735 100644 --- a/libopenage/renderer/opengl/texture.cpp +++ b/libopenage/renderer/opengl/texture.cpp @@ -15,8 +15,8 @@ namespace opengl { GlTexture::GlTexture(const resources::TextureData& data) : Texture(data.get_info()) { // generate opengl texture handle - glGenTextures(1, &this->id); - glBindTexture(GL_TEXTURE_2D, id); + glGenTextures(1, &this->handle); + glBindTexture(GL_TEXTURE_2D, this->handle); GLint input_format; GLenum output_format; @@ -52,7 +52,11 @@ GlTexture::GlTexture(const resources::TextureData& data) } GlTexture::~GlTexture() { - glDeleteTextures(1, &this->id); + glDeleteTextures(1, &this->handle); +} + +GLuint GlTexture::get_handle() const { + return this->handle; } }}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/texture.h b/libopenage/renderer/opengl/texture.h index b1953e48be..a693e87b6a 100644 --- a/libopenage/renderer/opengl/texture.h +++ b/libopenage/renderer/opengl/texture.h @@ -24,11 +24,12 @@ class GlTexture : public Texture { GlTexture(const resources::TextureData&); ~GlTexture(); + /// Returns the OpenGL handle to this texture. + GLuint get_handle() const; + private: - /** - * OpenGL handle id. - */ - GLuint id; + /// The OpenGL handle to this texture. + GLuint handle; }; }}} // openage::renderer::opengl diff --git a/libopenage/renderer/renderer.h b/libopenage/renderer/renderer.h index 2d6b2123d0..46708cbd52 100644 --- a/libopenage/renderer/renderer.h +++ b/libopenage/renderer/renderer.h @@ -57,9 +57,9 @@ struct Renderable { // this time, inputs are shader invocations (with gl state vars such as blending for each invocation) struct RenderPass { std::vector renderables; + RenderTarget const *target; float default_depth; uint8_t msaa_level; - RenderTarget const *target; }; class Renderer { diff --git a/libopenage/renderer/resources/shader_source.cpp b/libopenage/renderer/resources/shader_source.cpp index d2e9c4a6ce..5ec95cb8d1 100644 --- a/libopenage/renderer/resources/shader_source.cpp +++ b/libopenage/renderer/resources/shader_source.cpp @@ -11,38 +11,37 @@ namespace resources { const char *shader_source_type_to_str(shader_source_t type) { switch (type) { case shader_source_t::glsl_vertex: - return "vertex shader"; + return "GLSL vertex shader"; case shader_source_t::glsl_geometry: - return "geometry shader"; + return "GLSL geometry shader"; case shader_source_t::glsl_tesselation_control: - return "tesselation control shader"; + return "GLSL tesselation control shader"; case shader_source_t::glsl_tesselation_evaluation: - return "tesselation evaluation shader"; + return "GLSL tesselation evaluation shader"; case shader_source_t::glsl_fragment: - return "fragment shader"; + return "GLSL fragment shader"; default: - return "unknown shader type"; + return "unknown GLSL shader type"; } } ShaderSource::ShaderSource(shader_source_t type, std::string &&code) - : _type(type) - , code(std::move(code)) {} + : type(type) + , code(code) {} -ShaderSource ShaderSource::from_file(shader_source_t type, const char *path) { - util::File file(path); - return ShaderSource(type, file.read()); +ShaderSource ShaderSource::from_file(shader_source_t type, const util::Path &path) { + return ShaderSource(type, path.open().read()); } ShaderSource ShaderSource::from_string(shader_source_t type, std::string &&code) { return ShaderSource(type, std::move(code)); } -shader_source_t ShaderSource::type() const { - return this->_type; +shader_source_t ShaderSource::get_type() const { + return this->type; } -const char *ShaderSource::source() const { +const char *ShaderSource::get_source() const { return this->code.data(); } diff --git a/libopenage/renderer/resources/shader_source.h b/libopenage/renderer/resources/shader_source.h index 99ffb7b41e..7947b108a9 100644 --- a/libopenage/renderer/resources/shader_source.h +++ b/libopenage/renderer/resources/shader_source.h @@ -4,6 +4,7 @@ #include +#include "../../util/path.h" namespace openage { namespace renderer { @@ -24,21 +25,21 @@ const char *shader_source_type_to_str(shader_source_t); class ShaderSource { public: /// Obtain shader source code from a file. - static ShaderSource from_file(shader_source_t, const char *path); + static ShaderSource from_file(shader_source_t, const util::Path &path); /// Obtain shader source code from a string. static ShaderSource from_string(shader_source_t, std::string &&code); /// @returns a view of the shader source code - const char *source() const; + const char *get_source() const; - shader_source_t type() const; + shader_source_t get_type() const; private: ShaderSource(shader_source_t type, std::string &&code); private: - shader_source_t _type; + shader_source_t type; /// The shader source code. std::string code; diff --git a/libopenage/renderer/resources/texture_data.cpp b/libopenage/renderer/resources/texture_data.cpp index f1f4436048..5574b5e2b1 100644 --- a/libopenage/renderer/resources/texture_data.cpp +++ b/libopenage/renderer/resources/texture_data.cpp @@ -50,27 +50,28 @@ std::pair TextureInfo::get_subtexture_size(size_t subid) const { return std::make_pair(subtex.w, subtex.h); } -TextureData::TextureData(const char *filename, bool use_metafile) { - SDL_Surface *surface = IMG_Load(filename); +TextureData::TextureData(const util::Path &path, bool use_metafile) { + std::string native_path = path.resolve_native_path(); + SDL_Surface *surface = IMG_Load(native_path.c_str()); if (!surface) { throw Error(MSG(err) << "Could not load texture from " << - filename << ": " << IMG_GetError()); + native_path << ": " << IMG_GetError()); } else { - log::log(MSG(dbg) << "Texture has been loaded from " << filename); + log::log(MSG(dbg) << "Texture has been loaded from " << native_path); } auto fmt = *surface->format; if (fmt.Rmask != 0xf000 || fmt.Gmask != 0x0f00 || fmt.Bmask != 0x00f0) { - throw Error(MSG(err) << "Texture " << filename << " is not in RGB or RGBA format."); + throw Error(MSG(err) << "Texture " << native_path << " is not in RGB or RGBA format."); } auto& info = this->info; if (fmt.Amask == 0) { if (fmt.BytesPerPixel != 3) { - throw Error(MSG(err) << "Texture " << filename << " is in an unsupported RGB format."); + throw Error(MSG(err) << "Texture " << native_path << " is in an unsupported RGB format."); } info.format = pixel_format::rgb8; @@ -90,26 +91,12 @@ TextureData::TextureData(const char *filename, bool use_metafile) { SDL_FreeSurface(surface); if (use_metafile) { - // change the suffix to .docx (lol) - std::string meta_filename(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 - ); - } - - meta_filename.replace(start_pos, 5, ".docx"); - meta_filename = meta_filename.substr(0, start_pos + 5); + util::Path meta = path.get_parent() / path.get_stem() / ".docx"; - log::log(MSG(info) << "Loading meta file: " << meta_filename); + log::log(MSG(info) << "Loading meta file: " << meta); // get subtexture information by meta file exported by script - // TODO wot - //util::File file(meta_filename); - //info.subtextures = util::read_csv_file(file); + info.subtextures = util::read_csv_file(meta); } else { // we don't have a texture description file. diff --git a/libopenage/renderer/resources/texture_data.h b/libopenage/renderer/resources/texture_data.h index fd5874ccb9..705ed13f43 100644 --- a/libopenage/renderer/resources/texture_data.h +++ b/libopenage/renderer/resources/texture_data.h @@ -7,6 +7,7 @@ #include #include "../../gamedata/texture.gen.h" +#include "../../util/path.h" namespace openage { @@ -87,7 +88,7 @@ class TextureData { * Uses SDL Image internally. For supported image file types, * see the SDL_Image initialization in the engine. */ - TextureData(const char *filename, bool use_metafile = false); + TextureData(const util::Path &path, bool use_metafile = false); const TextureInfo& get_info() const; diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index 651d10115b..5a97bd3a96 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -117,9 +117,9 @@ void renderer_demo_0() { RenderPass pass { { gaben }, + renderer->get_framebuffer_target(), 0.0f, 8, - renderer->get_framebuffer_target(), }; render_demo test0{ @@ -128,6 +128,8 @@ void renderer_demo_0() { glEnable(GL_BLEND); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); + // what is this + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); GLuint vpos_buf; glGenBuffers(1, &vpos_buf); From c2dc925fa4ecfbbb077adafc15c5c786009f4d01 Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Thu, 13 Apr 2017 15:22:41 +0200 Subject: [PATCH 30/52] renderer: screenshots --- libopenage/renderer/opengl/context.cpp | 48 -------- libopenage/renderer/opengl/render_target.h | 62 +++++++++++ libopenage/renderer/opengl/renderer.cpp | 29 ++++- libopenage/renderer/opengl/renderer.h | 5 +- libopenage/renderer/opengl/shader_program.cpp | 41 ++++--- libopenage/renderer/opengl/texture.cpp | 82 ++++++++++---- libopenage/renderer/opengl/texture.h | 8 +- libopenage/renderer/renderer.h | 4 +- libopenage/renderer/resources/CMakeLists.txt | 1 + .../renderer/resources/texture_data.cpp | 103 ++++++++++-------- libopenage/renderer/resources/texture_data.h | 70 +----------- .../renderer/resources/texture_info.cpp | 52 +++++++++ libopenage/renderer/resources/texture_info.h | 84 ++++++++++++++ libopenage/renderer/tests.cpp | 13 +-- libopenage/renderer/texture.h | 2 + 15 files changed, 396 insertions(+), 208 deletions(-) create mode 100644 libopenage/renderer/opengl/render_target.h create mode 100644 libopenage/renderer/resources/texture_info.cpp create mode 100644 libopenage/renderer/resources/texture_info.h diff --git a/libopenage/renderer/opengl/context.cpp b/libopenage/renderer/opengl/context.cpp index 985c690900..86f080d185 100644 --- a/libopenage/renderer/opengl/context.cpp +++ b/libopenage/renderer/opengl/context.cpp @@ -218,52 +218,4 @@ void GlContext::set_vsync(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); -} -*/ - }}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/render_target.h b/libopenage/renderer/opengl/render_target.h new file mode 100644 index 0000000000..97c89be34e --- /dev/null +++ b/libopenage/renderer/opengl/render_target.h @@ -0,0 +1,62 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include "../renderer.h" +#include "texture.h" +#include "../../error/error.h" + + +namespace openage { +namespace renderer { +namespace opengl { + +enum class gl_render_target_t { + window, + texture, + // TODO MRT +}; + +class GlRenderTarget : public RenderTarget { +public: + /// Construct a render target that renders into the framebuffer (the screen). + GlRenderTarget() + : type(gl_render_target_t::window) + , handle(0) {} + + // TODO the validity of this object is contingent + // on its texture existing. use shared_ptr? + GlRenderTarget(std::vector textures) { + glGenFramebuffers(1, &this->handle); + glBindFramebuffer(GL_FRAMEBUFFER, this->handle); + + for (size_t i = 0; i < textures.size(); i++) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, textures[i]->get_handle(), 0); + } + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + throw Error(MSG(err) << "Could not create OpenGL framebuffer."); + } + } + + ~GlRenderTarget() { + if (type == gl_render_target_t::texture) { + glDeleteFramebuffers(1, &this->handle); + } + } + + void bind_read() const { + glBindFramebuffer(GL_READ_FRAMEBUFFER, this->handle); + } + + void bind_write() const { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, this->handle); + } + +private: + gl_render_target_t type; + + GLuint handle; +}; + +}}} diff --git a/libopenage/renderer/opengl/renderer.cpp b/libopenage/renderer/opengl/renderer.cpp index c3390425b6..19a3fea994 100644 --- a/libopenage/renderer/opengl/renderer.cpp +++ b/libopenage/renderer/opengl/renderer.cpp @@ -14,6 +14,7 @@ namespace opengl { GlRenderer::GlRenderer(GlContext *ctx) : gl_context(ctx) + , framebuffer() { log::log(MSG(info) << "Created OpenGL renderer"); } @@ -26,17 +27,37 @@ std::unique_ptr GlRenderer::add_shader(std::vector(srcs, this->gl_context->get_capabilities()); } -std::unique_ptr GlRenderer::create_texture_target(Texture const* tex) { - return std::unique_ptr(); - //return std::make_unique(tex); +std::unique_ptr GlRenderer::create_texture_target(std::vector textures) { + std::vector gl_textures; + for (auto tex : textures) { + gl_textures.push_back(static_cast(tex)); + } + + return std::make_unique(gl_textures); } RenderTarget const* GlRenderer::get_framebuffer_target() { - return this->framebuffer.get(); + return &this->framebuffer; } void GlRenderer::render(RenderPass const& pass) { + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + auto gl_target = dynamic_cast(pass.target); + gl_target->bind_write(); + for (auto obj : pass.renderables) { + if (obj.alpha_blending) + glEnable(GL_BLEND); + else + glDisable(GL_BLEND); + + if (obj.depth_test) + glEnable(GL_DEPTH_TEST); + else + glDisable(GL_DEPTH_TEST); + auto in = dynamic_cast(obj.unif_in); in->program->execute_with(in, obj.geometry); } diff --git a/libopenage/renderer/opengl/renderer.h b/libopenage/renderer/opengl/renderer.h index c84cc53557..f595d881de 100644 --- a/libopenage/renderer/opengl/renderer.h +++ b/libopenage/renderer/opengl/renderer.h @@ -9,6 +9,7 @@ #include "context.h" #include "../renderer.h" #include "shader_program.h" +#include "render_target.h" namespace openage { @@ -23,7 +24,7 @@ class GlRenderer : public Renderer { std::unique_ptr add_shader(std::vector const&) override; - std::unique_ptr create_texture_target(Texture const*) override; + std::unique_ptr create_texture_target(std::vector) override; RenderTarget const* get_framebuffer_target() override; void render(RenderPass const&) override; @@ -34,7 +35,7 @@ class GlRenderer : public Renderer { std::vector shaders; - std::unique_ptr framebuffer; + GlRenderTarget framebuffer; }; }}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/shader_program.cpp b/libopenage/renderer/opengl/shader_program.cpp index 2fb5d62a37..1f6ed0b432 100644 --- a/libopenage/renderer/opengl/shader_program.cpp +++ b/libopenage/renderer/opengl/shader_program.cpp @@ -39,6 +39,8 @@ size_t uniform_size(gl_uniform_t type) { return 4 * sizeof(float); case gl_uniform_t::SAMPLER2D: return sizeof(GLint); + default: + throw Error(MSG(err) << "Tried to find size of unknown GL uniform type."); } } @@ -255,6 +257,16 @@ GlShaderProgram& GlShaderProgram::operator=(GlShaderProgram&& other) { void GlShaderProgram::use() const { glUseProgram(this->id); + + for (auto const &pair : this->textures_per_texunits) { + // We have to bind the texture to their texture units here because + // the texture unit bindings are global to the context. Each time + // the shader switches, it is possible that some other shader overwrote + // these, and since we want the uniform values to persist across execute_with + // calls, we have to set them more often than just on execute_with. + glActiveTexture(GL_TEXTURE0 + pair.first); + glBindTexture(GL_TEXTURE_2D, pair.second); + } } void GlShaderProgram::execute_with(const GlUniformInput *unif_in, const Geometry *geom) { @@ -263,39 +275,40 @@ void GlShaderProgram::execute_with(const GlUniformInput *unif_in, const Geometry this->use(); uint8_t const* data = unif_in->update_data.data(); - for (auto pair : unif_in->update_offs) { + for (auto const &pair : unif_in->update_offs) { uint8_t const* ptr = data + pair.second; auto loc = this->uniforms[pair.first].location; switch (this->uniforms[pair.first].type) { case gl_uniform_t::I32: - glUniform1i(loc, *(GLint*)ptr); + glUniform1i(loc, *reinterpret_cast(ptr)); break; case gl_uniform_t::U32: - glUniform1ui(loc, *(GLuint*)ptr); + glUniform1ui(loc, *reinterpret_cast(ptr)); break; case gl_uniform_t::F32: - glUniform1f(loc, *(float*)ptr); + glUniform1f(loc, *reinterpret_cast(ptr)); break; case gl_uniform_t::F64: // TODO requires an extension - glUniform1d(loc, *(double*)ptr); + glUniform1d(loc, *reinterpret_cast(ptr)); break; case gl_uniform_t::V2F32: - glUniform2fv(loc, 1, (float*)ptr); + glUniform2fv(loc, 1, reinterpret_cast(ptr)); break; case gl_uniform_t::V3F32: - glUniform3fv(loc, 1, (float*)ptr); + glUniform3fv(loc, 1, reinterpret_cast(ptr)); break; case gl_uniform_t::V4F32: - glUniform4fv(loc, 1, (float*)ptr); + glUniform4fv(loc, 1, reinterpret_cast(ptr)); break; - case gl_uniform_t::SAMPLER2D: - // We do nothing here and bind texture to their units in use() instead. - // That is because the above uniform values persist in the shader state, - // but the texture unit bindings are global to the context. Each time - // the shader switches, it is possible that some other shader overwrote - // these, and so we have to set them more often than just on use(). + case gl_uniform_t::SAMPLER2D: { + GLuint tex_unit = this->texunits_per_unifs[pair.first]; + GLuint tex = *reinterpret_cast(ptr); + glActiveTexture(GL_TEXTURE0 + tex_unit); + glBindTexture(GL_TEXTURE_2D, tex); + this->textures_per_texunits[tex_unit] = tex; break; + } default: throw Error(MSG(err) << "Tried to upload unknown uniform type to GL shader."); } diff --git a/libopenage/renderer/opengl/texture.cpp b/libopenage/renderer/opengl/texture.cpp index f8bec9f735..dde7a2bee5 100644 --- a/libopenage/renderer/opengl/texture.cpp +++ b/libopenage/renderer/opengl/texture.cpp @@ -6,49 +6,70 @@ #include "../../error/error.h" #include "../resources/texture_data.h" +#include "render_target.h" namespace openage { namespace renderer { namespace opengl { +/// Returns the input and output formats for GL. +inline static std::pair gl_format(resources::pixel_format fmt) { + switch (fmt) { + case resources::pixel_format::rgb8: + return std::make_pair(GL_RGB8, GL_RGB); + case resources::pixel_format::rgba8: + return std::make_pair(GL_RGBA8, GL_RGBA); + default: + throw Error(MSG(err) << "invalid texture format passed to OpenGL."); + } +} + GlTexture::GlTexture(const resources::TextureData& data) - : Texture(data.get_info()) { + : Texture(data.get_info()) +{ // generate opengl texture handle glGenTextures(1, &this->handle); glBindTexture(GL_TEXTURE_2D, this->handle); - GLint input_format; - GLenum output_format; - // select pixel format - switch (this->info.get_format()) { - case resources::pixel_format::rgb8: - input_format = GL_RGB8; - output_format = GL_RGB; - break; - case resources::pixel_format::rgba8: - input_format = GL_RGBA8; - output_format = GL_RGBA; - break; - default: - throw Error{MSG(err) << "invalid texture format passed to OpenGL."}; - } + auto fmt_in_out = gl_format(this->info.get_format()); // store raw pixels to gpu auto size = this->info.get_size(); glTexImage2D( GL_TEXTURE_2D, 0, - input_format, size.first, size.second, 0, - output_format, GL_UNSIGNED_BYTE, data.get_data() + fmt_in_out.first, size.first, size.second, 0, + fmt_in_out.second, GL_UNSIGNED_BYTE, data.get_data() ); // drawing settings // TODO these are outdated, use sampler 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); +GlTexture::GlTexture(size_t width, size_t height, resources::pixel_format fmt) + : Texture(resources::TextureInfo(width, + height, + fmt, + std::vector())) +{ + // generate opengl texture handle + glGenTextures(1, &this->handle); + glBindTexture(GL_TEXTURE_2D, this->handle); + + auto fmt_in_out = gl_format(fmt); + + glTexImage2D( + GL_TEXTURE_2D, 0, + fmt_in_out.first, width, height, 0, + fmt_in_out.second, GL_UNSIGNED_BYTE, nullptr + ); + + // TODO these are outdated, use sampler settings + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } GlTexture::~GlTexture() { @@ -59,4 +80,27 @@ GLuint GlTexture::get_handle() const { return this->handle; } +inline static size_t pixel_size(resources::pixel_format fmt) { + switch (fmt) { + case resources::pixel_format::rgb8: + return 3; + case resources::pixel_format::rgba8: + return 4; + default: + throw Error(MSG(err) << "Tried to find size of unknown pixel format."); + } +} + +resources::TextureData GlTexture::into_data() const { + auto size = this->info.get_size(); + auto fmt_in_out = gl_format(this->info.get_format()); + std::vector data(size.first * size.second * pixel_size(this->info.get_format())); + + GlRenderTarget buf( { this } ); + buf.bind_read(); + glReadPixels(0, 0, size.first, size.second, fmt_in_out.second, GL_UNSIGNED_BYTE, data.data()); + + return resources::TextureData(resources::TextureInfo(this->info), std::move(data)); +} + }}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/texture.h b/libopenage/renderer/opengl/texture.h index a693e87b6a..bfa3d15c3c 100644 --- a/libopenage/renderer/opengl/texture.h +++ b/libopenage/renderer/opengl/texture.h @@ -21,12 +21,18 @@ namespace opengl { */ class GlTexture : public Texture { public: - GlTexture(const resources::TextureData&); + /// Constructs a texture and fills it with the given data. + explicit GlTexture(const resources::TextureData&); + + /// Constructs an empty texture with the given parameters. + GlTexture(size_t width, size_t height, resources::pixel_format); ~GlTexture(); /// Returns the OpenGL handle to this texture. GLuint get_handle() const; + resources::TextureData into_data() const override; + private: /// The OpenGL handle to this texture. GLuint handle; diff --git a/libopenage/renderer/renderer.h b/libopenage/renderer/renderer.h index 46708cbd52..629b28fa0f 100644 --- a/libopenage/renderer/renderer.h +++ b/libopenage/renderer/renderer.h @@ -58,8 +58,6 @@ struct Renderable { struct RenderPass { std::vector renderables; RenderTarget const *target; - float default_depth; - uint8_t msaa_level; }; class Renderer { @@ -73,7 +71,7 @@ class Renderer { virtual std::unique_ptr add_shader(std::vector const&) = 0; - virtual std::unique_ptr create_texture_target(Texture const*) = 0; + virtual std::unique_ptr create_texture_target(std::vector) = 0; virtual RenderTarget const* get_framebuffer_target() = 0; virtual void render(RenderPass const&) = 0; diff --git a/libopenage/renderer/resources/CMakeLists.txt b/libopenage/renderer/resources/CMakeLists.txt index 0156ef1eb9..3546f5dbab 100644 --- a/libopenage/renderer/resources/CMakeLists.txt +++ b/libopenage/renderer/resources/CMakeLists.txt @@ -1,4 +1,5 @@ add_sources(libopenage shader_source.cpp texture_data.cpp + texture_info.cpp ) diff --git a/libopenage/renderer/resources/texture_data.cpp b/libopenage/renderer/resources/texture_data.cpp index 5574b5e2b1..4b7e096822 100644 --- a/libopenage/renderer/resources/texture_data.cpp +++ b/libopenage/renderer/resources/texture_data.cpp @@ -14,41 +14,6 @@ namespace openage { namespace renderer { namespace resources { -pixel_format TextureInfo::get_format() const { - return this->format; -} - -std::pair TextureInfo::get_size() const { - return std::make_pair(this->w, this->h); -} - -size_t TextureInfo::get_subtexture_count() const { - return this->subtextures.size(); -} - -const gamedata::subtexture& TextureInfo::get_subtexture(size_t subid) const { - if (subid < this->subtextures.size()) { - return this->subtextures[subid]; - } - else { - throw Error(MSG(err) << "Unknown subtexture requested: " << subid); - } -} - -std::tuple TextureInfo::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 - ); -} - -std::pair TextureInfo::get_subtexture_size(size_t subid) const { - auto subtex = this->get_subtexture(subid); - return std::make_pair(subtex.w, subtex.h); -} TextureData::TextureData(const util::Path &path, bool use_metafile) { std::string native_path = path.resolve_native_path(); @@ -68,20 +33,20 @@ TextureData::TextureData(const util::Path &path, bool use_metafile) { throw Error(MSG(err) << "Texture " << native_path << " is not in RGB or RGBA format."); } - auto& info = this->info; + pixel_format format; if (fmt.Amask == 0) { if (fmt.BytesPerPixel != 3) { throw Error(MSG(err) << "Texture " << native_path << " is in an unsupported RGB format."); } - info.format = pixel_format::rgb8; + format = pixel_format::rgb8; } else { - info.format = pixel_format::rgba8; + format = pixel_format::rgba8; } - info.w = surface->w; - info.h = surface->h; + auto w = surface->w; + auto h = surface->h; size_t data_size = surface->format->BytesPerPixel * surface->w * surface->h; @@ -90,29 +55,81 @@ TextureData::TextureData(const util::Path &path, bool use_metafile) { memcpy(this->data.data(), surface->pixels, data_size); SDL_FreeSurface(surface); + std::vector subtextures; if (use_metafile) { util::Path meta = path.get_parent() / path.get_stem() / ".docx"; log::log(MSG(info) << "Loading meta file: " << meta); // get subtexture information by meta file exported by script - info.subtextures = util::read_csv_file(meta); + subtextures = util::read_csv_file(meta); } else { // we don't have a texture description file. // use the whole image as one texture then. - gamedata::subtexture s{0, 0, info.w, info.h, info.w/2, info.h/2}; + gamedata::subtexture s{0, 0, w, h, w/2, h/2}; - info.subtextures.push_back(s); + subtextures.push_back(s); } + + this->info = TextureInfo(w, h, format, std::move(subtextures)); } +TextureData::TextureData(TextureInfo &&info, std::vector &&data) + : info(std::move(info)) + , data(std::move(data)) {} + const TextureInfo& TextureData::get_info() const { return this->info; } const uint8_t *TextureData::get_data() const { return this->data.data(); +}; + +void TextureData::store(const util::Path& file) const { + log::log(MSG(info) << "Saving texture data to " << file); + + if (this->info.get_format() != pixel_format::rgba8) { + throw "unimplemented"; + } + + // color masks. + int32_t rmask, gmask, bmask, amask; + rmask = 0x000000FF; + gmask = 0x0000FF00; + bmask = 0x00FF0000; + amask = 0xFF000000; + + auto size = this->info.get_size(); + + // create output surface which will be stored later. + SDL_Surface *screen = SDL_CreateRGBSurface( + SDL_SWSURFACE, + size.first, size.second, + 32, rmask, gmask, bmask, amask + ); + + uint32_t *surf_data = reinterpret_cast(screen->pixels); + const uint32_t *data = reinterpret_cast(this->data.data()); + + // 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 pixel = data[irow * screen->w + col]; + + // TODO: store the alpha channels in the file, + // is buggy at the moment.. + surf_data[row * screen->w + col] = pixel | 0xFF000000; + } + } + + // call sdl_image for saving the screenshot to png + std::string path = file.resolve_native_path(); + IMG_SavePNG(screen, path.c_str()); + SDL_FreeSurface(screen); } }}} diff --git a/libopenage/renderer/resources/texture_data.h b/libopenage/renderer/resources/texture_data.h index 705ed13f43..0d5e0cd1e6 100644 --- a/libopenage/renderer/resources/texture_data.h +++ b/libopenage/renderer/resources/texture_data.h @@ -6,7 +6,7 @@ #include #include -#include "../../gamedata/texture.gen.h" +#include "texture_info.h" #include "../../util/path.h" @@ -14,70 +14,6 @@ namespace openage { namespace renderer { namespace resources { -/// How the pixels are represented in a texture. -enum class pixel_format { - /// 24 bits per pixel, no alpha channel - rgb8, - /// 32 bits per pixel - rgba8, -}; - -/** - * Information for texture processing. - * 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 TextureInfo { - friend class TextureData; -public: - pixel_format get_format() const; - - /** - * Return the dimensions of the whole texture bitmap - * @returns tuple(width, height) - */ - std::pair get_size() const; - - /** - * @return the number of available subtextures - */ - size_t get_subtexture_count() const; - - /** - * Get the subtexture coordinates by its idea. - */ - const gamedata::subtexture& get_subtexture(size_t subid) const; - - /** - * Fetch the size of the given subtexture. - * @param subid: index of the requested subtexture - */ - std::pair 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 surface size. - */ - std::tuple get_subtexture_coordinates(size_t subid) const; - -protected: - /// The pixel format of this texture. - pixel_format format; - - /// Width and height of this texture. - int32_t w, h; - - /** - * Some textures are merged together into texture atlases, large images which contain - * more than one individual texture. These are their positions in the atlas. - */ - std::vector subtextures; -}; - class TextureData { public: /** @@ -90,10 +26,14 @@ class TextureData { */ TextureData(const util::Path &path, bool use_metafile = false); + TextureData(TextureInfo &&info, std::vector &&data); + const TextureInfo& get_info() const; const uint8_t *get_data() const; + void store(const util::Path& file) const; + private: TextureInfo info; std::vector data; diff --git a/libopenage/renderer/resources/texture_info.cpp b/libopenage/renderer/resources/texture_info.cpp new file mode 100644 index 0000000000..db993d754f --- /dev/null +++ b/libopenage/renderer/resources/texture_info.cpp @@ -0,0 +1,52 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#include "texture_info.h" + + +namespace openage { +namespace renderer { +namespace resources { + +TextureInfo::TextureInfo(size_t width, size_t height, pixel_format fmt, std::vector &&subs) + : w(width) + , h(height) + , format(fmt) + , subtextures(std::move(subs)) {} + +pixel_format TextureInfo::get_format() const { + return this->format; +} + +std::pair TextureInfo::get_size() const { + return std::make_pair(this->w, this->h); +} + +size_t TextureInfo::get_subtexture_count() const { + return this->subtextures.size(); +} + +const gamedata::subtexture& TextureInfo::get_subtexture(size_t subid) const { + if (subid < this->subtextures.size()) { + return this->subtextures[subid]; + } + else { + throw Error(MSG(err) << "Unknown subtexture requested: " << subid); + } +} + +std::tuple TextureInfo::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 + ); +} + +std::pair TextureInfo::get_subtexture_size(size_t subid) const { + auto subtex = this->get_subtexture(subid); + return std::make_pair(subtex.w, subtex.h); +} + +}}} diff --git a/libopenage/renderer/resources/texture_info.h b/libopenage/renderer/resources/texture_info.h new file mode 100644 index 0000000000..03ed5d23fa --- /dev/null +++ b/libopenage/renderer/resources/texture_info.h @@ -0,0 +1,84 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include +#include + +#include "../../gamedata/texture.gen.h" + + +namespace openage { +namespace renderer { +namespace resources { + +/// How the pixels are represented in a texture. +enum class pixel_format { + /// 24 bits per pixel, no alpha channel + rgb8, + /// 32 bits per pixel + rgba8, +}; + +/** + * Information for texture processing. + * 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 TextureInfo { +public: + TextureInfo(size_t width, size_t height, pixel_format, std::vector&&); + + TextureInfo() = default; + ~TextureInfo() = default; + + /// @returns the format of pixels in this texture + pixel_format get_format() const; + + /** + * Return the dimensions of the whole texture bitmap + * @returns tuple(width, height) + */ + std::pair get_size() const; + + /** + * @return the number of available subtextures + */ + size_t get_subtexture_count() const; + + /** + * Get the subtexture coordinates by its idea. + */ + const gamedata::subtexture& get_subtexture(size_t subid) const; + + /** + * Fetch the size of the given subtexture. + * @param subid: index of the requested subtexture + */ + std::pair 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 surface size. + */ + std::tuple get_subtexture_coordinates(size_t subid) const; + +protected: + /// Width and height of this texture. + int32_t w, h; + + /// The pixel format of this texture. + pixel_format format; + + /** + * Some textures are merged together into texture atlases, large images which contain + * more than one individual texture. These are their positions in the atlas. + */ + std::vector subtextures; +}; + +}}} diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index 5a97bd3a96..45337530e1 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -118,22 +118,19 @@ void renderer_demo_0() { RenderPass pass { { gaben }, renderer->get_framebuffer_target(), - 0.0f, - 8, }; render_demo test0{ // init [&](Window */*window*/) { - glEnable(GL_BLEND); - glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); + glDepthRange(0.0, 1.0); // what is this glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - GLuint vpos_buf; - glGenBuffers(1, &vpos_buf); - glBindBuffer(GL_ARRAY_BUFFER, vpos_buf); + //GLuint vpos_buf; + //glGenBuffers(1, &vpos_buf); + //glBindBuffer(GL_ARRAY_BUFFER, vpos_buf); GLuint vao; glGenVertexArrays(1, &vao); @@ -141,8 +138,6 @@ void renderer_demo_0() { }, // frame [&]() { - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); renderer->render(pass); window.swap(); window.get_context()->check_error(); diff --git a/libopenage/renderer/texture.h b/libopenage/renderer/texture.h index 3ebd18d9e0..3ae19217d3 100644 --- a/libopenage/renderer/texture.h +++ b/libopenage/renderer/texture.h @@ -28,6 +28,8 @@ class Texture { const resources::TextureInfo& get_info() const; + virtual resources::TextureData into_data() const = 0; + protected: resources::TextureInfo info; }; From e352ab5c99d233d5150f88a4d91d729060da4c98 Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Fri, 14 Apr 2017 15:52:50 +0200 Subject: [PATCH 31/52] renderer: fix glsl parsing --- libopenage/.clang_complete | 2 - libopenage/.dir-locals.el | 2 - libopenage/renderer/opengl/shader_program.cpp | 47 +++++----- libopenage/renderer/tests.cpp | 85 ++++++++++++++----- 4 files changed, 89 insertions(+), 47 deletions(-) delete mode 100644 libopenage/.clang_complete delete mode 100644 libopenage/.dir-locals.el diff --git a/libopenage/.clang_complete b/libopenage/.clang_complete deleted file mode 100644 index 9b5869f672..0000000000 --- a/libopenage/.clang_complete +++ /dev/null @@ -1,2 +0,0 @@ --std=c++14 --Wno-pragma-once-outside-header \ No newline at end of file diff --git a/libopenage/.dir-locals.el b/libopenage/.dir-locals.el deleted file mode 100644 index 06b67b65fc..0000000000 --- a/libopenage/.dir-locals.el +++ /dev/null @@ -1,2 +0,0 @@ -((c++-mode . ((indent-tabs-mode . t) - (c-default-style . "k&r")))) diff --git a/libopenage/renderer/opengl/shader_program.cpp b/libopenage/renderer/opengl/shader_program.cpp index 1f6ed0b432..ca9bc76fb4 100644 --- a/libopenage/renderer/opengl/shader_program.cpp +++ b/libopenage/renderer/opengl/shader_program.cpp @@ -67,32 +67,39 @@ static gl_uniform_t glsl_str_to_type(std::experimental::string_view str) { void parse_glsl(std::map &uniforms, const char *code) { // this will match all uniform declarations - std::regex unif_r("uniform\\s+\\w+\\s+\\w+(?=\\s*;)"); + std::regex const unif_r("uniform\\s+\\w+\\s+\\w+(?=\\s*;)"); std::regex const word_r("\\w+"); - std::cmatch results; - if (regex_search(code, results, unif_r)) { - for (auto result : results) { - std::string sresult(result); + const char *end = code; + while (*end != '\0') { + end += 1; + } - // remove "uniform" - sresult = sresult.substr(7); + auto unif_iter = std::cregex_iterator(code, end, unif_r); + auto unif_iter_end = std::cregex_iterator(); + for (; unif_iter != unif_iter_end; unif_iter++) { + std::string unif = (*unif_iter).str(); - std::smatch words; - regex_search(sresult, words, word_r); + // remove "uniform" + unif = unif.substr(7); - // first word is the type - gl_uniform_t type = glsl_str_to_type(words.str(0)); + auto word_iter = std::sregex_iterator(unif.begin(), unif.end(), word_r); + + // first word is the type + gl_uniform_t type = glsl_str_to_type((*word_iter).str()); + + // second word is the uniform name + word_iter++; + + // second word is the uniform name + uniforms.insert(std::pair( + (*word_iter).str(), + GlUniform { + type, + 0, + } + )); - // second word is the uniform name - uniforms.insert(std::pair( - words.str(1), - GlUniform { - type, - 0, - } - )); - } } } diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index 45337530e1..3c6179f91c 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -80,34 +80,73 @@ void renderer_demo_0() { auto vshader_src = resources::ShaderSource::from_string( resources::shader_source_t::glsl_vertex, - "#version 330\n" - "smooth out vec2 pos;" - "void main() {" - " gl_Position.x = 2.0 * float(gl_VertexID & 1) - 1.0;" - " gl_Position.y = 2.0 * float((gl_VertexID & 2) >> 1) - 1.0;" - " gl_Position.z = 1.0;" - " gl_Position.w = 1.0;" - " gl_Position.xy *= 0.8;" - " pos = (vec2(gl_Position) + 1.0) / 2.0;" - "}" - ); + R"s( +#version 330 +uniform vec4 bounds; + +void main() { + gl_Position.x = 2.0 * float(gl_VertexID & 1) - 1.0; + gl_Position.y = 2.0 * float((gl_VertexID & 2) >> 1) - 1.0; + gl_Position.z = 1.0; + gl_Position.w = 1.0; + + gl_Position.x *= bounds[2] - bounds[0]; + gl_Position.y *= bounds[3] - bounds[1]; + + gl_Position.x += (bounds[0] + bounds[2]) / 2.0; + gl_Position.y += (bounds[1] + bounds[3]) / 2.0; +} +)s"); auto fshader_src = resources::ShaderSource::from_string( resources::shader_source_t::glsl_fragment, - "#version 330\n" - "smooth in vec2 pos;" - "out vec4 color;" - "void main() {" - " color = vec4(1.0f, pos.y, pos.x, 1.0f);" - "}" - ); + R"s( +#version 330 +uniform vec4 color; +out vec4 col; - auto shader = renderer->add_shader( { vshader_src, fshader_src } ); +void main() { + col = color; +} +)s"); + + std::vector srcs = {vshader_src, fshader_src}; + auto shader = renderer->add_shader(srcs); + + auto unif_in1 = shader->new_uniform_input( + "bounds", Eigen::Vector4f(-0.6f, -0.6f, -0.4f, -0.4f), + "color", Eigen::Vector4f(1.0f, 0.0f, 0.0f, 1.0f) + ); + auto unif_in2 = shader->new_uniform_input( + "bounds", Eigen::Vector4f(0.0f, 0.3f, 0.3f, 0.5f), + "color", Eigen::Vector4f(0.0f, 1.0f, 0.0f, 1.0f) + ); + auto unif_in3 = shader->new_uniform_input( + "bounds", Eigen::Vector4f(0.5f, -0.7f, 0.6f, -0.3f), + "color", Eigen::Vector4f(0.0f, 0.0f, 1.0f, 1.0f) + ); - auto unif_in = shader->new_uniform_input(); Geometry quad; - Renderable gaben { - unif_in.get(), + Renderable obj1 { + unif_in1.get(), + &quad, + true, + true, + true, + true, + }; + + Renderable obj2{ + unif_in2.get(), + &quad, + true, + true, + true, + true, + }; + + Renderable obj3 { + unif_in3.get(), &quad, true, true, @@ -116,7 +155,7 @@ void renderer_demo_0() { }; RenderPass pass { - { gaben }, + { obj1, obj2, obj3 }, renderer->get_framebuffer_target(), }; From 182065ca0a8da3a9dd45e109348b6e7d48641e83 Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Fri, 14 Apr 2017 19:24:33 +0200 Subject: [PATCH 32/52] renderer: documentation and small interface changes --- libopenage/renderer/geometry.h | 7 +- libopenage/renderer/opengl/CMakeLists.txt | 1 + libopenage/renderer/opengl/context.h | 15 ++-- libopenage/renderer/opengl/render_target.cpp | 62 ++++++++++++++++ libopenage/renderer/opengl/render_target.h | 70 +++++++++---------- libopenage/renderer/opengl/renderer.cpp | 25 ++++--- libopenage/renderer/opengl/renderer.h | 4 +- libopenage/renderer/opengl/shader_program.cpp | 16 +++-- libopenage/renderer/opengl/shader_program.h | 17 +++-- libopenage/renderer/opengl/texture.cpp | 34 +++++++-- libopenage/renderer/opengl/texture.h | 11 ++- libopenage/renderer/opengl/uniform_input.h | 11 +-- libopenage/renderer/renderer.h | 58 +++++++++------ .../renderer/resources/shader_source.cpp | 10 +-- libopenage/renderer/resources/shader_source.h | 17 +++-- libopenage/renderer/resources/texture_data.h | 25 ++++--- libopenage/renderer/resources/texture_info.h | 50 +++++-------- libopenage/renderer/shader_program.h | 14 +++- libopenage/renderer/tests.cpp | 12 +--- libopenage/renderer/texture.h | 23 +++--- libopenage/renderer/window.cpp | 45 +++++++++--- libopenage/renderer/window.h | 53 ++++++-------- 22 files changed, 360 insertions(+), 220 deletions(-) create mode 100644 libopenage/renderer/opengl/render_target.cpp diff --git a/libopenage/renderer/geometry.h b/libopenage/renderer/geometry.h index 659ce632b8..0bc87dcb59 100644 --- a/libopenage/renderer/geometry.h +++ b/libopenage/renderer/geometry.h @@ -2,24 +2,27 @@ #pragma once -#include "vertex_buffer.h" -#include "vertex_state.h" namespace openage { namespace renderer { +/// The type of geometry. enum class geometry_t { quad, mesh, }; +/// A class representing geometry to be passed to a draw call. +// TODO this class class Geometry { public: /// The default constructor makes a quad. Geometry() : type(geometry_t::quad) {} + // TODO maybe use inheritance instead of enum? // Geometry(mesh..) + /// Returns the type of this geometry. geometry_t get_type() const { return this->type; } private: diff --git a/libopenage/renderer/opengl/CMakeLists.txt b/libopenage/renderer/opengl/CMakeLists.txt index 1da53ddc4a..37e2d64ddf 100644 --- a/libopenage/renderer/opengl/CMakeLists.txt +++ b/libopenage/renderer/opengl/CMakeLists.txt @@ -1,5 +1,6 @@ add_sources(libopenage context.cpp + render_target.cpp renderer.cpp shader_program.cpp texture.cpp diff --git a/libopenage/renderer/opengl/context.h b/libopenage/renderer/opengl/context.h index 5e4cf71741..d03e65198c 100644 --- a/libopenage/renderer/opengl/context.h +++ b/libopenage/renderer/opengl/context.h @@ -4,8 +4,6 @@ #include -#include "texture.h" - namespace openage { namespace renderer { @@ -13,14 +11,18 @@ namespace opengl { /// Stores information about context capabilities and limitations. struct gl_context_capabilities { + /// The maximum number of vertex attributes in a shader. size_t max_vertex_attributes; + /// The amount of texture units (GL_TEXTUREi) available. size_t max_texture_slots; + /// The maximum size of a single dimension of a texture. size_t max_texture_size; int major_version; int minor_version; }; +/// Manages an OpenGL context. class GlContext { public: /// Create a GL context in the given SDL window. @@ -31,23 +33,28 @@ class GlContext { GlContext(const GlContext&) = delete; GlContext& operator=(const GlContext&) = delete; - /// We have to support moving to avoid a clusterfuck somewhere else. + /// We have to support moving to avoid a mess somewhere else. GlContext(GlContext&&); GlContext& operator=(GlContext&&); + /// Returns the underlying SDL context pointer. SDL_GLContext get_raw_context() const; + + /// Returns the capabilities of this context. gl_context_capabilities get_capabilities() const; + /// Turns VSYNC on or off for this context. void set_vsync(bool on); /// Checks whether the current GL context on this thread reported any errors - /// and throws an exception if it did. + /// and throws an exception if it did. Note that it's static. static void check_error(); private: /// Pointer to SDL struct representing the GL context. SDL_GLContext gl_context; + /// Context capabilities. gl_context_capabilities capabilities; }; diff --git a/libopenage/renderer/opengl/render_target.cpp b/libopenage/renderer/opengl/render_target.cpp new file mode 100644 index 0000000000..b3ce942cca --- /dev/null +++ b/libopenage/renderer/opengl/render_target.cpp @@ -0,0 +1,62 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#include "render_target.h" + + +namespace openage { +namespace renderer { +namespace opengl { + +GlRenderTarget::GlRenderTarget() + : type(gl_render_target_t::display) + , handle(0) {} + +// TODO the validity of this object is contingent +// on its texture existing. use shared_ptr? +GlRenderTarget::GlRenderTarget(std::vector textures) { + glGenFramebuffers(1, &*this->handle); + glBindFramebuffer(GL_FRAMEBUFFER, *this->handle); + + for (size_t i = 0; i < textures.size(); i++) { + // TODO figure out attachment points from pixel formats + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, textures[i]->get_handle(), 0); + } + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + throw Error(MSG(err) << "Could not create OpenGL framebuffer."); + } +} + +GlRenderTarget::GlRenderTarget(GlRenderTarget &&other) + : type(other.type) + , handle(other.handle) { + other.handle = std::experimental::optional(); +} + +GlRenderTarget &GlRenderTarget::operator =(GlRenderTarget&& other) { + if (this->type == gl_render_target_t::textures && this->handle) { + glDeleteFramebuffers(1, &*this->handle); + } + + this->type = other.type; + this->handle = other.handle; + other.handle = std::experimental::optional(); + + return *this; +} + +GlRenderTarget::~GlRenderTarget() { + if (this->type == gl_render_target_t::textures && this->handle) { + glDeleteFramebuffers(1, &*this->handle); + } +} + +void GlRenderTarget::bind_read() const { + glBindFramebuffer(GL_READ_FRAMEBUFFER, *this->handle); +} + +void GlRenderTarget::bind_write() const { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, *this->handle); +} + +}}} diff --git a/libopenage/renderer/opengl/render_target.h b/libopenage/renderer/opengl/render_target.h index 97c89be34e..5f216e870f 100644 --- a/libopenage/renderer/opengl/render_target.h +++ b/libopenage/renderer/opengl/render_target.h @@ -2,6 +2,8 @@ #pragma once +#include + #include "../renderer.h" #include "texture.h" #include "../../error/error.h" @@ -11,52 +13,46 @@ namespace openage { namespace renderer { namespace opengl { +/// The type of OpenGL render target enum class gl_render_target_t { - window, - texture, - // TODO MRT + /// The actual window. This is visible to the user after swapping front and back buffers + display, + /// A bunch of textures + textures, + // TODO renderbuffers mixed with textures }; class GlRenderTarget : public RenderTarget { public: - /// Construct a render target that renders into the framebuffer (the screen). - GlRenderTarget() - : type(gl_render_target_t::window) - , handle(0) {} - - // TODO the validity of this object is contingent - // on its texture existing. use shared_ptr? - GlRenderTarget(std::vector textures) { - glGenFramebuffers(1, &this->handle); - glBindFramebuffer(GL_FRAMEBUFFER, this->handle); - - for (size_t i = 0; i < textures.size(); i++) { - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, textures[i]->get_handle(), 0); - } - - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - throw Error(MSG(err) << "Could not create OpenGL framebuffer."); - } - } - - ~GlRenderTarget() { - if (type == gl_render_target_t::texture) { - glDeleteFramebuffers(1, &this->handle); - } - } - - void bind_read() const { - glBindFramebuffer(GL_READ_FRAMEBUFFER, this->handle); - } - - void bind_write() const { - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, this->handle); - } + /// Construct a render target pointed at the default framebuffer - the window. + GlRenderTarget(); + ~GlRenderTarget(); + + /// Construct a render target pointing at the given textures. + /// Texture are attached to points specific to their pixel format, + /// e.g. a depth texture will be set as the depth target. + GlRenderTarget(std::vector textures); + + /// Cannot copy a framebuffer. + GlRenderTarget(const GlRenderTarget&) = delete; + GlRenderTarget &operator =(const GlRenderTarget&) = delete; + + /// Can move the object. + GlRenderTarget(GlRenderTarget&&); + GlRenderTarget &operator =(GlRenderTarget&&); + + /// Bind this framebuffer to GL_READ_FRAMEBUFFER. + void bind_read() const; + + /// Bind this framebuffer to GL_DRAW_FRAMEBUFFER. + void bind_write() const; private: + /// The type. gl_render_target_t type; - GLuint handle; + /// The handle to the underlying OpenGL object. + std::experimental::optional handle; }; }}} diff --git a/libopenage/renderer/opengl/renderer.cpp b/libopenage/renderer/opengl/renderer.cpp index 19a3fea994..ed629556e3 100644 --- a/libopenage/renderer/opengl/renderer.cpp +++ b/libopenage/renderer/opengl/renderer.cpp @@ -2,6 +2,7 @@ #include "renderer.h" +#include "../../log/log.h" #include "../../error/error.h" #include "texture.h" #include "shader_program.h" @@ -14,7 +15,7 @@ namespace opengl { GlRenderer::GlRenderer(GlContext *ctx) : gl_context(ctx) - , framebuffer() + , display() { log::log(MSG(info) << "Created OpenGL renderer"); } @@ -36,8 +37,8 @@ std::unique_ptr GlRenderer::create_texture_target(std::vector(gl_textures); } -RenderTarget const* GlRenderer::get_framebuffer_target() { - return &this->framebuffer; +RenderTarget const* GlRenderer::get_display_target() { + return &this->display; } void GlRenderer::render(RenderPass const& pass) { @@ -48,15 +49,19 @@ void GlRenderer::render(RenderPass const& pass) { gl_target->bind_write(); for (auto obj : pass.renderables) { - if (obj.alpha_blending) + if (obj.alpha_blending) { glEnable(GL_BLEND); - else + } + else { glDisable(GL_BLEND); - - if (obj.depth_test) - glEnable(GL_DEPTH_TEST); - else - glDisable(GL_DEPTH_TEST); + } + + if (obj.depth_test) { + glEnable(GL_DEPTH_TEST); + } + else { + glDisable(GL_DEPTH_TEST); + } auto in = dynamic_cast(obj.unif_in); in->program->execute_with(in, obj.geometry); diff --git a/libopenage/renderer/opengl/renderer.h b/libopenage/renderer/opengl/renderer.h index f595d881de..a569ee3893 100644 --- a/libopenage/renderer/opengl/renderer.h +++ b/libopenage/renderer/opengl/renderer.h @@ -25,7 +25,7 @@ class GlRenderer : public Renderer { std::unique_ptr add_shader(std::vector const&) override; std::unique_ptr create_texture_target(std::vector) override; - RenderTarget const* get_framebuffer_target() override; + RenderTarget const* get_display_target() override; void render(RenderPass const&) override; @@ -35,7 +35,7 @@ class GlRenderer : public Renderer { std::vector shaders; - GlRenderTarget framebuffer; + GlRenderTarget display; }; }}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/shader_program.cpp b/libopenage/renderer/opengl/shader_program.cpp index ca9bc76fb4..98a4c0bf2f 100644 --- a/libopenage/renderer/opengl/shader_program.cpp +++ b/libopenage/renderer/opengl/shader_program.cpp @@ -242,12 +242,6 @@ GlShaderProgram::GlShaderProgram(const std::vector &src log::log(MSG(info) << "Created OpenGL shader program"); } -GlShaderProgram::~GlShaderProgram() { - if (this->id != 0) { - glDeleteProgram(this->id); - } -} - GlShaderProgram::GlShaderProgram(GlShaderProgram &&other) : uniforms(std::move(other.uniforms)) , id(other.id) { @@ -255,6 +249,10 @@ GlShaderProgram::GlShaderProgram(GlShaderProgram &&other) } GlShaderProgram& GlShaderProgram::operator=(GlShaderProgram&& other) { + if (this->id != 0) { + glDeleteProgram(this->id); + } + this->uniforms = std::move(other.uniforms); this->id = other.id; other.id = 0; @@ -262,6 +260,12 @@ GlShaderProgram& GlShaderProgram::operator=(GlShaderProgram&& other) { return *this; } +GlShaderProgram::~GlShaderProgram() { + if (this->id != 0) { + glDeleteProgram(this->id); + } +} + void GlShaderProgram::use() const { glUseProgram(this->id); diff --git a/libopenage/renderer/opengl/shader_program.h b/libopenage/renderer/opengl/shader_program.h index 75fe68f748..9aa7b378ae 100644 --- a/libopenage/renderer/opengl/shader_program.h +++ b/libopenage/renderer/opengl/shader_program.h @@ -8,7 +8,6 @@ #include "../shader_program.h" #include "../resources/shader_source.h" -#include "../../util/vector.h" #include "../renderer.h" #include "uniform_input.h" #include "context.h" @@ -18,7 +17,7 @@ namespace openage { namespace renderer { namespace opengl { -/// GLSL uniform types. +/// GLSL uniform types enum class gl_uniform_t { I32, U32, @@ -30,14 +29,16 @@ enum class gl_uniform_t { SAMPLER2D, }; -/// @returns the size in bytes of a GLSL uniform type +/// Returns the size in bytes of a GLSL uniform type size_t uniform_size(gl_uniform_t); +/// Represents a uniform location in the shader program struct GlUniform { gl_uniform_t type; GLint location; }; +/// A handle to an OpenGL shader program class GlShaderProgram : public ShaderProgram { public: /// Tries to create a shader program from the given sources. @@ -45,16 +46,19 @@ class GlShaderProgram : public ShaderProgram { explicit GlShaderProgram(const std::vector&, const gl_context_capabilities&); ~GlShaderProgram(); + /// No copying. GlShaderProgram(const GlShaderProgram&) = delete; GlShaderProgram& operator=(const GlShaderProgram&) = delete; + /// Moving is allowed. GlShaderProgram(GlShaderProgram&&); GlShaderProgram& operator=(GlShaderProgram&&); /// Bind this program as the currently used one in the OpenGL context. void use() const; - /// Renders the specified renderable using this shader program. + /// Does what the description of Renderable specifies - updates the uniform values + /// and draws the Geometry of it's not nullptr. void execute_with(const GlUniformInput*, const Geometry*); bool has_uniform(const char*) override; @@ -72,16 +76,19 @@ class GlShaderProgram : public ShaderProgram { void set_tex(UniformInput*, const char*, Texture const*) override; private: + /// A map of uniform locations from their names std::map uniforms; // TODO parse uniform buffer structure ugh // std::unordered_map uniform_buffers; // GlVertexInputInfo; - /// The GL shader program ID + /// The GL shader program handle GLuint id; + /// A map from sampler uniform names to their assigned texture units std::unordered_map texunits_per_unifs; + /// A map from texture units to the texture handles that are currently bound to them std::unordered_map textures_per_texunits; }; diff --git a/libopenage/renderer/opengl/texture.cpp b/libopenage/renderer/opengl/texture.cpp index dde7a2bee5..3bb3661312 100644 --- a/libopenage/renderer/opengl/texture.cpp +++ b/libopenage/renderer/opengl/texture.cpp @@ -29,8 +29,8 @@ GlTexture::GlTexture(const resources::TextureData& data) : Texture(data.get_info()) { // generate opengl texture handle - glGenTextures(1, &this->handle); - glBindTexture(GL_TEXTURE_2D, this->handle); + glGenTextures(1, &*this->handle); + glBindTexture(GL_TEXTURE_2D, *this->handle); // select pixel format auto fmt_in_out = gl_format(this->info.get_format()); @@ -56,8 +56,8 @@ GlTexture::GlTexture(size_t width, size_t height, resources::pixel_format fmt) std::vector())) { // generate opengl texture handle - glGenTextures(1, &this->handle); - glBindTexture(GL_TEXTURE_2D, this->handle); + glGenTextures(1, &*this->handle); + glBindTexture(GL_TEXTURE_2D, *this->handle); auto fmt_in_out = gl_format(fmt); @@ -72,12 +72,34 @@ GlTexture::GlTexture(size_t width, size_t height, resources::pixel_format fmt) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } +GlTexture::GlTexture(GlTexture &&other) + : Texture(std::move(other.info)) + , handle(std::move(other.handle)) { + // make the other handle empty + other.handle = std::experimental::optional(); +} + +GlTexture &GlTexture::operator =(GlTexture &&other) { + if (this->handle) { + glDeleteTextures(1, &*this->handle); + } + + this->info = std::move(other.info); + this->handle = std::move(other.handle); + // make the other handle empty + other.handle = std::experimental::optional(); + + return *this; +} + GlTexture::~GlTexture() { - glDeleteTextures(1, &this->handle); + if (this->handle) { + glDeleteTextures(1, &*this->handle); + } } GLuint GlTexture::get_handle() const { - return this->handle; + return *this->handle; } inline static size_t pixel_size(resources::pixel_format fmt) { diff --git a/libopenage/renderer/opengl/texture.h b/libopenage/renderer/opengl/texture.h index bfa3d15c3c..eb9f1b0473 100644 --- a/libopenage/renderer/opengl/texture.h +++ b/libopenage/renderer/opengl/texture.h @@ -3,6 +3,7 @@ #pragma once #include +#include #include "../texture.h" @@ -28,6 +29,14 @@ class GlTexture : public Texture { GlTexture(size_t width, size_t height, resources::pixel_format); ~GlTexture(); + // TODO support copying in the future + GlTexture(const GlTexture&) = delete; + GlTexture &operator =(const GlTexture&) = delete; + + /// Moving is supported. + GlTexture(GlTexture&&); + GlTexture &operator =(GlTexture&&); + /// Returns the OpenGL handle to this texture. GLuint get_handle() const; @@ -35,7 +44,7 @@ class GlTexture : public Texture { private: /// The OpenGL handle to this texture. - GLuint handle; + std::experimental::optional handle; }; }}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/uniform_input.h b/libopenage/renderer/opengl/uniform_input.h index 5d5e4896e4..7f4549d441 100644 --- a/libopenage/renderer/opengl/uniform_input.h +++ b/libopenage/renderer/opengl/uniform_input.h @@ -14,14 +14,15 @@ namespace opengl { class GlShaderProgram; +/// Describes OpenGL-specific uniform valuations. struct GlUniformInput : public UniformInput { + /// The program that this was created for. GlShaderProgram* program; - // store uniforms updates lazily - // execute_with reads, uploads, and clears - // std::unordered_map updates; - // enable partial valuations - only some uniforms - /// Map from uniform names to their locations in the update buffer. + /// We store uniform updates lazily. They are only actually uploaded to GPU + /// when a draw call is made. Update_offs maps the uniform names to where their + /// value is in update_data in terms of a byte-wise offset. This is only a partial + /// valuation, so not all uniforms have to be present here. std::unordered_map update_offs; /// Buffer containing untyped uniform update data. diff --git a/libopenage/renderer/renderer.h b/libopenage/renderer/renderer.h index 629b28fa0f..5f4429829b 100644 --- a/libopenage/renderer/renderer.h +++ b/libopenage/renderer/renderer.h @@ -2,7 +2,6 @@ #pragma once -#include #include #include @@ -15,65 +14,80 @@ namespace resources { class ShaderSource; } -// In terms of the graphics pipeline, there is no difference between APIs. The difference can be contained internally. -// ShaderProgram is API-independent. class ShaderProgram; class Geometry; class Texture; +/// The abstract base for uniform input. Not terribly interesting. class UniformInput { protected: - UniformInput() {} + UniformInput() = default; public: - virtual ~UniformInput() {} + virtual ~UniformInput() = default; }; +/// The abstract base for a render target. Not terribly interesting. class RenderTarget { protected: - RenderTarget() {} + RenderTarget() = default; public: - virtual ~RenderTarget() {} + virtual ~RenderTarget() = default; }; - -// each renderable shoud be UniformInput, Geometry, State -// Geometry could be Billboard, Mesh, for now restrict to Billboard -// each object is a shader valuation + execution of draw with settings and optional mesh -// instancing for same shader & mesh +/// A renderable is a set of shader uniform inputs and a possible draw call. +/// When a renderable is parsed, the uniform inputs are first passed to the shader they were created with. +/// Then, if the geometry is not nullptr, the shader is executed with the geometry and the specified settings +/// and the results are written into the render target. If the geometry is nullptr, the uniform values are +/// uploaded to the shader but no draw call is performed. This can be used to, for example, first set the values +/// of uniforms that many objects have in common, and then only upload the uniforms that vary between them in +/// each draw call. This works because uniform values in any given shader are preserved across a render pass. struct Renderable { + /// Uniform values to be set in the appropriate shader. UniformInput const *unif_in; - // can be nullptr to only set uniforms but do not perform draw call + /// The geometry. It can be a simple primitive or a complex mesh. + /// Can be nullptr to only set uniforms but do not perform draw call. Geometry const *geometry; + /// Whether to perform alpha-based color blending with whatever was in the render target before. bool alpha_blending; + /// Whether to perform depth testing and discard occluded fragments. bool depth_test; - bool back_culling; - bool front_clockwise; }; -// what's a render pass? a series of draw calls that output into a texture -// it's a function similarly to a shader -// this time, inputs are shader invocations (with gl state vars such as blending for each invocation) +/// A render pass is a series of draw calls represented by renderables that output into the given render target. struct RenderPass { + /// The renderables to parse and possibly execute. std::vector renderables; + /// The render targe to write into. RenderTarget const *target; }; +/// The renderer. This class is used for performing all graphics operations. It is abstract and has implementations +/// for various low-level graphics APIs like OpenGL. class Renderer { -protected: - Renderer() {} - public: virtual ~Renderer() = default; + /// Uploads the given texture data (usually loaded from disk) to graphics hardware and makes it available + /// as a Texture object that can be used for various operations. virtual std::unique_ptr add_texture(resources::TextureData const&) = 0; + /// Compiles the given shader source code into a shader program. A shader program is the main tool used + /// for graphics rendering. virtual std::unique_ptr add_shader(std::vector const&) = 0; + /// Constructs a render target from the given textures. All subsequent drawing operations pointed at this + /// target will write to these textures. The textures are attached to the render target according to their + /// internal format, for example RGB will be attached as a color component and DEPTH will be attached as a + /// depth component. (TODO depth, not implemented yet) virtual std::unique_ptr create_texture_target(std::vector) = 0; - virtual RenderTarget const* get_framebuffer_target() = 0; + /// Returns the built-in display target that represents the window. Passes pointed at this target will have + /// their output visible on the screen. + virtual RenderTarget const* get_display_target() = 0; + + /// Executes a render pass. virtual void render(RenderPass const&) = 0; }; diff --git a/libopenage/renderer/resources/shader_source.cpp b/libopenage/renderer/resources/shader_source.cpp index 5ec95cb8d1..a80d7ce540 100644 --- a/libopenage/renderer/resources/shader_source.cpp +++ b/libopenage/renderer/resources/shader_source.cpp @@ -29,13 +29,9 @@ ShaderSource::ShaderSource(shader_source_t type, std::string &&code) : type(type) , code(code) {} -ShaderSource ShaderSource::from_file(shader_source_t type, const util::Path &path) { - return ShaderSource(type, path.open().read()); -} - -ShaderSource ShaderSource::from_string(shader_source_t type, std::string &&code) { - return ShaderSource(type, std::move(code)); -} +ShaderSource::ShaderSource(shader_source_t type, const util::Path &path) + : type(type) + , code(path.open().read()) {} shader_source_t ShaderSource::get_type() const { return this->type; diff --git a/libopenage/renderer/resources/shader_source.h b/libopenage/renderer/resources/shader_source.h index 7947b108a9..52d39cbf38 100644 --- a/libopenage/renderer/resources/shader_source.h +++ b/libopenage/renderer/resources/shader_source.h @@ -6,12 +6,14 @@ #include "../../util/path.h" + namespace openage { namespace renderer { namespace resources { /// Available shader types present in modern graphics pipelines. -/// Also contains information about the source language. +/// Contains information about the source language and the shader +/// stage that it describes. enum class shader_source_t { glsl_vertex, glsl_geometry, @@ -20,25 +22,26 @@ enum class shader_source_t { glsl_fragment, }; +/// Returns a readable description of the given shader source type. const char *shader_source_type_to_str(shader_source_t); +/// Stores source code for a part of a shader program. class ShaderSource { public: /// Obtain shader source code from a file. - static ShaderSource from_file(shader_source_t, const util::Path &path); + ShaderSource(shader_source_t, const util::Path &path); /// Obtain shader source code from a string. - static ShaderSource from_string(shader_source_t, std::string &&code); + ShaderSource(shader_source_t, std::string &&code); - /// @returns a view of the shader source code + /// Returns a view of the shader source code const char *get_source() const; + /// Returns the type of this shader source. shader_source_t get_type() const; private: - ShaderSource(shader_source_t type, std::string &&code); - -private: + /// The type of the source. shader_source_t type; /// The shader source code. diff --git a/libopenage/renderer/resources/texture_data.h b/libopenage/renderer/resources/texture_data.h index 0d5e0cd1e6..d00421346b 100644 --- a/libopenage/renderer/resources/texture_data.h +++ b/libopenage/renderer/resources/texture_data.h @@ -14,30 +14,37 @@ namespace openage { namespace renderer { namespace resources { +/// Stores CPU-accessible texture data in a byte buffer. Can be loaded and stored from/onto disk +/// and passed to or read from graphics hardware. In a sense it provides a mediator between +/// the filesystem, the CPU and graphics hardware. class TextureData { public: - /** - * Create a texture from an image file. - * @param[in] use_metafile determines whether the loading should read an accompanying - * metadata file to split the texture into subtextures - * - * Uses SDL Image internally. For supported image file types, - * see the SDL_Image initialization in the engine. - */ + /// Create a texture from an image file. + /// @param[in] use_metafile determines whether the loading should read an accompanying + /// metadata file to split the texture into subtextures + /// + /// Uses SDL Image internally. For supported image file types, + /// see the SDL_Image initialization in the engine. TextureData(const util::Path &path, bool use_metafile = false); + /// Construct by moving the information and raw texture data from somewhere else. TextureData(TextureInfo &&info, std::vector &&data); + /// Returns the information describing this texture data. const TextureInfo& get_info() const; + /// Returns a pointer to the raw texture data. const uint8_t *get_data() const; + /// Stores this texture data in the given file in the PNG format. void store(const util::Path& file) const; private: + /// Information about this texture data. TextureInfo info; - std::vector data; + /// The raw texture data. + std::vector data; }; }}} // namespace openage::renderer::resources diff --git a/libopenage/renderer/resources/texture_info.h b/libopenage/renderer/resources/texture_info.h index 03ed5d23fa..6a09c3f591 100644 --- a/libopenage/renderer/resources/texture_info.h +++ b/libopenage/renderer/resources/texture_info.h @@ -18,16 +18,17 @@ enum class pixel_format { rgb8, /// 32 bits per pixel rgba8, + /// 24 bits per pixel, depth texture + depth24, }; -/** - * Information for texture processing. - * The class supports subtextures, so that one big texture ("texture atlas") - * can contain several smaller images. These are the ones actually to be - * rendered. - */ +/// Information for texture processing. +/// 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 TextureInfo { public: + /// Constructs a TextureInfo with the given information. TextureInfo(size_t width, size_t height, pixel_format, std::vector&&); TextureInfo() = default; @@ -36,35 +37,24 @@ class TextureInfo { /// @returns the format of pixels in this texture pixel_format get_format() const; - /** - * Return the dimensions of the whole texture bitmap - * @returns tuple(width, height) - */ + /// Returns the dimensions of the whole texture bitmap + /// @returns tuple(width, height) std::pair get_size() const; - /** - * @return the number of available subtextures - */ + /// Returns the number of available subtextures size_t get_subtexture_count() const; - /** - * Get the subtexture coordinates by its idea. - */ + /// Returns the coordinates of the subtexture with the given ID const gamedata::subtexture& get_subtexture(size_t subid) const; - /** - * Fetch the size of the given subtexture. - * @param subid: index of the requested subtexture - */ + /// Returns the size of the subtexture with the given ID std::pair 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 surface size. - */ + /// Returns 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 surface size. std::tuple get_subtexture_coordinates(size_t subid) const; protected: @@ -74,10 +64,8 @@ class TextureInfo { /// The pixel format of this texture. pixel_format format; - /** - * Some textures are merged together into texture atlases, large images which contain - * more than one individual texture. These are their positions in the atlas. - */ + /// Some textures are merged together into texture atlases, large images which contain + /// more than one individual texture. These are their positions in the atlas. std::vector subtextures; }; diff --git a/libopenage/renderer/shader_program.h b/libopenage/renderer/shader_program.h index d3e12f71a2..66587ffe53 100644 --- a/libopenage/renderer/shader_program.h +++ b/libopenage/renderer/shader_program.h @@ -15,6 +15,7 @@ class Texture; class ShaderProgram { private: + // Template dispatches for uniform variable setting. void update_uniform_input(UniformInput*) {} void update_uniform_input(UniformInput *input, const char *unif, int32_t val) { @@ -51,8 +52,15 @@ class ShaderProgram { } public: + /// Returns whether the shader program contains a uniform variable with the given name. virtual bool has_uniform(const char *unif) = 0; + /// Creates a new uniform input (a binding of uniform names to values) for this shader + /// and optionally sets some uniform values. To do that, just pass two arguments - + /// - a string literal and the value for that uniform for any uniform you want to set. + /// For example new_uniform_input("color", { 0.5, 0.5, 0.5, 1.0 }, "num", 5) will set + /// "color" to { 0.5, 0.5, 0.5, 0.5 } and "num" to 5. Types are important here and a type + /// mismatch between the uniform variable and the input might result in an error. template std::unique_ptr new_uniform_input(Ts... vals) { auto input = this->new_unif_in(); @@ -60,15 +68,17 @@ class ShaderProgram { return input; } + /// Updates the given uniform input with new uniform values similarly to new_uniform_input. + /// For example, update_uniform_input(in, "awesome", true) will set the "awesome" uniform + /// in addition to whatever values were in the uniform input before. template void update_uniform_input(UniformInput *input, const char *unif, T val, Ts... vals) { this->update_uniform_input(input, unif, val); this->update_uniform_input(input, vals...); } - void new_geometry(/*geometry_t*/) {} - protected: + // Virtual dispatches to the actual shader program implementation. virtual std::unique_ptr new_unif_in() = 0; virtual void set_i32(UniformInput*, const char*, int32_t) = 0; virtual void set_u32(UniformInput*, const char*, uint32_t) = 0; diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index 3c6179f91c..e47a65663c 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -78,7 +78,7 @@ void renderer_demo_0() { window.make_context_current(); auto renderer = std::make_unique(window.get_context()); - auto vshader_src = resources::ShaderSource::from_string( + auto vshader_src = resources::ShaderSource( resources::shader_source_t::glsl_vertex, R"s( #version 330 @@ -98,7 +98,7 @@ void main() { } )s"); - auto fshader_src = resources::ShaderSource::from_string( + auto fshader_src = resources::ShaderSource( resources::shader_source_t::glsl_fragment, R"s( #version 330 @@ -132,8 +132,6 @@ void main() { &quad, true, true, - true, - true, }; Renderable obj2{ @@ -141,8 +139,6 @@ void main() { &quad, true, true, - true, - true, }; Renderable obj3 { @@ -150,13 +146,11 @@ void main() { &quad, true, true, - true, - true, }; RenderPass pass { { obj1, obj2, obj3 }, - renderer->get_framebuffer_target(), + renderer->get_display_target(), }; render_demo test0{ diff --git a/libopenage/renderer/texture.h b/libopenage/renderer/texture.h index 3ae19217d3..f4336fef08 100644 --- a/libopenage/renderer/texture.h +++ b/libopenage/renderer/texture.h @@ -2,35 +2,30 @@ #pragma once -#include - -#include -#include -#include - -#include "../gamedata/texture.gen.h" #include "resources/texture_data.h" namespace openage { namespace renderer { -/** - * A handle to texture data that has been uploaded to the GPU. - * Obtained by registering some texture data to the renderer. - */ +/// An abstract base for a handle to a texture buffer allocated in graphics hardware. +/// Can be obtained by passing texture data to the renderer. class Texture { -protected: - Texture(resources::TextureInfo); - public: virtual ~Texture(); + /// Returns the texture information. const resources::TextureInfo& get_info() const; + /// Copies this texture's data from graphics hardware into a CPU-accessible + /// TextureData buffer. virtual resources::TextureData into_data() const = 0; protected: + /// Constructs the base with the given information. + Texture(resources::TextureInfo); + + /// Information about the size, format, etc. of this texture. resources::TextureInfo info; }; diff --git a/libopenage/renderer/window.cpp b/libopenage/renderer/window.cpp index 7f7f452c6e..76652c09bc 100644 --- a/libopenage/renderer/window.cpp +++ b/libopenage/renderer/window.cpp @@ -14,6 +14,7 @@ namespace openage { namespace renderer { +/// A static SDL singleton manager. Used to lazily initialize SDL. class SDL { public: static void make_available() { @@ -61,7 +62,9 @@ Window::Window(const char *title) SDL::make_available(); // Even with Vulkan rendering, we still need GL for QtQuick. - // We need HIGHDPI for eventual support of GUI scaling. (but it requires newer SDL2?) + // TODO Qt might support Vulkan natively in the future + // We need HIGHDPI for eventual support of GUI scaling. + // TODO HIGHDPI fails (requires newer SDL2?) int32_t window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_MAXIMIZED; //| SDL_WINDOW_HIGHDPI; this->window = SDL_CreateWindow( title, @@ -83,17 +86,29 @@ Window::Window(const char *title) // TODO catch window events such as resizes } -Window::~Window() { - SDL_DestroyWindow(this->window); - log::log(MSG(info) << "Destroyed SDL window"); +Window::Window(Window &&other) + : window(other.window) + , context(std::move(other.context)) { + other.window = nullptr; } -void Window::swap() { - SDL_GL_SwapWindow(this->window); +Window &Window::operator=(Window &&other) { + if (this->window != nullptr) { + SDL_DestroyWindow(this->window); + log::log(MSG(info) << "Destroyed SDL window"); + } + + this->window = other.window; + this->context = std::move(other.context); + other.window = nullptr; + return *this; } -void Window::make_context_current() { - SDL_GL_MakeCurrent(this->window, this->context->get_raw_context()); +Window::~Window() { + if (this->window != nullptr) { + SDL_DestroyWindow(this->window); + log::log(MSG(info) << "Destroyed SDL window"); + } } coord::window Window::get_size() { @@ -107,12 +122,20 @@ void Window::set_size(const coord::window &new_size) { } } -opengl::GlContext* Window::get_context() { - return &this->context.value(); +void Window::swap() { + SDL_GL_SwapWindow(this->window); } -SDL_Window* Window::get_raw_window() const { +void Window::make_context_current() { + SDL_GL_MakeCurrent(this->window, this->context->get_raw_context()); +} + +SDL_Window *Window::get_raw_window() const { return this->window; } +opengl::GlContext *Window::get_context() { + return &this->context.value(); +} + }} //openage::renderer diff --git a/libopenage/renderer/window.h b/libopenage/renderer/window.h index 11a36aacbf..f922a00413 100644 --- a/libopenage/renderer/window.h +++ b/libopenage/renderer/window.h @@ -3,8 +3,6 @@ #pragma once #include -#include -#include #include #include @@ -18,36 +16,31 @@ namespace renderer { class Window { public: + /// Create a shiny window with the given title. + Window(const char *title); + ~Window(); + + /// A window cannot be copied. Window(const Window &other) = delete; - Window(Window &&other) = delete; Window &operator =(const Window &other) = delete; - Window &operator =(Window &&other) = delete; - /** - * Create a shiny window with the given title. - */ - Window(const char *title); - ~Window(); + /// But it can be moved. + Window(Window &&other); + Window &operator =(Window &&other); - /** - * @returns the window dimensions - */ + /// Returns the dimensions of this window. coord::window get_size(); - /** - * Resize the window's client area. - * @param[in] update determines what? - */ + /// Force the window to the given size. It's generally not a good idea to use this, + /// as it makes the window jump around wierdly. void set_size(const coord::window &new_size); - /** - * Swaps the back and front framebuffers. - * Used to actually display the newly rendered frame on the screen. - */ + /// Swap the front and back framebuffers. This has to be after drawing every frame to actually + /// display it. void swap(); - /// Make this context the current rendering context of the window thread. - /// Only use this and most other GL functions on the window thread. + /// Make this window's context the current rendering context of the current thread. + /// Only use this and most other GL functions on a dedicated window thread. void make_context_current(); // TODO: this shouldn't be exposed. @@ -55,20 +48,20 @@ class Window { // 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; + /// Returns the raw SDL window handle. + SDL_Window *get_raw_window() const; - /** - * Return a reference to this window's GL context. - */ - opengl::GlContext* get_context(); + /// Return a pointer to this window's GL context. + opengl::GlContext *get_context(); private: + /// The current size of the framebuffer. coord::window size; + /// The SDL struct representing this window. SDL_Window *window; + /// This window's OpenGL context. It's optional because it can't be constructed immediately, + /// but after the constructor runs it's guaranteed to be available. std::experimental::optional context; }; From d9205606278d722900eb535da224c52d83c0859b Mon Sep 17 00:00:00 2001 From: Erik Griese Date: Mon, 17 Apr 2017 20:23:32 +0200 Subject: [PATCH 33/52] First work on pixel-perfect hitbox, minor changes - added `glDrawBuffers` call when creating fbo - moved a clear color after actually binding the fbo to clear - added r16ui pixel format - getting the gl_format for a pixel format now also returns the type of cpu-side data (i.e. GL_UNSIGNED_BYTE) - added adjusting the GL_UNPACK_ALIGNMENT and GL_PACK_ALIGNMENT if necesarry - into_data for textures uses glGetTexImage - moved renderables to extra files renderable.h and renderable.cpp - renderables have a unique id and can be retrieved by knowing this id - renderables have callback function for clicking and area selecting them - renderables can specify if they write their id or not (not fully working) - made some update_uniform public - added some uniform code for textures - basic code to test pixel perfect hitboxes --- libopenage/renderer/CMakeLists.txt | 3 +- libopenage/renderer/opengl/render_target.cpp | 8 +- libopenage/renderer/opengl/renderer.cpp | 7 +- libopenage/renderer/opengl/shader_program.cpp | 2 + libopenage/renderer/opengl/texture.cpp | 64 ++++++-- libopenage/renderer/opengl/texture.h | 10 +- libopenage/renderer/renderable.cpp | 69 ++++++++ libopenage/renderer/renderable.h | 64 ++++++++ libopenage/renderer/renderer.h | 20 +-- libopenage/renderer/resources/texture_info.h | 1 + libopenage/renderer/shader_program.h | 6 +- libopenage/renderer/tests.cpp | 148 +++++++++++++++++- libopenage/renderer/texture.h | 2 +- 13 files changed, 359 insertions(+), 45 deletions(-) create mode 100644 libopenage/renderer/renderable.cpp create mode 100644 libopenage/renderer/renderable.h diff --git a/libopenage/renderer/CMakeLists.txt b/libopenage/renderer/CMakeLists.txt index 1a68c4bdbf..57a022d269 100644 --- a/libopenage/renderer/CMakeLists.txt +++ b/libopenage/renderer/CMakeLists.txt @@ -1,9 +1,10 @@ add_sources(libopenage tests.cpp texture.cpp - window.cpp + window.cpp text.cpp color.cpp + renderable.cpp ) pxdgen( diff --git a/libopenage/renderer/opengl/render_target.cpp b/libopenage/renderer/opengl/render_target.cpp index b3ce942cca..c60449720a 100644 --- a/libopenage/renderer/opengl/render_target.cpp +++ b/libopenage/renderer/opengl/render_target.cpp @@ -16,12 +16,18 @@ GlRenderTarget::GlRenderTarget() GlRenderTarget::GlRenderTarget(std::vector textures) { glGenFramebuffers(1, &*this->handle); glBindFramebuffer(GL_FRAMEBUFFER, *this->handle); + + std::vector drawBuffers; for (size_t i = 0; i < textures.size(); i++) { // TODO figure out attachment points from pixel formats - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, textures[i]->get_handle(), 0); + auto attachmentPoint = GL_COLOR_ATTACHMENT0 + i; + glFramebufferTexture2D(GL_FRAMEBUFFER, attachmentPoint, GL_TEXTURE_2D, textures[i]->get_handle(), 0); + drawBuffers.push_back(attachmentPoint); } + glDrawBuffers(drawBuffers.size(), drawBuffers.data()); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { throw Error(MSG(err) << "Could not create OpenGL framebuffer."); } diff --git a/libopenage/renderer/opengl/renderer.cpp b/libopenage/renderer/opengl/renderer.cpp index ed629556e3..0d12109c55 100644 --- a/libopenage/renderer/opengl/renderer.cpp +++ b/libopenage/renderer/opengl/renderer.cpp @@ -4,6 +4,7 @@ #include "../../log/log.h" #include "../../error/error.h" +#include "../renderable.h" #include "texture.h" #include "shader_program.h" #include "uniform_input.h" @@ -42,12 +43,12 @@ RenderTarget const* GlRenderer::get_display_target() { } void GlRenderer::render(RenderPass const& pass) { - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - auto gl_target = dynamic_cast(pass.target); gl_target->bind_write(); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + for (auto obj : pass.renderables) { if (obj.alpha_blending) { glEnable(GL_BLEND); diff --git a/libopenage/renderer/opengl/shader_program.cpp b/libopenage/renderer/opengl/shader_program.cpp index 98a4c0bf2f..f957252f12 100644 --- a/libopenage/renderer/opengl/shader_program.cpp +++ b/libopenage/renderer/opengl/shader_program.cpp @@ -317,6 +317,8 @@ void GlShaderProgram::execute_with(const GlUniformInput *unif_in, const Geometry GLuint tex = *reinterpret_cast(ptr); glActiveTexture(GL_TEXTURE0 + tex_unit); glBindTexture(GL_TEXTURE_2D, tex); + //TODO: maybe call this at an more appropiate position + glUniform1i(loc, tex_unit); this->textures_per_texunits[tex_unit] = tex; break; } diff --git a/libopenage/renderer/opengl/texture.cpp b/libopenage/renderer/opengl/texture.cpp index 3bb3661312..8306d58ef6 100644 --- a/libopenage/renderer/opengl/texture.cpp +++ b/libopenage/renderer/opengl/texture.cpp @@ -3,6 +3,7 @@ #include "texture.h" #include +#include #include "../../error/error.h" #include "../resources/texture_data.h" @@ -14,17 +15,35 @@ namespace renderer { namespace opengl { /// Returns the input and output formats for GL. -inline static std::pair gl_format(resources::pixel_format fmt) { +inline static std::tuple gl_format(resources::pixel_format fmt) { switch (fmt) { + case resources::pixel_format::r16ui: + return std::make_tuple(GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT); case resources::pixel_format::rgb8: - return std::make_pair(GL_RGB8, GL_RGB); + return std::make_tuple(GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE); case resources::pixel_format::rgba8: - return std::make_pair(GL_RGBA8, GL_RGBA); + return std::make_tuple(GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE); default: throw Error(MSG(err) << "invalid texture format passed to OpenGL."); } } +static GLint alignment_requirement(resources::pixel_format fmt) { + switch (fmt) { + case resources::pixel_format::r16ui: + return 2; + case resources::pixel_format::rgb8: + return 1; + case resources::pixel_format::rgba8: + return 4; + default: + throw Error(MSG(err) << "invalid texture format passed to OpenGL."); + } +} + +GLint GlTexture::PACK_ALIGNMENT = 4; +GLint GlTexture::UNPACK_ALIGNMENT = 4; + GlTexture::GlTexture(const resources::TextureData& data) : Texture(data.get_info()) { @@ -37,10 +56,13 @@ GlTexture::GlTexture(const resources::TextureData& data) // store raw pixels to gpu auto size = this->info.get_size(); + + adjust_unpack_alignment(alignment_requirement(this->info.get_format())); + glTexImage2D( GL_TEXTURE_2D, 0, - fmt_in_out.first, size.first, size.second, 0, - fmt_in_out.second, GL_UNSIGNED_BYTE, data.get_data() + std::get<0>(fmt_in_out), size.first, size.second, 0, + std::get<1>(fmt_in_out), std::get<2>(fmt_in_out), data.get_data() ); // drawing settings @@ -63,8 +85,8 @@ GlTexture::GlTexture(size_t width, size_t height, resources::pixel_format fmt) glTexImage2D( GL_TEXTURE_2D, 0, - fmt_in_out.first, width, height, 0, - fmt_in_out.second, GL_UNSIGNED_BYTE, nullptr + std::get<0>(fmt_in_out), width, height, 0, + std::get<1>(fmt_in_out), std::get<2>(fmt_in_out), nullptr ); // TODO these are outdated, use sampler settings @@ -104,6 +126,8 @@ GLuint GlTexture::get_handle() const { inline static size_t pixel_size(resources::pixel_format fmt) { switch (fmt) { + case resources::pixel_format::r16ui: + return 2; case resources::pixel_format::rgb8: return 3; case resources::pixel_format::rgba8: @@ -113,16 +137,34 @@ inline static size_t pixel_size(resources::pixel_format fmt) { } } -resources::TextureData GlTexture::into_data() const { +resources::TextureData GlTexture::into_data() { auto size = this->info.get_size(); auto fmt_in_out = gl_format(this->info.get_format()); std::vector data(size.first * size.second * pixel_size(this->info.get_format())); - GlRenderTarget buf( { this } ); - buf.bind_read(); - glReadPixels(0, 0, size.first, size.second, fmt_in_out.second, GL_UNSIGNED_BYTE, data.data()); + adjust_pack_alignment(alignment_requirement(this->info.get_format())); + glBindTexture(GL_TEXTURE_2D, *this->handle); + glGetTexImage(GL_TEXTURE_2D, 0, std::get<1>(fmt_in_out), std::get<2>(fmt_in_out), data.data()); return resources::TextureData(resources::TextureInfo(this->info), std::move(data)); } +void GlTexture::adjust_unpack_alignment(GLint alignment) { + if (alignment == this->UNPACK_ALIGNMENT) { + return; + } + + glPixelStorei(GL_UNPACK_ALIGNMENT, alignment); + this->UNPACK_ALIGNMENT = alignment; +} + +void GlTexture::adjust_pack_alignment(GLint alignment) { + if (alignment == this->PACK_ALIGNMENT) { + return; + } + + glPixelStorei(GL_PACK_ALIGNMENT, alignment); + this->PACK_ALIGNMENT = alignment; +} + }}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/texture.h b/libopenage/renderer/opengl/texture.h index eb9f1b0473..b5737d1794 100644 --- a/libopenage/renderer/opengl/texture.h +++ b/libopenage/renderer/opengl/texture.h @@ -40,11 +40,19 @@ class GlTexture : public Texture { /// Returns the OpenGL handle to this texture. GLuint get_handle() const; - resources::TextureData into_data() const override; + resources::TextureData into_data() override; private: /// The OpenGL handle to this texture. std::experimental::optional handle; + + /// The current pack alignment. See glPixelStorei + static GLint PACK_ALIGNMENT; + /// The current unpack alignment. See glPixelStorei + static GLint UNPACK_ALIGNMENT; + + void adjust_unpack_alignment(const GLint alignment); + void adjust_pack_alignment(const GLint alignment); }; }}} // openage::renderer::opengl diff --git a/libopenage/renderer/renderable.cpp b/libopenage/renderer/renderable.cpp new file mode 100644 index 0000000000..3c622c0742 --- /dev/null +++ b/libopenage/renderer/renderable.cpp @@ -0,0 +1,69 @@ +// Copyright 2015-2017 the openage authors. See copying.md for legal info. + +#include "renderable.h" + +namespace openage { +namespace renderer { + +std::vector Renderable::RENDERABLES; +std::vector Renderable::FREE_IDS; +std::mutex Renderable::ID_MUTEX; + +Renderable::Renderable(UniformInput const *uniform_input, Geometry const* geometry, const bool alpha_blending, const bool depth_test, + const bool writes_id, const function_t &on_click, const function_t &on_area_select) + : id{this->free_id()}, unif_in{uniform_input}, geometry{geometry}, alpha_blending{alpha_blending}, depth_test{depth_test}, + writes_id{writes_id}, on_click_callback{on_click}, on_area_selected_callback{on_area_select} +{ + std::lock_guard lock(ID_MUTEX); + RENDERABLES[this->id] = this; +} + +Renderable::Renderable(const Renderable& renderable) + : Renderable{ + renderable.unif_in, renderable.geometry, renderable.alpha_blending, renderable.depth_test, renderable.writes_id, + renderable.on_click_callback, renderable.on_area_selected_callback + } +{ + +} + +Renderable& Renderable::operator=(const Renderable& renderable) +{ + this->unif_in = renderable.unif_in; + this->geometry = renderable.geometry; + this->alpha_blending = renderable.alpha_blending; + this->depth_test = renderable.depth_test; + this->writes_id = renderable.writes_id; + this->on_click_callback = renderable.on_click_callback; + this->on_area_selected_callback = renderable.on_area_selected_callback; +} + +Renderable::~Renderable() +{ + std::lock_guard lock(ID_MUTEX); + RENDERABLES[this->id] = nullptr; + FREE_IDS.push_back(this->id); +} + +Renderable::id_t Renderable::free_id() +{ + std::lock_guard lock(ID_MUTEX); + if (FREE_IDS.size() > 0) { + auto free_id = FREE_IDS.back(); + FREE_IDS.pop_back(); + return free_id; + } + + // create new id by incrementing current highest id, and also allocationg the place for + // the new renderable with this id in the RENDERABLES vector + auto new_id = static_cast(RENDERABLES.size()); + RENDERABLES.push_back(nullptr); + return new_id; +} + +Renderable* Renderable::get(const id_t id) +{ + return RENDERABLES[id]; +} + +}} diff --git a/libopenage/renderer/renderable.h b/libopenage/renderer/renderable.h new file mode 100644 index 0000000000..4d30ea1f13 --- /dev/null +++ b/libopenage/renderer/renderable.h @@ -0,0 +1,64 @@ +// Copyright 2015-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include +#include +#include +#include + +namespace openage { +namespace renderer { + +class UniformInput; +class Geometry; + +/// A renderable is a set of shader uniform inputs and a possible draw call. +/// When a renderable is parsed, the uniform inputs are first passed to the shader they were created with. +/// Then, if the geometry is not nullptr, the shader is executed with the geometry and the specified settings +/// and the results are written into the render target. If the geometry is nullptr, the uniform values are +/// uploaded to the shader but no draw call is performed. This can be used to, for example, first set the values +/// of uniforms that many objects have in common, and then only upload the uniforms that vary between them in +/// each draw call. This works because uniform values in any given shader are preserved across a render pass. +class Renderable { +public: + using function_t = std::function; + using id_t = uint16_t; + + Renderable(UniformInput const *uniform_input = nullptr, Geometry const* geometry = nullptr, const bool alpha_blending = true, const bool depth_test = true, + const bool writes_id = true, const function_t &on_click = [](){}, const function_t &on_area_select = [](){}); + Renderable(const Renderable& renderable); + ~Renderable(); + + Renderable& operator=(const Renderable& renderable); + Renderable& operator=(const Renderable&& renderable) = delete; + + static Renderable* get(const id_t id); + + /// Uniform values to be set in the appropriate shader. + UniformInput const *unif_in; + /// The geometry. It can be a simple primitive or a complex mesh. + /// Can be nullptr to only set uniforms but do not perform draw call. + Geometry const *geometry; + /// Whether to perform alpha-based color blending with whatever was in the render target before. + bool alpha_blending; + /// Whether to perform depth testing and discard occluded fragments. + bool depth_test; + + const id_t id; + + bool writes_id; + + function_t on_click_callback; + function_t on_area_selected_callback; + +private: + static id_t free_id(); + + static std::vector RENDERABLES; + static std::vector FREE_IDS; + + static std::mutex ID_MUTEX; +}; + +}} diff --git a/libopenage/renderer/renderer.h b/libopenage/renderer/renderer.h index 5f4429829b..82d046f857 100644 --- a/libopenage/renderer/renderer.h +++ b/libopenage/renderer/renderer.h @@ -17,6 +17,7 @@ namespace resources { class ShaderProgram; class Geometry; class Texture; +class Renderable; /// The abstract base for uniform input. Not terribly interesting. class UniformInput { @@ -36,25 +37,6 @@ class RenderTarget { virtual ~RenderTarget() = default; }; -/// A renderable is a set of shader uniform inputs and a possible draw call. -/// When a renderable is parsed, the uniform inputs are first passed to the shader they were created with. -/// Then, if the geometry is not nullptr, the shader is executed with the geometry and the specified settings -/// and the results are written into the render target. If the geometry is nullptr, the uniform values are -/// uploaded to the shader but no draw call is performed. This can be used to, for example, first set the values -/// of uniforms that many objects have in common, and then only upload the uniforms that vary between them in -/// each draw call. This works because uniform values in any given shader are preserved across a render pass. -struct Renderable { - /// Uniform values to be set in the appropriate shader. - UniformInput const *unif_in; - /// The geometry. It can be a simple primitive or a complex mesh. - /// Can be nullptr to only set uniforms but do not perform draw call. - Geometry const *geometry; - /// Whether to perform alpha-based color blending with whatever was in the render target before. - bool alpha_blending; - /// Whether to perform depth testing and discard occluded fragments. - bool depth_test; -}; - /// A render pass is a series of draw calls represented by renderables that output into the given render target. struct RenderPass { /// The renderables to parse and possibly execute. diff --git a/libopenage/renderer/resources/texture_info.h b/libopenage/renderer/resources/texture_info.h index 6a09c3f591..6cae0c50e1 100644 --- a/libopenage/renderer/resources/texture_info.h +++ b/libopenage/renderer/resources/texture_info.h @@ -14,6 +14,7 @@ namespace resources { /// How the pixels are represented in a texture. enum class pixel_format { + r16ui, //16 bits per pixel, stored as unsigned int /// 24 bits per pixel, no alpha channel rgb8, /// 32 bits per pixel diff --git a/libopenage/renderer/shader_program.h b/libopenage/renderer/shader_program.h index 66587ffe53..ed68ca815d 100644 --- a/libopenage/renderer/shader_program.h +++ b/libopenage/renderer/shader_program.h @@ -14,7 +14,7 @@ class UniformInput; class Texture; class ShaderProgram { -private: +public: // Template dispatches for uniform variable setting. void update_uniform_input(UniformInput*) {} @@ -45,6 +45,10 @@ class ShaderProgram { void update_uniform_input(UniformInput *input, const char *unif, Eigen::Vector4f const &val) { this->set_v4f32(input, unif, val); } + + void update_uniform_input(UniformInput *input, const char *unif, Texture const *val) { + this->set_tex(input, unif, val); + } template void update_uniform_input(UniformInput*, const char*, T) { diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index e47a65663c..6b056bb58b 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "../log/log.h" #include "geometry.h" @@ -11,6 +12,7 @@ #include "resources/shader_source.h" #include "opengl/renderer.h" #include "window.h" +#include "renderable.h" namespace openage { @@ -24,9 +26,9 @@ struct render_demo { std::function setup; std::function frame; std::function resize; +std::function click; }; - void render_test(Window &window, const render_demo *actions) { SDL_Event event; @@ -64,12 +66,16 @@ while (running) { break; } break; + } + case SDL_MOUSEBUTTONDOWN: { + actions->click(event.button.x, event.button.y); + break; }} } actions->frame(); - window.swap(); + //window.swap(); } } @@ -96,34 +102,88 @@ void main() { gl_Position.x += (bounds[0] + bounds[2]) / 2.0; gl_Position.y += (bounds[1] + bounds[3]) / 2.0; } +)s"); + + auto vshader_display_src = resources::ShaderSource( + resources::shader_source_t::glsl_vertex, + R"s( +#version 330 + +out vec2 v_uv; + +void main() { + gl_Position.x = 2.0 * float(gl_VertexID & 1) - 1.0; + gl_Position.y = 2.0 * float((gl_VertexID & 2) >> 1) - 1.0; + gl_Position.z = 1.0; + gl_Position.w = 1.0; + + v_uv = gl_Position.xy * 0.5 + 0.5; +} )s"); auto fshader_src = resources::ShaderSource( resources::shader_source_t::glsl_fragment, R"s( #version 330 + uniform vec4 color; -out vec4 col; +uniform uint u_id; +uniform uint writes_id; + +layout(location=0) out vec4 col; +layout(location=1) out uint id; void main() { col = color; + if (bool(writes_id)) { + id = u_id + 1u; + } else { + //not writing anything does produce undefined values + id = 0u; + } +} +)s"); + + auto fshader_display_src = resources::ShaderSource( + resources::shader_source_t::glsl_fragment, + R"s( +#version 330 +uniform sampler2D color_texture; + +in vec2 v_uv; +out vec4 col; + +void main() { + vec3 col2 = texture(color_texture, v_uv).xyz; + col = vec4(col2, 1.0); } )s"); + std::vector srcs = {vshader_src, fshader_src}; + std::vector srcs_display = {vshader_display_src, fshader_display_src}; auto shader = renderer->add_shader(srcs); + log::log(INFO << "Before dispaly shader"); + auto shader_display = renderer->add_shader(srcs_display); auto unif_in1 = shader->new_uniform_input( - "bounds", Eigen::Vector4f(-0.6f, -0.6f, -0.4f, -0.4f), - "color", Eigen::Vector4f(1.0f, 0.0f, 0.0f, 1.0f) + "bounds", Eigen::Vector4f(-1.0f, -1.0f, -0.8f, -0.8f), + "color", Eigen::Vector4f(1.0f, 0.0f, 0.0f, 1.0f), + "u_id", 1u, + "writes_id", 1u + ); auto unif_in2 = shader->new_uniform_input( "bounds", Eigen::Vector4f(0.0f, 0.3f, 0.3f, 0.5f), - "color", Eigen::Vector4f(0.0f, 1.0f, 0.0f, 1.0f) + "color", Eigen::Vector4f(0.0f, 1.0f, 0.0f, 1.0f), + "u_id", 2u, + "writes_id", 1u ); auto unif_in3 = shader->new_uniform_input( "bounds", Eigen::Vector4f(0.5f, -0.7f, 0.6f, -0.3f), - "color", Eigen::Vector4f(0.0f, 0.0f, 1.0f, 1.0f) + "color", Eigen::Vector4f(0.0f, 0.0f, 1.0f, 1.0f), + "u_id", 3u, + "writes_id", 1u ); Geometry quad; @@ -134,6 +194,11 @@ void main() { true, }; + obj1.on_click_callback = [](){ + log::log(INFO << "Object 1 clicked"); + }; + shader->update_uniform_input(unif_in1.get(), "u_id", static_cast(obj1.id)); + Renderable obj2{ unif_in2.get(), &quad, @@ -141,18 +206,55 @@ void main() { true, }; + obj2.on_click_callback = [](){ + log::log(INFO << "Object 2 clicked"); + }; + shader->update_uniform_input(unif_in2.get(), "u_id", static_cast(obj2.id)); + Renderable obj3 { unif_in3.get(), &quad, true, true, + false + }; + + obj3.on_click_callback = [](){ + log::log(INFO << "Object 3 clicked"); + }; + shader->update_uniform_input(unif_in3.get(), "u_id", static_cast(obj3.id), "writes_id", obj3.writes_id ? 1u : 0u); + + std::unique_ptr color_texture = std::unique_ptr( new opengl::GlTexture(window.get_size().x, window.get_size().y, resources::pixel_format::rgb8) ); + std::unique_ptr id_texture = std::unique_ptr( new opengl::GlTexture(window.get_size().x, window.get_size().y, resources::pixel_format::r16ui) ); + opengl::GlTexture* color_texture_ptr = color_texture.get(); + opengl::GlTexture* id_texture_ptr = id_texture.get(); + std::vector fbo_textures; + fbo_textures.push_back(color_texture_ptr); + fbo_textures.push_back(id_texture_ptr); + + auto fbo = std::unique_ptr(new opengl::GlRenderTarget(fbo_textures)); + + auto color_texture_uniform = shader_display->new_uniform_input("color_texture", static_cast(color_texture.get())); + Renderable display_obj { + color_texture_uniform.get(), + &quad, + false, + false, }; RenderPass pass { { obj1, obj2, obj3 }, + fbo.get() + }; + + RenderPass display_pass { + { display_obj } , renderer->get_display_target(), }; + resources::TextureData id_texture_data = id_texture->into_data(); + bool texture_data_valid = false; + render_demo test0{ // init [&](Window */*window*/) { @@ -171,7 +273,9 @@ void main() { }, // frame [&]() { + texture_data_valid = false; renderer->render(pass); + renderer->render(display_pass); window.swap(); window.get_context()->check_error(); /*simplequad->use(); @@ -189,6 +293,36 @@ void main() { [&](const coord::window &new_size) { // handle in renderer.. glViewport(0, 0, new_size.x, new_size.y); + //resize fbo + color_texture = std::unique_ptr( new opengl::GlTexture(new_size.x, new_size.y, resources::pixel_format::rgb8) ); + id_texture = std::unique_ptr( new opengl::GlTexture(new_size.x, new_size.y, resources::pixel_format::r16ui) ); + std::vector fbo_textures_new; + fbo_textures_new.push_back(color_texture.get()); + fbo_textures_new.push_back(id_texture.get()); + + fbo = std::unique_ptr(new opengl::GlRenderTarget(fbo_textures_new)); + shader_display->update_uniform_input(color_texture_uniform.get(), "color_texture", static_cast(color_texture.get())); + pass.target = fbo.get(); + }, + //click + [&](const uint16_t x, const uint16_t y) { + log::log(INFO << "Clicked at location (" << x << ", " << y << ")"); + if (!texture_data_valid) { + id_texture_data = id_texture->into_data(); + texture_data_valid = true; + } + auto texture_dimensions = id_texture->get_info().get_size(); + auto position = (texture_dimensions.second-y)*texture_dimensions.first + x; + position *= 2; //2 byte per pixel + auto id = *reinterpret_cast(id_texture_data.get_data()+position); + log::log(INFO << "Id-texture-value at location: " << id); + if (id == 0) { + //no renderable at given location + log::log(INFO << "Clicked at non existent object"); + return; + } + id--; //real id is id-1 + Renderable::get(id)->on_click_callback(); } }; diff --git a/libopenage/renderer/texture.h b/libopenage/renderer/texture.h index f4336fef08..8de75030bc 100644 --- a/libopenage/renderer/texture.h +++ b/libopenage/renderer/texture.h @@ -19,7 +19,7 @@ class Texture { /// Copies this texture's data from graphics hardware into a CPU-accessible /// TextureData buffer. - virtual resources::TextureData into_data() const = 0; + virtual resources::TextureData into_data() = 0; protected: /// Constructs the base with the given information. From bd6bcc5b799cafbaacfa98f56c6d3f1f07e82ea7 Mon Sep 17 00:00:00 2001 From: Erik Griese Date: Tue, 18 Apr 2017 22:04:09 +0200 Subject: [PATCH 34/52] Working optional id write also dependent on alpha value. A bit hacky --- libopenage/renderer/opengl/texture.cpp | 6 ++++ libopenage/renderer/renderable.cpp | 2 ++ libopenage/renderer/resources/texture_info.h | 2 ++ libopenage/renderer/tests.cpp | 29 ++++++++++---------- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/libopenage/renderer/opengl/texture.cpp b/libopenage/renderer/opengl/texture.cpp index 8306d58ef6..f09bd10f7a 100644 --- a/libopenage/renderer/opengl/texture.cpp +++ b/libopenage/renderer/opengl/texture.cpp @@ -23,6 +23,8 @@ inline static std::tuple gl_format(resources::pixel_forma return std::make_tuple(GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE); case resources::pixel_format::rgba8: return std::make_tuple(GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE); + case resources::pixel_format::rgba8ui: + return std::make_tuple(GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE); default: throw Error(MSG(err) << "invalid texture format passed to OpenGL."); } @@ -36,6 +38,8 @@ static GLint alignment_requirement(resources::pixel_format fmt) { return 1; case resources::pixel_format::rgba8: return 4; + case resources::pixel_format::rgba8ui: + return 4; default: throw Error(MSG(err) << "invalid texture format passed to OpenGL."); } @@ -132,6 +136,8 @@ inline static size_t pixel_size(resources::pixel_format fmt) { return 3; case resources::pixel_format::rgba8: return 4; + case resources::pixel_format::rgba8ui: + return 4; default: throw Error(MSG(err) << "Tried to find size of unknown pixel format."); } diff --git a/libopenage/renderer/renderable.cpp b/libopenage/renderer/renderable.cpp index 3c622c0742..80d596c1dd 100644 --- a/libopenage/renderer/renderable.cpp +++ b/libopenage/renderer/renderable.cpp @@ -36,6 +36,8 @@ Renderable& Renderable::operator=(const Renderable& renderable) this->writes_id = renderable.writes_id; this->on_click_callback = renderable.on_click_callback; this->on_area_selected_callback = renderable.on_area_selected_callback; + + return *this; } Renderable::~Renderable() diff --git a/libopenage/renderer/resources/texture_info.h b/libopenage/renderer/resources/texture_info.h index 6cae0c50e1..49c4ffb42e 100644 --- a/libopenage/renderer/resources/texture_info.h +++ b/libopenage/renderer/resources/texture_info.h @@ -19,6 +19,8 @@ enum class pixel_format { rgb8, /// 32 bits per pixel rgba8, + /// 32 bits per pixel stored as unsigned integer + rgba8ui, /// 24 bits per pixel, depth texture depth24, }; diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index 6b056bb58b..eddbaadb6a 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -131,15 +131,16 @@ uniform uint u_id; uniform uint writes_id; layout(location=0) out vec4 col; -layout(location=1) out uint id; +layout(location=1) out vec4 id; void main() { col = color; - if (bool(writes_id)) { - id = u_id + 1u; + uint id_write = u_id + 1u; // 0 is for no object + vec3 id_vector = vec3(float(id_write >> 8) / 255.0, float(id_write & 255u) / 255.0, 0.0); + if (bool(writes_id) && (color.a > 0.0)) { + id = vec4(id_vector, 1.0); } else { - //not writing anything does produce undefined values - id = 0u; + id = vec4(id_vector, 0.0); } } )s"); @@ -154,8 +155,7 @@ in vec2 v_uv; out vec4 col; void main() { - vec3 col2 = texture(color_texture, v_uv).xyz; - col = vec4(col2, 1.0); + col = texture(color_texture, v_uv); } )s"); @@ -181,7 +181,7 @@ void main() { ); auto unif_in3 = shader->new_uniform_input( "bounds", Eigen::Vector4f(0.5f, -0.7f, 0.6f, -0.3f), - "color", Eigen::Vector4f(0.0f, 0.0f, 1.0f, 1.0f), + "color", Eigen::Vector4f(0.0f, 0.0f, 1.0f, 0.5f), "u_id", 3u, "writes_id", 1u ); @@ -224,8 +224,8 @@ void main() { }; shader->update_uniform_input(unif_in3.get(), "u_id", static_cast(obj3.id), "writes_id", obj3.writes_id ? 1u : 0u); - std::unique_ptr color_texture = std::unique_ptr( new opengl::GlTexture(window.get_size().x, window.get_size().y, resources::pixel_format::rgb8) ); - std::unique_ptr id_texture = std::unique_ptr( new opengl::GlTexture(window.get_size().x, window.get_size().y, resources::pixel_format::r16ui) ); + std::unique_ptr color_texture = std::unique_ptr( new opengl::GlTexture(window.get_size().x, window.get_size().y, resources::pixel_format::rgba8) ); + std::unique_ptr id_texture = std::unique_ptr( new opengl::GlTexture(window.get_size().x, window.get_size().y, resources::pixel_format::rgba8) ); opengl::GlTexture* color_texture_ptr = color_texture.get(); opengl::GlTexture* id_texture_ptr = id_texture.get(); std::vector fbo_textures; @@ -294,8 +294,8 @@ void main() { // handle in renderer.. glViewport(0, 0, new_size.x, new_size.y); //resize fbo - color_texture = std::unique_ptr( new opengl::GlTexture(new_size.x, new_size.y, resources::pixel_format::rgb8) ); - id_texture = std::unique_ptr( new opengl::GlTexture(new_size.x, new_size.y, resources::pixel_format::r16ui) ); + color_texture = std::unique_ptr( new opengl::GlTexture(new_size.x, new_size.y, resources::pixel_format::rgba8) ); + id_texture = std::unique_ptr( new opengl::GlTexture(new_size.x, new_size.y, resources::pixel_format::rgba8) ); std::vector fbo_textures_new; fbo_textures_new.push_back(color_texture.get()); fbo_textures_new.push_back(id_texture.get()); @@ -313,8 +313,9 @@ void main() { } auto texture_dimensions = id_texture->get_info().get_size(); auto position = (texture_dimensions.second-y)*texture_dimensions.first + x; - position *= 2; //2 byte per pixel - auto id = *reinterpret_cast(id_texture_data.get_data()+position); + position *= 4; //4 byte per pixel + auto id_vector_ptr = reinterpret_cast(id_texture_data.get_data()+position); + uint16_t id = *id_vector_ptr << 8 | *(++id_vector_ptr); log::log(INFO << "Id-texture-value at location: " << id); if (id == 0) { //no renderable at given location From 0cd4208cb6ed3d0a20a07247cc9e344d7cdf2623 Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Fri, 21 Apr 2017 21:03:41 +0200 Subject: [PATCH 35/52] renderer: add row alignment support for textures; add support for reading pixel data; refactor clicking demo --- libopenage/renderer/CMakeLists.txt | 1 - libopenage/renderer/opengl/render_target.cpp | 2 +- libopenage/renderer/opengl/renderer.cpp | 5 +- libopenage/renderer/opengl/renderer.h | 1 + libopenage/renderer/opengl/texture.cpp | 73 +---- libopenage/renderer/opengl/texture.h | 10 +- libopenage/renderer/renderable.cpp | 71 ---- libopenage/renderer/renderable.h | 64 ---- libopenage/renderer/renderer.h | 28 +- .../renderer/resources/texture_data.cpp | 7 +- libopenage/renderer/resources/texture_data.h | 17 + .../renderer/resources/texture_info.cpp | 47 ++- libopenage/renderer/resources/texture_info.h | 42 ++- libopenage/renderer/shader_program.h | 10 +- libopenage/renderer/tests.cpp | 303 ++++-------------- 15 files changed, 206 insertions(+), 475 deletions(-) delete mode 100644 libopenage/renderer/renderable.cpp delete mode 100644 libopenage/renderer/renderable.h diff --git a/libopenage/renderer/CMakeLists.txt b/libopenage/renderer/CMakeLists.txt index 57a022d269..be87cdd7a6 100644 --- a/libopenage/renderer/CMakeLists.txt +++ b/libopenage/renderer/CMakeLists.txt @@ -4,7 +4,6 @@ add_sources(libopenage window.cpp text.cpp color.cpp - renderable.cpp ) pxdgen( diff --git a/libopenage/renderer/opengl/render_target.cpp b/libopenage/renderer/opengl/render_target.cpp index c60449720a..b9e297a95a 100644 --- a/libopenage/renderer/opengl/render_target.cpp +++ b/libopenage/renderer/opengl/render_target.cpp @@ -16,7 +16,7 @@ GlRenderTarget::GlRenderTarget() GlRenderTarget::GlRenderTarget(std::vector textures) { glGenFramebuffers(1, &*this->handle); glBindFramebuffer(GL_FRAMEBUFFER, *this->handle); - + std::vector drawBuffers; for (size_t i = 0; i < textures.size(); i++) { diff --git a/libopenage/renderer/opengl/renderer.cpp b/libopenage/renderer/opengl/renderer.cpp index 0d12109c55..91845df60a 100644 --- a/libopenage/renderer/opengl/renderer.cpp +++ b/libopenage/renderer/opengl/renderer.cpp @@ -4,7 +4,6 @@ #include "../../log/log.h" #include "../../error/error.h" -#include "../renderable.h" #include "texture.h" #include "shader_program.h" #include "uniform_input.h" @@ -25,6 +24,10 @@ std::unique_ptr GlRenderer::add_texture(const resources::TextureData& d return std::make_unique(data); } +std::unique_ptr GlRenderer::add_texture(const resources::TextureInfo& info) { + return std::make_unique(info); +} + std::unique_ptr GlRenderer::add_shader(std::vector const& srcs) { return std::make_unique(srcs, this->gl_context->get_capabilities()); } diff --git a/libopenage/renderer/opengl/renderer.h b/libopenage/renderer/opengl/renderer.h index a569ee3893..26c3b0f076 100644 --- a/libopenage/renderer/opengl/renderer.h +++ b/libopenage/renderer/opengl/renderer.h @@ -21,6 +21,7 @@ class GlRenderer : public Renderer { GlRenderer(GlContext*); std::unique_ptr add_texture(resources::TextureData const&) override; + std::unique_ptr add_texture(resources::TextureInfo const&) override; std::unique_ptr add_shader(std::vector const&) override; diff --git a/libopenage/renderer/opengl/texture.cpp b/libopenage/renderer/opengl/texture.cpp index f09bd10f7a..9bb0801177 100644 --- a/libopenage/renderer/opengl/texture.cpp +++ b/libopenage/renderer/opengl/texture.cpp @@ -30,24 +30,6 @@ inline static std::tuple gl_format(resources::pixel_forma } } -static GLint alignment_requirement(resources::pixel_format fmt) { - switch (fmt) { - case resources::pixel_format::r16ui: - return 2; - case resources::pixel_format::rgb8: - return 1; - case resources::pixel_format::rgba8: - return 4; - case resources::pixel_format::rgba8ui: - return 4; - default: - throw Error(MSG(err) << "invalid texture format passed to OpenGL."); - } -} - -GLint GlTexture::PACK_ALIGNMENT = 4; -GLint GlTexture::UNPACK_ALIGNMENT = 4; - GlTexture::GlTexture(const resources::TextureData& data) : Texture(data.get_info()) { @@ -60,8 +42,8 @@ GlTexture::GlTexture(const resources::TextureData& data) // store raw pixels to gpu auto size = this->info.get_size(); - - adjust_unpack_alignment(alignment_requirement(this->info.get_format())); + + glPixelStorei(GL_UNPACK_ALIGNMENT, this->info.get_row_alignment()); glTexImage2D( GL_TEXTURE_2D, 0, @@ -75,21 +57,18 @@ GlTexture::GlTexture(const resources::TextureData& data) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } -GlTexture::GlTexture(size_t width, size_t height, resources::pixel_format fmt) - : Texture(resources::TextureInfo(width, - height, - fmt, - std::vector())) -{ +GlTexture::GlTexture(const resources::TextureInfo &info) + : Texture(info) { // generate opengl texture handle glGenTextures(1, &*this->handle); glBindTexture(GL_TEXTURE_2D, *this->handle); - auto fmt_in_out = gl_format(fmt); + auto fmt_in_out = gl_format(this->info.get_format()); + auto dims = this->info.get_size(); glTexImage2D( GL_TEXTURE_2D, 0, - std::get<0>(fmt_in_out), width, height, 0, + std::get<0>(fmt_in_out), dims.first, dims.second, 0, std::get<1>(fmt_in_out), std::get<2>(fmt_in_out), nullptr ); @@ -128,49 +107,15 @@ GLuint GlTexture::get_handle() const { return *this->handle; } -inline static size_t pixel_size(resources::pixel_format fmt) { - switch (fmt) { - case resources::pixel_format::r16ui: - return 2; - case resources::pixel_format::rgb8: - return 3; - case resources::pixel_format::rgba8: - return 4; - case resources::pixel_format::rgba8ui: - return 4; - default: - throw Error(MSG(err) << "Tried to find size of unknown pixel format."); - } -} - resources::TextureData GlTexture::into_data() { - auto size = this->info.get_size(); auto fmt_in_out = gl_format(this->info.get_format()); - std::vector data(size.first * size.second * pixel_size(this->info.get_format())); + std::vector data(this->info.get_data_size()); - adjust_pack_alignment(alignment_requirement(this->info.get_format())); + glPixelStorei(GL_PACK_ALIGNMENT, this->info.get_row_alignment()); glBindTexture(GL_TEXTURE_2D, *this->handle); glGetTexImage(GL_TEXTURE_2D, 0, std::get<1>(fmt_in_out), std::get<2>(fmt_in_out), data.data()); return resources::TextureData(resources::TextureInfo(this->info), std::move(data)); } -void GlTexture::adjust_unpack_alignment(GLint alignment) { - if (alignment == this->UNPACK_ALIGNMENT) { - return; - } - - glPixelStorei(GL_UNPACK_ALIGNMENT, alignment); - this->UNPACK_ALIGNMENT = alignment; -} - -void GlTexture::adjust_pack_alignment(GLint alignment) { - if (alignment == this->PACK_ALIGNMENT) { - return; - } - - glPixelStorei(GL_PACK_ALIGNMENT, alignment); - this->PACK_ALIGNMENT = alignment; -} - }}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/texture.h b/libopenage/renderer/opengl/texture.h index b5737d1794..0b4d9a4a05 100644 --- a/libopenage/renderer/opengl/texture.h +++ b/libopenage/renderer/opengl/texture.h @@ -26,7 +26,7 @@ class GlTexture : public Texture { explicit GlTexture(const resources::TextureData&); /// Constructs an empty texture with the given parameters. - GlTexture(size_t width, size_t height, resources::pixel_format); + GlTexture(resources::TextureInfo const&); ~GlTexture(); // TODO support copying in the future @@ -45,14 +45,6 @@ class GlTexture : public Texture { private: /// The OpenGL handle to this texture. std::experimental::optional handle; - - /// The current pack alignment. See glPixelStorei - static GLint PACK_ALIGNMENT; - /// The current unpack alignment. See glPixelStorei - static GLint UNPACK_ALIGNMENT; - - void adjust_unpack_alignment(const GLint alignment); - void adjust_pack_alignment(const GLint alignment); }; }}} // openage::renderer::opengl diff --git a/libopenage/renderer/renderable.cpp b/libopenage/renderer/renderable.cpp deleted file mode 100644 index 80d596c1dd..0000000000 --- a/libopenage/renderer/renderable.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2015-2017 the openage authors. See copying.md for legal info. - -#include "renderable.h" - -namespace openage { -namespace renderer { - -std::vector Renderable::RENDERABLES; -std::vector Renderable::FREE_IDS; -std::mutex Renderable::ID_MUTEX; - -Renderable::Renderable(UniformInput const *uniform_input, Geometry const* geometry, const bool alpha_blending, const bool depth_test, - const bool writes_id, const function_t &on_click, const function_t &on_area_select) - : id{this->free_id()}, unif_in{uniform_input}, geometry{geometry}, alpha_blending{alpha_blending}, depth_test{depth_test}, - writes_id{writes_id}, on_click_callback{on_click}, on_area_selected_callback{on_area_select} -{ - std::lock_guard lock(ID_MUTEX); - RENDERABLES[this->id] = this; -} - -Renderable::Renderable(const Renderable& renderable) - : Renderable{ - renderable.unif_in, renderable.geometry, renderable.alpha_blending, renderable.depth_test, renderable.writes_id, - renderable.on_click_callback, renderable.on_area_selected_callback - } -{ - -} - -Renderable& Renderable::operator=(const Renderable& renderable) -{ - this->unif_in = renderable.unif_in; - this->geometry = renderable.geometry; - this->alpha_blending = renderable.alpha_blending; - this->depth_test = renderable.depth_test; - this->writes_id = renderable.writes_id; - this->on_click_callback = renderable.on_click_callback; - this->on_area_selected_callback = renderable.on_area_selected_callback; - - return *this; -} - -Renderable::~Renderable() -{ - std::lock_guard lock(ID_MUTEX); - RENDERABLES[this->id] = nullptr; - FREE_IDS.push_back(this->id); -} - -Renderable::id_t Renderable::free_id() -{ - std::lock_guard lock(ID_MUTEX); - if (FREE_IDS.size() > 0) { - auto free_id = FREE_IDS.back(); - FREE_IDS.pop_back(); - return free_id; - } - - // create new id by incrementing current highest id, and also allocationg the place for - // the new renderable with this id in the RENDERABLES vector - auto new_id = static_cast(RENDERABLES.size()); - RENDERABLES.push_back(nullptr); - return new_id; -} - -Renderable* Renderable::get(const id_t id) -{ - return RENDERABLES[id]; -} - -}} diff --git a/libopenage/renderer/renderable.h b/libopenage/renderer/renderable.h deleted file mode 100644 index 4d30ea1f13..0000000000 --- a/libopenage/renderer/renderable.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2015-2017 the openage authors. See copying.md for legal info. - -#pragma once - -#include -#include -#include -#include - -namespace openage { -namespace renderer { - -class UniformInput; -class Geometry; - -/// A renderable is a set of shader uniform inputs and a possible draw call. -/// When a renderable is parsed, the uniform inputs are first passed to the shader they were created with. -/// Then, if the geometry is not nullptr, the shader is executed with the geometry and the specified settings -/// and the results are written into the render target. If the geometry is nullptr, the uniform values are -/// uploaded to the shader but no draw call is performed. This can be used to, for example, first set the values -/// of uniforms that many objects have in common, and then only upload the uniforms that vary between them in -/// each draw call. This works because uniform values in any given shader are preserved across a render pass. -class Renderable { -public: - using function_t = std::function; - using id_t = uint16_t; - - Renderable(UniformInput const *uniform_input = nullptr, Geometry const* geometry = nullptr, const bool alpha_blending = true, const bool depth_test = true, - const bool writes_id = true, const function_t &on_click = [](){}, const function_t &on_area_select = [](){}); - Renderable(const Renderable& renderable); - ~Renderable(); - - Renderable& operator=(const Renderable& renderable); - Renderable& operator=(const Renderable&& renderable) = delete; - - static Renderable* get(const id_t id); - - /// Uniform values to be set in the appropriate shader. - UniformInput const *unif_in; - /// The geometry. It can be a simple primitive or a complex mesh. - /// Can be nullptr to only set uniforms but do not perform draw call. - Geometry const *geometry; - /// Whether to perform alpha-based color blending with whatever was in the render target before. - bool alpha_blending; - /// Whether to perform depth testing and discard occluded fragments. - bool depth_test; - - const id_t id; - - bool writes_id; - - function_t on_click_callback; - function_t on_area_selected_callback; - -private: - static id_t free_id(); - - static std::vector RENDERABLES; - static std::vector FREE_IDS; - - static std::mutex ID_MUTEX; -}; - -}} diff --git a/libopenage/renderer/renderer.h b/libopenage/renderer/renderer.h index 82d046f857..dee43a6bd6 100644 --- a/libopenage/renderer/renderer.h +++ b/libopenage/renderer/renderer.h @@ -10,14 +10,14 @@ namespace openage { namespace renderer { namespace resources { - class TextureData; - class ShaderSource; +class TextureData; +class TextureInfo; +class ShaderSource; } class ShaderProgram; class Geometry; class Texture; -class Renderable; /// The abstract base for uniform input. Not terribly interesting. class UniformInput { @@ -37,6 +37,25 @@ class RenderTarget { virtual ~RenderTarget() = default; }; +/// A renderable is a set of shader uniform inputs and a possible draw call. +/// When a renderable is parsed, the uniform inputs are first passed to the shader they were created with. +/// Then, if the geometry is not nullptr, the shader is executed with the geometry and the specified settings +/// and the results are written into the render target. If the geometry is nullptr, the uniform values are +/// uploaded to the shader but no draw call is performed. This can be used to, for example, first set the values +/// of uniforms that many objects have in common, and then only upload the uniforms that vary between them in +/// each draw call. This works because uniform values in any given shader are preserved across a render pass. +struct Renderable { + /// Uniform values to be set in the appropriate shader. + UniformInput const *unif_in; + /// The geometry. It can be a simple primitive or a complex mesh. + /// Can be nullptr to only set uniforms but do not perform draw call. + Geometry const *geometry; + /// Whether to perform alpha-based color blending with whatever was in the render target before. + bool alpha_blending; + /// Whether to perform depth testing and discard occluded fragments. + bool depth_test; +}; + /// A render pass is a series of draw calls represented by renderables that output into the given render target. struct RenderPass { /// The renderables to parse and possibly execute. @@ -55,6 +74,9 @@ class Renderer { /// as a Texture object that can be used for various operations. virtual std::unique_ptr add_texture(resources::TextureData const&) = 0; + /// Creates a new empty texture with the given parameters on the graphics hardware. + virtual std::unique_ptr add_texture(resources::TextureInfo const&) = 0; + /// Compiles the given shader source code into a shader program. A shader program is the main tool used /// for graphics rendering. virtual std::unique_ptr add_shader(std::vector const&) = 0; diff --git a/libopenage/renderer/resources/texture_data.cpp b/libopenage/renderer/resources/texture_data.cpp index 4b7e096822..8121e927e9 100644 --- a/libopenage/renderer/resources/texture_data.cpp +++ b/libopenage/renderer/resources/texture_data.cpp @@ -14,7 +14,6 @@ namespace openage { namespace renderer { namespace resources { - TextureData::TextureData(const util::Path &path, bool use_metafile) { std::string native_path = path.resolve_native_path(); SDL_Surface *surface = IMG_Load(native_path.c_str()); @@ -72,7 +71,7 @@ TextureData::TextureData(const util::Path &path, bool use_metafile) { subtextures.push_back(s); } - this->info = TextureInfo(w, h, format, std::move(subtextures)); + this->info = TextureInfo(w, h, format, 1, std::move(subtextures)); } TextureData::TextureData(TextureInfo &&info, std::vector &&data) @@ -90,6 +89,8 @@ const uint8_t *TextureData::get_data() const { void TextureData::store(const util::Path& file) const { log::log(MSG(info) << "Saving texture data to " << file); + // TODO support row_alignment in copying below + throw "unimplemented"; if (this->info.get_format() != pixel_format::rgba8) { throw "unimplemented"; } @@ -133,5 +134,3 @@ void TextureData::store(const util::Path& file) const { } }}} - - diff --git a/libopenage/renderer/resources/texture_data.h b/libopenage/renderer/resources/texture_data.h index d00421346b..07f8177be9 100644 --- a/libopenage/renderer/resources/texture_data.h +++ b/libopenage/renderer/resources/texture_data.h @@ -36,6 +36,23 @@ class TextureData { /// Returns a pointer to the raw texture data. const uint8_t *get_data() const; + /// Reads the pixel at the given position and casts it to the given type. + /// The texture is _not_ read as if it consisted of pixels of the given type, + /// but rather according to its original pixel format. + template + T read_pixel(size_t x, size_t y) const { + const uint8_t *data = this->data.data(); + auto dims = this->info.get_size(); + size_t off = (dims.second - y) * this->info.get_row_size(); + off += x * pixel_size(this->info.get_format()); + + if ((off + sizeof(T)) > this->info.get_data_size()) { + throw Error(MSG(err) << "Pixel position (" << x << ", " << y << ") is outside texture."); + } + + return *reinterpret_cast(data + off); + } + /// Stores this texture data in the given file in the PNG format. void store(const util::Path& file) const; diff --git a/libopenage/renderer/resources/texture_info.cpp b/libopenage/renderer/resources/texture_info.cpp index db993d754f..257ef96b4b 100644 --- a/libopenage/renderer/resources/texture_info.cpp +++ b/libopenage/renderer/resources/texture_info.cpp @@ -7,18 +7,49 @@ namespace openage { namespace renderer { namespace resources { -TextureInfo::TextureInfo(size_t width, size_t height, pixel_format fmt, std::vector &&subs) +size_t pixel_size(pixel_format fmt) { + switch (fmt) { + case pixel_format::r16ui: + return 2; + case pixel_format::rgb8: + return 3; + case pixel_format::depth24: + return 3; + case pixel_format::rgba8: + return 4; + case pixel_format::rgba8ui: + return 4; + } +} + +TextureInfo::TextureInfo(size_t width, size_t height, pixel_format fmt, size_t row_alignment, std::vector &&subs) : w(width) , h(height) , format(fmt) + , row_alignment(row_alignment) , subtextures(std::move(subs)) {} +std::pair TextureInfo::get_size() const { + return std::make_pair(this->w, this->h); +} + pixel_format TextureInfo::get_format() const { return this->format; } -std::pair TextureInfo::get_size() const { - return std::make_pair(this->w, this->h); +size_t TextureInfo::get_row_alignment() const { + return this->row_alignment; +} + +size_t TextureInfo::get_row_size() const { + size_t px_size = pixel_size(this->format); + size_t row_size = this->w * px_size; + row_size += row_size % this->row_alignment; // there might be padding at row ends to match the alignment + return row_size; +} + +size_t TextureInfo::get_data_size() const { + return this->get_row_size() * this->h; } size_t TextureInfo::get_subtexture_count() const { @@ -34,6 +65,11 @@ const gamedata::subtexture& TextureInfo::get_subtexture(size_t subid) const { } } +std::pair TextureInfo::get_subtexture_size(size_t subid) const { + auto subtex = this->get_subtexture(subid); + return std::make_pair(subtex.w, subtex.h); +} + std::tuple TextureInfo::get_subtexture_coordinates(size_t subid) const { auto tx = this->get_subtexture(subid); return std::make_tuple( @@ -44,9 +80,4 @@ std::tuple TextureInfo::get_subtexture_coordinates(s ); } -std::pair TextureInfo::get_subtexture_size(size_t subid) const { - auto subtex = this->get_subtexture(subid); - return std::make_pair(subtex.w, subtex.h); -} - }}} diff --git a/libopenage/renderer/resources/texture_info.h b/libopenage/renderer/resources/texture_info.h index 49c4ffb42e..77f424cf36 100644 --- a/libopenage/renderer/resources/texture_info.h +++ b/libopenage/renderer/resources/texture_info.h @@ -14,17 +14,21 @@ namespace resources { /// How the pixels are represented in a texture. enum class pixel_format { - r16ui, //16 bits per pixel, stored as unsigned int - /// 24 bits per pixel, no alpha channel + /// 16 bits per pixel, unsigned integer + r16ui, + /// 24 bits per pixel, float rgb8, - /// 32 bits per pixel - rgba8, - /// 32 bits per pixel stored as unsigned integer - rgba8ui, /// 24 bits per pixel, depth texture depth24, + /// 32 bits per pixel, float, alpha channel + rgba8, + /// 32 bits per pixel, unsigned integer + rgba8ui, }; +/// Returns the size in bytes of a single pixel of the specified format. +size_t pixel_size(pixel_format); + /// Information for texture processing. /// The class supports subtextures, so that one big texture ("texture atlas") /// can contain several smaller images. These are the ones actually to be @@ -32,18 +36,29 @@ enum class pixel_format { class TextureInfo { public: /// Constructs a TextureInfo with the given information. - TextureInfo(size_t width, size_t height, pixel_format, std::vector&&); + TextureInfo(size_t width, size_t height, pixel_format, size_t row_alignment = 1, std::vector&& = std::vector()); TextureInfo() = default; ~TextureInfo() = default; - /// @returns the format of pixels in this texture - pixel_format get_format() const; - /// Returns the dimensions of the whole texture bitmap /// @returns tuple(width, height) std::pair get_size() const; + /// @returns the format of pixels in this texture + pixel_format get_format() const; + + /// Returns the alignment of texture rows to byte boundaries. + size_t get_row_alignment() const; + + /// Returns the size in bytes of a single row, + /// including possible padding at its end. + size_t get_row_size() const; + + /// Returns the size in bytes of the raw pixel data. It is equal to + /// get_row_size() * get_size().second. + size_t get_data_size() const; + /// Returns the number of available subtextures size_t get_subtexture_count() const; @@ -60,13 +75,18 @@ class TextureInfo { /// range 0.0 to 1.0, relative to the whole surface size. std::tuple get_subtexture_coordinates(size_t subid) const; -protected: +private: /// Width and height of this texture. int32_t w, h; /// The pixel format of this texture. pixel_format format; + /// The alignment of texture rows to byte boundaries. Can be 1, 2, 4 or 8. + /// There is padding at the end of each row to match the alignment if the + /// row size is not a multiple of the alignment. + size_t row_alignment; + /// Some textures are merged together into texture atlases, large images which contain /// more than one individual texture. These are their positions in the atlas. std::vector subtextures; diff --git a/libopenage/renderer/shader_program.h b/libopenage/renderer/shader_program.h index ed68ca815d..4801bc7e4f 100644 --- a/libopenage/renderer/shader_program.h +++ b/libopenage/renderer/shader_program.h @@ -45,17 +45,19 @@ class ShaderProgram { void update_uniform_input(UniformInput *input, const char *unif, Eigen::Vector4f const &val) { this->set_v4f32(input, unif, val); } - + void update_uniform_input(UniformInput *input, const char *unif, Texture const *val) { this->set_tex(input, unif, val); } + void update_uniform_input(UniformInput *input, const char *unif, Texture *val) { + this->set_tex(input, unif, val); + } template - void update_uniform_input(UniformInput*, const char*, T) { - throw Error(MSG(err) << "Unknown type.."); + void update_uniform_input(UniformInput*, const char *unif, T) { + throw Error(MSG(err) << "Tried to set uniform " << unif << " using unknown type."); } -public: /// Returns whether the shader program contains a uniform variable with the given name. virtual bool has_uniform(const char *unif) = 0; diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index eddbaadb6a..8ebe227b5f 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -1,4 +1,4 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. +// Copyright 2015-2017 the openage authors. See copying.md for legal info. #include #include @@ -12,7 +12,6 @@ #include "resources/shader_source.h" #include "opengl/renderer.h" #include "window.h" -#include "renderable.h" namespace openage { @@ -20,63 +19,47 @@ namespace renderer { namespace tests { /** -* render demo function collection. -*/ + * render demo function collection. + */ struct render_demo { -std::function setup; -std::function frame; -std::function resize; -std::function click; + std::function setup; + std::function frame; + std::function resize; + std::function click; }; void render_test(Window &window, const render_demo *actions) { -SDL_Event event; + SDL_Event event; -actions->setup(&window); + actions->setup(&window); -bool running = true; -while (running) { - while (SDL_PollEvent(&event)) { - switch (event.type) { - case SDL_WINDOWEVENT: - switch (event.window.event) { - case SDL_WINDOWEVENT_RESIZED: { + bool running = true; + while (running) { + while (SDL_PollEvent(&event)) { + if (event.type == SDL_WINDOWEVENT && event.window.event == 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 ); - //window.set_size(new_size); actions->resize(new_size); - break; - }} - break; - - case SDL_QUIT: - running = false; - break; - - case SDL_KEYUP: { - SDL_Keycode sym = reinterpret_cast(&event)->keysym.sym; - switch (sym) { - case SDLK_ESCAPE: + } + else if (event.type == SDL_QUIT) { running = false; - break; - default: - break; } - break; + else if (event.type == SDL_KEYUP) { + SDL_Keycode sym = reinterpret_cast(&event)->keysym.sym; + if (sym == SDLK_ESCAPE) { + running = false; + } + } + else if (event.type == SDL_MOUSEBUTTONDOWN) { + actions->click(event.button.x, event.button.y); + } } - case SDL_MOUSEBUTTONDOWN: { - actions->click(event.button.x, event.button.y); - break; - }} - } - - actions->frame(); - //window.swap(); -} + actions->frame(); + } } void renderer_demo_0() { @@ -102,23 +85,6 @@ void main() { gl_Position.x += (bounds[0] + bounds[2]) / 2.0; gl_Position.y += (bounds[1] + bounds[3]) / 2.0; } -)s"); - - auto vshader_display_src = resources::ShaderSource( - resources::shader_source_t::glsl_vertex, - R"s( -#version 330 - -out vec2 v_uv; - -void main() { - gl_Position.x = 2.0 * float(gl_VertexID & 1) - 1.0; - gl_Position.y = 2.0 * float((gl_VertexID & 2) >> 1) - 1.0; - gl_Position.z = 1.0; - gl_Position.w = 1.0; - - v_uv = gl_Position.xy * 0.5 + 0.5; -} )s"); auto fshader_src = resources::ShaderSource( @@ -143,8 +109,26 @@ void main() { id = vec4(id_vector, 0.0); } } -)s"); - +)s"); + + auto vshader_display_src = resources::ShaderSource( + resources::shader_source_t::glsl_vertex, + R"s( +#version 330 + +out vec2 v_uv; + +void main() { + gl_Position.x = 2.0 * float(gl_VertexID & 1) - 1.0; + gl_Position.y = 2.0 * float((gl_VertexID & 2) >> 1) - 1.0; + gl_Position.z = 1.0; + gl_Position.w = 1.0; + + v_uv = gl_Position.xy * 0.5 + 0.5; +} +)s"); + + auto fshader_display_src = resources::ShaderSource( resources::shader_source_t::glsl_fragment, R"s( @@ -159,26 +143,23 @@ void main() { } )s"); - - std::vector srcs = {vshader_src, fshader_src}; - std::vector srcs_display = {vshader_display_src, fshader_display_src}; - auto shader = renderer->add_shader(srcs); - log::log(INFO << "Before dispaly shader"); - auto shader_display = renderer->add_shader(srcs_display); + auto shader = renderer->add_shader( { vshader_src, fshader_src } ); + auto shader_display = renderer->add_shader( { vshader_display_src, fshader_display_src } ); auto unif_in1 = shader->new_uniform_input( "bounds", Eigen::Vector4f(-1.0f, -1.0f, -0.8f, -0.8f), "color", Eigen::Vector4f(1.0f, 0.0f, 0.0f, 1.0f), "u_id", 1u, - "writes_id", 1u - + "writes_id", 1u ); + auto unif_in2 = shader->new_uniform_input( "bounds", Eigen::Vector4f(0.0f, 0.3f, 0.3f, 0.5f), "color", Eigen::Vector4f(0.0f, 1.0f, 0.0f, 1.0f), "u_id", 2u, "writes_id", 1u ); + auto unif_in3 = shader->new_uniform_input( "bounds", Eigen::Vector4f(0.5f, -0.7f, 0.6f, -0.3f), "color", Eigen::Vector4f(0.0f, 0.0f, 1.0f, 0.5f), @@ -194,11 +175,6 @@ void main() { true, }; - obj1.on_click_callback = [](){ - log::log(INFO << "Object 1 clicked"); - }; - shader->update_uniform_input(unif_in1.get(), "u_id", static_cast(obj1.id)); - Renderable obj2{ unif_in2.get(), &quad, @@ -206,35 +182,19 @@ void main() { true, }; - obj2.on_click_callback = [](){ - log::log(INFO << "Object 2 clicked"); - }; - shader->update_uniform_input(unif_in2.get(), "u_id", static_cast(obj2.id)); - Renderable obj3 { unif_in3.get(), &quad, true, true, - false - }; - - obj3.on_click_callback = [](){ - log::log(INFO << "Object 3 clicked"); }; - shader->update_uniform_input(unif_in3.get(), "u_id", static_cast(obj3.id), "writes_id", obj3.writes_id ? 1u : 0u); - std::unique_ptr color_texture = std::unique_ptr( new opengl::GlTexture(window.get_size().x, window.get_size().y, resources::pixel_format::rgba8) ); - std::unique_ptr id_texture = std::unique_ptr( new opengl::GlTexture(window.get_size().x, window.get_size().y, resources::pixel_format::rgba8) ); - opengl::GlTexture* color_texture_ptr = color_texture.get(); - opengl::GlTexture* id_texture_ptr = id_texture.get(); - std::vector fbo_textures; - fbo_textures.push_back(color_texture_ptr); - fbo_textures.push_back(id_texture_ptr); + auto size = window.get_size(); + auto color_texture = renderer->add_texture(resources::TextureInfo(size.x, size.y, resources::pixel_format::rgba8)); + auto id_texture = renderer->add_texture(resources::TextureInfo(size.x, size.y, resources::pixel_format::rgba8)); + auto fbo = renderer->create_texture_target( { color_texture.get(), id_texture.get() } ); - auto fbo = std::unique_ptr(new opengl::GlRenderTarget(fbo_textures)); - - auto color_texture_uniform = shader_display->new_uniform_input("color_texture", static_cast(color_texture.get())); + auto color_texture_uniform = shader_display->new_uniform_input("color_texture", color_texture.get()); Renderable display_obj { color_texture_uniform.get(), &quad, @@ -248,7 +208,7 @@ void main() { }; RenderPass display_pass { - { display_obj } , + { display_obj }, renderer->get_display_target(), }; @@ -263,10 +223,7 @@ void main() { // what is this glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - //GLuint vpos_buf; - //glGenBuffers(1, &vpos_buf); - //glBindBuffer(GL_ARRAY_BUFFER, vpos_buf); - + // TODO put in renderer GLuint vao; glGenVertexArrays(1, &vao); glBindVertexArray(vao); // stores all the vertex attrib state.*/ @@ -278,30 +235,18 @@ void main() { renderer->render(display_pass); window.swap(); window.get_context()->check_error(); - /*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); - - renderer.check_error();*/ }, // resize [&](const coord::window &new_size) { // handle in renderer.. glViewport(0, 0, new_size.x, new_size.y); - //resize fbo - color_texture = std::unique_ptr( new opengl::GlTexture(new_size.x, new_size.y, resources::pixel_format::rgba8) ); - id_texture = std::unique_ptr( new opengl::GlTexture(new_size.x, new_size.y, resources::pixel_format::rgba8) ); - std::vector fbo_textures_new; - fbo_textures_new.push_back(color_texture.get()); - fbo_textures_new.push_back(id_texture.get()); - - fbo = std::unique_ptr(new opengl::GlRenderTarget(fbo_textures_new)); - shader_display->update_uniform_input(color_texture_uniform.get(), "color_texture", static_cast(color_texture.get())); + + // resize fbo + color_texture = renderer->add_texture(resources::TextureInfo(new_size.x, new_size.y, resources::pixel_format::rgba8)); + id_texture = renderer->add_texture(resources::TextureInfo(new_size.x, new_size.y, resources::pixel_format::rgba8)); + fbo = renderer->create_texture_target( { color_texture.get(), id_texture.get() } ); + + shader_display->update_uniform_input(color_texture_uniform.get(), "color_texture", color_texture.get()); pass.target = fbo.get(); }, //click @@ -311,11 +256,9 @@ void main() { id_texture_data = id_texture->into_data(); texture_data_valid = true; } - auto texture_dimensions = id_texture->get_info().get_size(); - auto position = (texture_dimensions.second-y)*texture_dimensions.first + x; - position *= 4; //4 byte per pixel - auto id_vector_ptr = reinterpret_cast(id_texture_data.get_data()+position); - uint16_t id = *id_vector_ptr << 8 | *(++id_vector_ptr); + auto id = id_texture_data.read_pixel(x, y); + id &= 0x0000ff00; + id >>= 8; log::log(INFO << "Id-texture-value at location: " << id); if (id == 0) { //no renderable at given location @@ -323,121 +266,13 @@ void main() { return; } id--; //real id is id-1 - Renderable::get(id)->on_click_callback(); + log::log(INFO << "Object number " << id << " clicked."); } }; render_test(window, &test0); } - - /*void renderer_demo_1() { - Window window{"openage renderer testing"}; - Renderer renderer{window.get_context()}; - // instead do - // Renderer renderer(&window); - // also do - // GuiRenderer gui(&window) using qtquick - // probably use shared_ptr for window so as to guarantee lifetimes - - 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;" - "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(); - - 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*//*) { - log::log(MSG(dbg) << "preparing test"); - - tex_pipeline.tex.set(gaben.get()); - tex_pipeline.position.set_layout(0); - tex_pipeline.texcoord.set_layout(1); - - float val = 0.9f; - tex_pipeline.set_positions({ - {-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}, - }); - - // apply the pipeline properties - tex_pipeline.upload_uniforms(); - 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); - - - // 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(); - }, - // 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: From 1085d12f1e0565827e9d2391fc75260323afa5dc Mon Sep 17 00:00:00 2001 From: Erik Griese Date: Sat, 22 Apr 2017 19:28:18 +0200 Subject: [PATCH 36/52] Extracting id from pixel value improved, added missing glPixelStorei - id extraction is now independent of endianess - using full 16 bits of written id value --- libopenage/renderer/opengl/texture.cpp | 3 +++ libopenage/renderer/tests.cpp | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/libopenage/renderer/opengl/texture.cpp b/libopenage/renderer/opengl/texture.cpp index 9bb0801177..a39382e530 100644 --- a/libopenage/renderer/opengl/texture.cpp +++ b/libopenage/renderer/opengl/texture.cpp @@ -66,6 +66,9 @@ GlTexture::GlTexture(const resources::TextureInfo &info) auto fmt_in_out = gl_format(this->info.get_format()); auto dims = this->info.get_size(); + + glPixelStorei(GL_UNPACK_ALIGNMENT, this->info.get_row_alignment()); + glTexImage2D( GL_TEXTURE_2D, 0, std::get<0>(fmt_in_out), dims.first, dims.second, 0, diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index 8ebe227b5f..05e108ec1c 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -256,9 +256,9 @@ void main() { id_texture_data = id_texture->into_data(); texture_data_valid = true; } - auto id = id_texture_data.read_pixel(x, y); - id &= 0x0000ff00; - id >>= 8; + auto pixel_value = id_texture_data.read_pixel(x, y); + uint8_t *pixel_components = reinterpret_cast(&pixel_value); + uint16_t id = (pixel_components[0] << 8) | pixel_components[1]; log::log(INFO << "Id-texture-value at location: " << id); if (id == 0) { //no renderable at given location From 71a679e9a0363d3c0658f9d5010fcd2159c12b24 Mon Sep 17 00:00:00 2001 From: Erik Griese Date: Sat, 22 Apr 2017 21:09:38 +0200 Subject: [PATCH 37/52] Simplified id drawing, using 32 bit ids now --- libopenage/renderer/opengl/renderer.cpp | 2 +- libopenage/renderer/tests.cpp | 31 +++++++++---------------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/libopenage/renderer/opengl/renderer.cpp b/libopenage/renderer/opengl/renderer.cpp index 91845df60a..adefe16913 100644 --- a/libopenage/renderer/opengl/renderer.cpp +++ b/libopenage/renderer/opengl/renderer.cpp @@ -49,7 +49,7 @@ void GlRenderer::render(RenderPass const& pass) { auto gl_target = dynamic_cast(pass.target); gl_target->bind_write(); - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); for (auto obj : pass.renderables) { diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index 05e108ec1c..4d5e2efb2e 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -94,20 +94,16 @@ void main() { uniform vec4 color; uniform uint u_id; -uniform uint writes_id; layout(location=0) out vec4 col; -layout(location=1) out vec4 id; +layout(location=1) out uint id; void main() { - col = color; - uint id_write = u_id + 1u; // 0 is for no object - vec3 id_vector = vec3(float(id_write >> 8) / 255.0, float(id_write & 255u) / 255.0, 0.0); - if (bool(writes_id) && (color.a > 0.0)) { - id = vec4(id_vector, 1.0); - } else { - id = vec4(id_vector, 0.0); + if (color.a == 0.0) { + discard; } + col = color; + id = u_id + 1u; } )s"); @@ -149,22 +145,19 @@ void main() { auto unif_in1 = shader->new_uniform_input( "bounds", Eigen::Vector4f(-1.0f, -1.0f, -0.8f, -0.8f), "color", Eigen::Vector4f(1.0f, 0.0f, 0.0f, 1.0f), - "u_id", 1u, - "writes_id", 1u + "u_id", 1u ); auto unif_in2 = shader->new_uniform_input( "bounds", Eigen::Vector4f(0.0f, 0.3f, 0.3f, 0.5f), "color", Eigen::Vector4f(0.0f, 1.0f, 0.0f, 1.0f), - "u_id", 2u, - "writes_id", 1u + "u_id", 2u ); auto unif_in3 = shader->new_uniform_input( "bounds", Eigen::Vector4f(0.5f, -0.7f, 0.6f, -0.3f), "color", Eigen::Vector4f(0.0f, 0.0f, 1.0f, 0.5f), - "u_id", 3u, - "writes_id", 1u + "u_id", 3u ); Geometry quad; @@ -191,7 +184,7 @@ void main() { auto size = window.get_size(); auto color_texture = renderer->add_texture(resources::TextureInfo(size.x, size.y, resources::pixel_format::rgba8)); - auto id_texture = renderer->add_texture(resources::TextureInfo(size.x, size.y, resources::pixel_format::rgba8)); + auto id_texture = renderer->add_texture(resources::TextureInfo(size.x, size.y, resources::pixel_format::rgba8ui)); auto fbo = renderer->create_texture_target( { color_texture.get(), id_texture.get() } ); auto color_texture_uniform = shader_display->new_uniform_input("color_texture", color_texture.get()); @@ -243,7 +236,7 @@ void main() { // resize fbo color_texture = renderer->add_texture(resources::TextureInfo(new_size.x, new_size.y, resources::pixel_format::rgba8)); - id_texture = renderer->add_texture(resources::TextureInfo(new_size.x, new_size.y, resources::pixel_format::rgba8)); + id_texture = renderer->add_texture(resources::TextureInfo(new_size.x, new_size.y, resources::pixel_format::rgba8ui)); fbo = renderer->create_texture_target( { color_texture.get(), id_texture.get() } ); shader_display->update_uniform_input(color_texture_uniform.get(), "color_texture", color_texture.get()); @@ -256,9 +249,7 @@ void main() { id_texture_data = id_texture->into_data(); texture_data_valid = true; } - auto pixel_value = id_texture_data.read_pixel(x, y); - uint8_t *pixel_components = reinterpret_cast(&pixel_value); - uint16_t id = (pixel_components[0] << 8) | pixel_components[1]; + auto id = id_texture_data.read_pixel(x, y); log::log(INFO << "Id-texture-value at location: " << id); if (id == 0) { //no renderable at given location From f74561a4e4c3b7f81983ff91052e3a5662f261d5 Mon Sep 17 00:00:00 2001 From: Erik Griese Date: Sun, 30 Apr 2017 17:28:02 +0200 Subject: [PATCH 38/52] WIP: Using VBO's and VAO's for drawing, matrix transformations tests --- libopenage/renderer/CMakeLists.txt | 4 ++ libopenage/renderer/buffer.cpp | 4 +- libopenage/renderer/buffer.h | 4 +- libopenage/renderer/geometry.cpp | 68 +++++++++++++++++++ libopenage/renderer/geometry.h | 25 ++++++- libopenage/renderer/opengl/CMakeLists.txt | 3 + libopenage/renderer/opengl/buffer.cpp | 4 +- libopenage/renderer/opengl/buffer.h | 2 +- libopenage/renderer/opengl/geometry.cpp | 34 ++++++++++ libopenage/renderer/opengl/geometry.h | 22 ++++++ libopenage/renderer/opengl/shader_program.cpp | 32 +++++++-- libopenage/renderer/opengl/shader_program.h | 2 + libopenage/renderer/opengl/vertex_state.cpp | 9 +-- libopenage/renderer/opengl/vertex_state.h | 2 +- libopenage/renderer/shader_program.h | 4 ++ libopenage/renderer/tests.cpp | 52 ++++++++------ libopenage/renderer/vertex_buffer.cpp | 11 ++- libopenage/renderer/vertex_buffer.h | 6 +- libopenage/renderer/vertex_state.cpp | 9 ++- libopenage/renderer/vertex_state.h | 4 +- 20 files changed, 252 insertions(+), 49 deletions(-) create mode 100644 libopenage/renderer/geometry.cpp create mode 100644 libopenage/renderer/opengl/geometry.cpp create mode 100644 libopenage/renderer/opengl/geometry.h diff --git a/libopenage/renderer/CMakeLists.txt b/libopenage/renderer/CMakeLists.txt index be87cdd7a6..0e09e46b0b 100644 --- a/libopenage/renderer/CMakeLists.txt +++ b/libopenage/renderer/CMakeLists.txt @@ -4,6 +4,10 @@ add_sources(libopenage window.cpp text.cpp color.cpp + geometry.cpp + vertex_buffer.cpp + buffer.cpp + vertex_state.cpp ) pxdgen( diff --git a/libopenage/renderer/buffer.cpp b/libopenage/renderer/buffer.cpp index f01047d38e..7319b9cdfa 100644 --- a/libopenage/renderer/buffer.cpp +++ b/libopenage/renderer/buffer.cpp @@ -5,9 +5,9 @@ namespace openage { namespace renderer { -Buffer::Buffer(Context *ctx, size_t size) +Buffer::Buffer(/*Context *ctx,*/ size_t size) : - context{ctx}, + //context{ctx}, allocd{size}, on_gpu{false} { diff --git a/libopenage/renderer/buffer.h b/libopenage/renderer/buffer.h index aafd4bc266..e132e6babb 100644 --- a/libopenage/renderer/buffer.h +++ b/libopenage/renderer/buffer.h @@ -35,7 +35,7 @@ class Buffer { }; std::shared_ptr wow; - Buffer(Context *ctx, size_t size=0); + Buffer(/*Context *ctx,*/ size_t size=0); virtual ~Buffer() = default; Buffer(const Buffer &other) = delete; @@ -76,7 +76,7 @@ class Buffer { /** * The associated graphics context. */ - Context *const context; + //Context *const context; protected: /** diff --git a/libopenage/renderer/geometry.cpp b/libopenage/renderer/geometry.cpp new file mode 100644 index 0000000000..1b113817e3 --- /dev/null +++ b/libopenage/renderer/geometry.cpp @@ -0,0 +1,68 @@ +// Copyright 2015-2016 the openage authors. See copying.md for legal info. + +#include "geometry.h" +#include "vertex_state.h" +#include "vertex_buffer.h" + +namespace openage { +namespace renderer { + +const std::array Geometry::quad_geometry{{ + { Eigen::Vector2f(-1.0f, 1.0f), Eigen::Vector2f(0.0f, 1.0f) }, + { Eigen::Vector2f(-1.0f, -1.0f), Eigen::Vector2f(0.0f, 0.0f) }, + { Eigen::Vector2f(1.0f, 1.0f), Eigen::Vector2f(1.0f, 1.0f) }, + { Eigen::Vector2f(1.0f, -1.0f), Eigen::Vector2f(1.0f, 0.0f) } +}}; + +Geometry::Geometry(VertexState* vaoPtr, VertexBuffer *vboPtr, const geometry_t &geometry_type) + : type(geometry_type), vertex_state(vaoPtr), vertex_buffer(vboPtr) +{ + if (this->type != geometry_t::quad) { + throw std::invalid_argument("Only Quad geometry is currently supported"); + } + this->setup_quad(); +} + +Geometry::~Geometry() +{ + +} + +void Geometry::setup_quad() +{ + std::vector data; + for (const auto& vertex : this->quad_geometry) { + data.push_back(vertex.position(0)); + data.push_back(vertex.position(1)); + data.push_back(vertex.texture_coordinates(0)); + data.push_back(vertex.texture_coordinates(1)); + } + + const size_t byte_size = data.size() * sizeof(decltype(data)::value_type); + this->vertex_buffer->alloc(byte_size); + auto buffer_data_ptr = this->vertex_buffer->get(true); + std::memcpy(buffer_data_ptr, data.data(), byte_size); + + this->vertex_buffer->upload(); + + VertexBuffer::vbo_section position_section{ + 0, + vertex_attribute_type::float_32, + 2u, + 0u, + 16u + }; + VertexBuffer::vbo_section texture_coordinate_section{ + 1, + vertex_attribute_type::float_32, + 2u, + 8u, + 16u + }; + + this->vertex_buffer->add_section(position_section); + this->vertex_buffer->add_section(texture_coordinate_section); + this->vertex_state->attach_buffer(*(this->vertex_buffer)); +} + +}} //openage::renderer diff --git a/libopenage/renderer/geometry.h b/libopenage/renderer/geometry.h index 0bc87dcb59..83901730f8 100644 --- a/libopenage/renderer/geometry.h +++ b/libopenage/renderer/geometry.h @@ -2,10 +2,16 @@ #pragma once +#include +#include +#include namespace openage { namespace renderer { +class VertexState; +class VertexBuffer; + /// The type of geometry. enum class geometry_t { quad, @@ -16,8 +22,15 @@ enum class geometry_t { // TODO this class class Geometry { public: + struct Vertex { + Eigen::Vector2f position; + Eigen::Vector2f texture_coordinates; + }; + + /// The default constructor makes a quad. - Geometry() : type(geometry_t::quad) {} + Geometry(VertexState* vaoPtr, VertexBuffer *vboPtr, const geometry_t &geometry_type); + virtual ~Geometry(); // TODO maybe use inheritance instead of enum? // Geometry(mesh..) @@ -25,8 +38,18 @@ class Geometry { /// Returns the type of this geometry. geometry_t get_type() const { return this->type; } + virtual void draw() const = 0; + +protected: + std::unique_ptr vertex_state; + std::unique_ptr vertex_buffer; + private: + static const std::array quad_geometry; + geometry_t type; + + void setup_quad(); }; }} // openage::renderer diff --git a/libopenage/renderer/opengl/CMakeLists.txt b/libopenage/renderer/opengl/CMakeLists.txt index 37e2d64ddf..05290b4b49 100644 --- a/libopenage/renderer/opengl/CMakeLists.txt +++ b/libopenage/renderer/opengl/CMakeLists.txt @@ -4,4 +4,7 @@ add_sources(libopenage renderer.cpp shader_program.cpp texture.cpp + vertex_state.cpp + buffer.cpp + geometry.cpp ) diff --git a/libopenage/renderer/opengl/buffer.cpp b/libopenage/renderer/opengl/buffer.cpp index dcaeed879d..980540d000 100644 --- a/libopenage/renderer/opengl/buffer.cpp +++ b/libopenage/renderer/opengl/buffer.cpp @@ -13,9 +13,9 @@ namespace openage { namespace renderer { namespace opengl { -Buffer::Buffer(renderer::Context *ctx, size_t size) +Buffer::Buffer(/*renderer::Context *ctx,*/ size_t size) : - renderer::Buffer{ctx, size} { + renderer::Buffer{/*ctx,*/ size} { glGenBuffers(1, &this->id); } diff --git a/libopenage/renderer/opengl/buffer.h b/libopenage/renderer/opengl/buffer.h index 1b26c2df15..472d92942f 100644 --- a/libopenage/renderer/opengl/buffer.h +++ b/libopenage/renderer/opengl/buffer.h @@ -17,7 +17,7 @@ namespace opengl { */ class Buffer : public renderer::Buffer { public: - Buffer(renderer::Context *ctx, size_t size=0); + Buffer(/*renderer::Context *ctx,*/ size_t size=0); virtual ~Buffer(); Buffer(const Buffer &other) = delete; diff --git a/libopenage/renderer/opengl/geometry.cpp b/libopenage/renderer/opengl/geometry.cpp new file mode 100644 index 0000000000..e8d3aca772 --- /dev/null +++ b/libopenage/renderer/opengl/geometry.cpp @@ -0,0 +1,34 @@ +// Copyright 2015-2016 the openage authors. See copying.md for legal info. + +#include + +#include "geometry.h" +#include "vertex_state.h" +#include "buffer.h" +#include "../vertex_buffer.h" + +namespace openage { +namespace renderer { +namespace opengl { + +GlGeometry::GlGeometry(const geometry_t &geometry_type) + : Geometry(new VertexState(), new VertexBuffer(new Buffer(), Buffer::usage::static_draw), geometry_type) +{ + +} + +GlGeometry::~GlGeometry() +{ + +} + +void GlGeometry::draw() const +{ + if (this->get_type() != geometry_t::quad) { + throw std::runtime_error("Only drawing quads is currently supported"); + } + this->vertex_state->bind(); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); +} + +}}} //openage::renderer::opengl diff --git a/libopenage/renderer/opengl/geometry.h b/libopenage/renderer/opengl/geometry.h new file mode 100644 index 0000000000..8a7850746c --- /dev/null +++ b/libopenage/renderer/opengl/geometry.h @@ -0,0 +1,22 @@ +// Copyright 2015-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include "../geometry.h" + + +namespace openage { +namespace renderer { +namespace opengl { + +/// The OpenGL class representing geometry to be passed to a draw call. +class GlGeometry : public Geometry { +public: + /// The default constructor makes a quad. + GlGeometry(const geometry_t &geometry_type); + virtual ~GlGeometry(); + + virtual void draw() const override; +}; + +}}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/shader_program.cpp b/libopenage/renderer/opengl/shader_program.cpp index f957252f12..078dc50335 100644 --- a/libopenage/renderer/opengl/shader_program.cpp +++ b/libopenage/renderer/opengl/shader_program.cpp @@ -15,6 +15,7 @@ #include "../../util/file.h" #include "../../util/strings.h" #include "texture.h" +#include "geometry.h" namespace openage { @@ -37,6 +38,8 @@ size_t uniform_size(gl_uniform_t type) { return 3 * sizeof(float); case gl_uniform_t::V4F32: return 4 * sizeof(float); + case gl_uniform_t::M4F32: + return 16 * sizeof(float); case gl_uniform_t::SAMPLER2D: return sizeof(GLint); default: @@ -61,6 +64,8 @@ static gl_uniform_t glsl_str_to_type(std::experimental::string_view str) { return gl_uniform_t::V4F32; else if (str == "sampler2D") return gl_uniform_t::SAMPLER2D; + else if (str == "mat4") + return gl_uniform_t::M4F32; else throw Error(MSG(err) << "Unsupported GLSL uniform type " << str); } @@ -220,9 +225,12 @@ GlShaderProgram::GlShaderProgram(const std::vector &src for (auto& pair : this->uniforms) { GLint loc = glGetUniformLocation(this->id, pair.first.data()); - if (unlikely(loc == -1)) { - throw Error(MSG(err) - << "Could not determine the location of OpenGL shader uniform that was found before. WTF?!"); + pair.second.location = loc; + + if (loc == -1) { + log::log(MSG(info) + << "Could not determine the location of OpenGL shader uniform that was found before. Probably optimized away."); + continue; } GLuint tex_unit = 0; @@ -236,7 +244,6 @@ GlShaderProgram::GlShaderProgram(const std::vector &src tex_unit += 1; } - pair.second.location = loc; } log::log(MSG(info) << "Created OpenGL shader program"); @@ -289,6 +296,9 @@ void GlShaderProgram::execute_with(const GlUniformInput *unif_in, const Geometry for (auto const &pair : unif_in->update_offs) { uint8_t const* ptr = data + pair.second; auto loc = this->uniforms[pair.first].location; + if(loc == -1) { + continue; + } switch (this->uniforms[pair.first].type) { case gl_uniform_t::I32: glUniform1i(loc, *reinterpret_cast(ptr)); @@ -312,6 +322,9 @@ void GlShaderProgram::execute_with(const GlUniformInput *unif_in, const Geometry case gl_uniform_t::V4F32: glUniform4fv(loc, 1, reinterpret_cast(ptr)); break; + case gl_uniform_t::M4F32: + glUniformMatrix4fv(loc, 1, false, reinterpret_cast(ptr)); + break; case gl_uniform_t::SAMPLER2D: { GLuint tex_unit = this->texunits_per_unifs[pair.first]; GLuint tex = *reinterpret_cast(ptr); @@ -327,8 +340,11 @@ void GlShaderProgram::execute_with(const GlUniformInput *unif_in, const Geometry } } - // TODO read geom and obj.blend + family - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + if (geom != nullptr) { + // TODO read geom and obj.blend + family + //glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + geom->draw(); + } } std::unique_ptr GlShaderProgram::new_unif_in() { @@ -390,6 +406,10 @@ void GlShaderProgram::set_v4f32(UniformInput *in, const char *unif, Eigen::Vecto this->set_unif(in, unif, &val); } +void GlShaderProgram::set_m4f32(UniformInput *in, const char *unif, Eigen::Matrix4f const& val) { + this->set_unif(in, unif, val.data()); +} + void GlShaderProgram::set_tex(UniformInput *in, const char *unif, Texture const* val) { auto const& tex = *static_cast(val); GLuint handle = tex.get_handle(); diff --git a/libopenage/renderer/opengl/shader_program.h b/libopenage/renderer/opengl/shader_program.h index 9aa7b378ae..69eeb2bd6c 100644 --- a/libopenage/renderer/opengl/shader_program.h +++ b/libopenage/renderer/opengl/shader_program.h @@ -26,6 +26,7 @@ enum class gl_uniform_t { V2F32, V3F32, V4F32, + M4F32, SAMPLER2D, }; @@ -73,6 +74,7 @@ class GlShaderProgram : public ShaderProgram { void set_v2f32(UniformInput*, const char*, Eigen::Vector2f const&) override; void set_v3f32(UniformInput*, const char*, Eigen::Vector3f const&) override; void set_v4f32(UniformInput*, const char*, Eigen::Vector4f const&) override; + void set_m4f32(UniformInput*, const char*, Eigen::Matrix4f const&) override; void set_tex(UniformInput*, const char*, Texture const*) override; private: diff --git a/libopenage/renderer/opengl/vertex_state.cpp b/libopenage/renderer/opengl/vertex_state.cpp index f3bb12351a..02f794d515 100644 --- a/libopenage/renderer/opengl/vertex_state.cpp +++ b/libopenage/renderer/opengl/vertex_state.cpp @@ -12,9 +12,10 @@ namespace openage { namespace renderer { namespace opengl { -VertexState::VertexState(renderer::Context *ctx) - : - renderer::VertexState{ctx} { +VertexState::VertexState(/*renderer::Context *ctx*/) + //: + //renderer::VertexState{ctx} { +{ glGenVertexArrays(1, &this->id); } @@ -39,7 +40,7 @@ void VertexState::attach_buffer(VertexBuffer &buf) { glVertexAttribPointer( section.attr_id, section.dimension, - type, GL_FALSE, 0, + type, GL_FALSE, section.stride, reinterpret_cast(section.offset) ); diff --git a/libopenage/renderer/opengl/vertex_state.h b/libopenage/renderer/opengl/vertex_state.h index db0a4fc108..f8c7261f78 100644 --- a/libopenage/renderer/opengl/vertex_state.h +++ b/libopenage/renderer/opengl/vertex_state.h @@ -18,7 +18,7 @@ namespace opengl { */ class VertexState : public renderer::VertexState { public: - VertexState(renderer::Context *ctx); + VertexState(/*renderer::Context *ctx*/); virtual ~VertexState(); /** diff --git a/libopenage/renderer/shader_program.h b/libopenage/renderer/shader_program.h index 4801bc7e4f..f458a7efae 100644 --- a/libopenage/renderer/shader_program.h +++ b/libopenage/renderer/shader_program.h @@ -53,6 +53,9 @@ class ShaderProgram { void update_uniform_input(UniformInput *input, const char *unif, Texture *val) { this->set_tex(input, unif, val); } + void update_uniform_input(UniformInput *input, const char *unif, Eigen::Matrix4f const &val) { + this->set_m4f32(input, unif, val); + } template void update_uniform_input(UniformInput*, const char *unif, T) { throw Error(MSG(err) << "Tried to set uniform " << unif << " using unknown type."); @@ -93,6 +96,7 @@ class ShaderProgram { virtual void set_v2f32(UniformInput*, const char*, Eigen::Vector2f const&) = 0; virtual void set_v3f32(UniformInput*, const char*, Eigen::Vector3f const&) = 0; virtual void set_v4f32(UniformInput*, const char*, Eigen::Vector4f const&) = 0; + virtual void set_m4f32(UniformInput*, const char*, Eigen::Matrix4f const&) = 0; virtual void set_tex(UniformInput*, const char*, Texture const*) = 0; }; diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index 4d5e2efb2e..7c6a7dcb3c 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -5,9 +5,10 @@ #include #include #include +#include #include "../log/log.h" -#include "geometry.h" +#include "opengl/geometry.h" #include "../error/error.h" #include "resources/shader_source.h" #include "opengl/renderer.h" @@ -71,19 +72,12 @@ void renderer_demo_0() { resources::shader_source_t::glsl_vertex, R"s( #version 330 -uniform vec4 bounds; +layout(location=0) in vec2 in_position; -void main() { - gl_Position.x = 2.0 * float(gl_VertexID & 1) - 1.0; - gl_Position.y = 2.0 * float((gl_VertexID & 2) >> 1) - 1.0; - gl_Position.z = 1.0; - gl_Position.w = 1.0; - - gl_Position.x *= bounds[2] - bounds[0]; - gl_Position.y *= bounds[3] - bounds[1]; +uniform mat4 mvp; - gl_Position.x += (bounds[0] + bounds[2]) / 2.0; - gl_Position.y += (bounds[1] + bounds[3]) / 2.0; +void main() { + gl_Position = mvp * vec4(in_position, 0.0, 1.0); } )s"); @@ -112,15 +106,14 @@ void main() { R"s( #version 330 +layout(location=0) in vec2 in_position; +layout(location=1) in vec2 in_texcoord; out vec2 v_uv; void main() { - gl_Position.x = 2.0 * float(gl_VertexID & 1) - 1.0; - gl_Position.y = 2.0 * float((gl_VertexID & 2) >> 1) - 1.0; - gl_Position.z = 1.0; - gl_Position.w = 1.0; + gl_Position = vec4(in_position, 0.0, 1.0); - v_uv = gl_Position.xy * 0.5 + 0.5; + v_uv = in_texcoord; } )s"); @@ -136,31 +129,48 @@ out vec4 col; void main() { col = texture(color_texture, v_uv); + //col = vec4(v_uv, 0.0, 1.0); } )s"); auto shader = renderer->add_shader( { vshader_src, fshader_src } ); auto shader_display = renderer->add_shader( { vshader_display_src, fshader_display_src } ); + auto transform1 = Eigen::Affine3f::Identity(); + transform1.prescale(Eigen::Vector3f(0.4f, 0.2f, 1.0f)); + transform1.prerotate(Eigen::AngleAxisf(30.0f * 3.14159f / 180.0f, Eigen::Vector3f::UnitX())); + transform1.pretranslate(Eigen::Vector3f(-0.4f, -0.6f, 0.0f)); + auto unif_in1 = shader->new_uniform_input( - "bounds", Eigen::Vector4f(-1.0f, -1.0f, -0.8f, -0.8f), + "mvp", transform1.matrix(), "color", Eigen::Vector4f(1.0f, 0.0f, 0.0f, 1.0f), "u_id", 1u ); + auto transform2 = Eigen::Affine3f::Identity(); + transform2.prescale(Eigen::Vector3f(0.3f, 0.1f, 1.0f)); + transform2.prerotate(Eigen::AngleAxisf(50.0f * 3.14159f / 180.0f, Eigen::Vector3f::UnitZ())); + + auto transform3 = transform2; + + transform2.pretranslate(Eigen::Vector3f(0.3f, 0.1f, 0.0f)); + auto unif_in2 = shader->new_uniform_input( - "bounds", Eigen::Vector4f(0.0f, 0.3f, 0.3f, 0.5f), + "mvp", transform2.matrix(), "color", Eigen::Vector4f(0.0f, 1.0f, 0.0f, 1.0f), "u_id", 2u ); + transform3.prerotate(Eigen::AngleAxisf(90.0f * 3.14159f / 180.0f, Eigen::Vector3f::UnitZ())); + transform3.pretranslate(Eigen::Vector3f(0.3f, 0.1f, 0.2f)); + auto unif_in3 = shader->new_uniform_input( - "bounds", Eigen::Vector4f(0.5f, -0.7f, 0.6f, -0.3f), + "mvp", transform3.matrix(), "color", Eigen::Vector4f(0.0f, 0.0f, 1.0f, 0.5f), "u_id", 3u ); - Geometry quad; + opengl::GlGeometry quad(geometry_t::quad); Renderable obj1 { unif_in1.get(), &quad, diff --git a/libopenage/renderer/vertex_buffer.cpp b/libopenage/renderer/vertex_buffer.cpp index bb57bf8268..8c2ac79965 100644 --- a/libopenage/renderer/vertex_buffer.cpp +++ b/libopenage/renderer/vertex_buffer.cpp @@ -2,17 +2,22 @@ #include "vertex_buffer.h" -#include "context.h" - namespace openage { namespace renderer { -VertexBuffer::VertexBuffer(Context *ctx, Buffer::usage usage) +/*VertexBuffer::VertexBuffer(Context *ctx, Buffer::usage usage) : usage{usage} { this->buffer = ctx->create_buffer(); } +*/ +VertexBuffer::VertexBuffer(Buffer *bufferPtr, Buffer::usage usage) + : + usage{usage} { + + this->buffer = std::unique_ptr(bufferPtr); +} VertexBuffer::VertexBuffer(VertexBuffer &&other) : diff --git a/libopenage/renderer/vertex_buffer.h b/libopenage/renderer/vertex_buffer.h index d811a543d1..4ef8e464be 100644 --- a/libopenage/renderer/vertex_buffer.h +++ b/libopenage/renderer/vertex_buffer.h @@ -58,9 +58,13 @@ class VertexBuffer { vertex_attribute_type type; //!< attribute data type size_t dimension; //!< attribute dimension size_t offset; //!< start offset in the buffer + size_t stride; //!< byte offset between consecutive attributes, 0 means tightly packed }; - VertexBuffer(Context *ctx, + //VertexBuffer(Context *ctx, + // Buffer::usage usage=Buffer::usage::static_draw); + + VertexBuffer(Buffer *bufferPtr, Buffer::usage usage=Buffer::usage::static_draw); VertexBuffer(VertexBuffer &&other); diff --git a/libopenage/renderer/vertex_state.cpp b/libopenage/renderer/vertex_state.cpp index 59611c9f58..b7a6fb00bd 100644 --- a/libopenage/renderer/vertex_state.cpp +++ b/libopenage/renderer/vertex_state.cpp @@ -5,8 +5,11 @@ namespace openage { namespace renderer { -VertexState::VertexState(Context *ctx) - : - context{ctx} {} +VertexState::VertexState(/*Context *ctx*/) + //: + //context{ctx} +{ + +} }} // openage::renderer diff --git a/libopenage/renderer/vertex_state.h b/libopenage/renderer/vertex_state.h index 48f9b964d3..b1e8a29dc7 100644 --- a/libopenage/renderer/vertex_state.h +++ b/libopenage/renderer/vertex_state.h @@ -24,7 +24,7 @@ class VertexBuffer; */ class VertexState { public: - VertexState(Context *ctx); + VertexState(/*Context *ctx*/); const VertexState &operator =(VertexState &&other) = delete; const VertexState &operator =(const VertexState &other) = delete; @@ -58,7 +58,7 @@ class VertexState { /** * Associated rendering context */ - Context *const context; + //Context *const context; /** * This vertex state has data for attributes with From e43f521cdcf3be98a3c09adf4b693418f726a89c Mon Sep 17 00:00:00 2001 From: Erik Griese Date: Mon, 1 May 2017 15:23:59 +0200 Subject: [PATCH 39/52] Enabled depth testing (added depth texture to FBO) --- libopenage/renderer/opengl/render_target.cpp | 11 ++++++++--- libopenage/renderer/opengl/texture.cpp | 2 ++ libopenage/renderer/resources/texture_info.cpp | 2 ++ libopenage/renderer/tests.cpp | 12 +++++++----- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/libopenage/renderer/opengl/render_target.cpp b/libopenage/renderer/opengl/render_target.cpp index b9e297a95a..31b40136cc 100644 --- a/libopenage/renderer/opengl/render_target.cpp +++ b/libopenage/renderer/opengl/render_target.cpp @@ -19,11 +19,16 @@ GlRenderTarget::GlRenderTarget(std::vector textures) { std::vector drawBuffers; + size_t colorTextureCount = 0; for (size_t i = 0; i < textures.size(); i++) { // TODO figure out attachment points from pixel formats - auto attachmentPoint = GL_COLOR_ATTACHMENT0 + i; - glFramebufferTexture2D(GL_FRAMEBUFFER, attachmentPoint, GL_TEXTURE_2D, textures[i]->get_handle(), 0); - drawBuffers.push_back(attachmentPoint); + if (textures[i]->get_info().get_format() == resources::pixel_format::depth24) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, textures[i]->get_handle(), 0); + } else { + auto attachmentPoint = GL_COLOR_ATTACHMENT0 + colorTextureCount++; + glFramebufferTexture2D(GL_FRAMEBUFFER, attachmentPoint, GL_TEXTURE_2D, textures[i]->get_handle(), 0); + drawBuffers.push_back(attachmentPoint); + } } glDrawBuffers(drawBuffers.size(), drawBuffers.data()); diff --git a/libopenage/renderer/opengl/texture.cpp b/libopenage/renderer/opengl/texture.cpp index a39382e530..f09bc55145 100644 --- a/libopenage/renderer/opengl/texture.cpp +++ b/libopenage/renderer/opengl/texture.cpp @@ -25,6 +25,8 @@ inline static std::tuple gl_format(resources::pixel_forma return std::make_tuple(GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE); case resources::pixel_format::rgba8ui: return std::make_tuple(GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE); + case resources::pixel_format::depth24: + return std::make_tuple(GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE); default: throw Error(MSG(err) << "invalid texture format passed to OpenGL."); } diff --git a/libopenage/renderer/resources/texture_info.cpp b/libopenage/renderer/resources/texture_info.cpp index 257ef96b4b..510e859c25 100644 --- a/libopenage/renderer/resources/texture_info.cpp +++ b/libopenage/renderer/resources/texture_info.cpp @@ -19,6 +19,8 @@ size_t pixel_size(pixel_format fmt) { return 4; case pixel_format::rgba8ui: return 4; + default: + throw Error(MSG(err) << "Unknown pixel format"); } } diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index 7c6a7dcb3c..0acefb15fc 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -153,7 +153,7 @@ void main() { auto transform3 = transform2; - transform2.pretranslate(Eigen::Vector3f(0.3f, 0.1f, 0.0f)); + transform2.pretranslate(Eigen::Vector3f(0.3f, 0.1f, 0.3f)); auto unif_in2 = shader->new_uniform_input( "mvp", transform2.matrix(), @@ -162,11 +162,11 @@ void main() { ); transform3.prerotate(Eigen::AngleAxisf(90.0f * 3.14159f / 180.0f, Eigen::Vector3f::UnitZ())); - transform3.pretranslate(Eigen::Vector3f(0.3f, 0.1f, 0.2f)); + transform3.pretranslate(Eigen::Vector3f(0.3f, 0.1f, 0.5f)); auto unif_in3 = shader->new_uniform_input( "mvp", transform3.matrix(), - "color", Eigen::Vector4f(0.0f, 0.0f, 1.0f, 0.5f), + "color", Eigen::Vector4f(0.0f, 0.0f, 1.0f, 1.0f), "u_id", 3u ); @@ -195,7 +195,8 @@ void main() { auto size = window.get_size(); auto color_texture = renderer->add_texture(resources::TextureInfo(size.x, size.y, resources::pixel_format::rgba8)); auto id_texture = renderer->add_texture(resources::TextureInfo(size.x, size.y, resources::pixel_format::rgba8ui)); - auto fbo = renderer->create_texture_target( { color_texture.get(), id_texture.get() } ); + auto depth_texture = renderer->add_texture(resources::TextureInfo(size.x, size.y, resources::pixel_format::depth24)); + auto fbo = renderer->create_texture_target( { color_texture.get(), id_texture.get(), depth_texture.get() } ); auto color_texture_uniform = shader_display->new_uniform_input("color_texture", color_texture.get()); Renderable display_obj { @@ -247,7 +248,8 @@ void main() { // resize fbo color_texture = renderer->add_texture(resources::TextureInfo(new_size.x, new_size.y, resources::pixel_format::rgba8)); id_texture = renderer->add_texture(resources::TextureInfo(new_size.x, new_size.y, resources::pixel_format::rgba8ui)); - fbo = renderer->create_texture_target( { color_texture.get(), id_texture.get() } ); + depth_texture = renderer->add_texture(resources::TextureInfo(new_size.x, new_size.y, resources::pixel_format::depth24)); + fbo = renderer->create_texture_target( { color_texture.get(), id_texture.get(), depth_texture.get() } ); shader_display->update_uniform_input(color_texture_uniform.get(), "color_texture", color_texture.get()); pass.target = fbo.get(); From 625476c87f580b761c791dbaa598bbaa00e06530 Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Thu, 11 May 2017 15:56:08 +0100 Subject: [PATCH 40/52] renderer: small shader fix to avoid invalid uniforms --- libopenage/renderer/opengl/shader_program.cpp | 24 ++++++++++++------- libopenage/renderer/opengl/shader_program.h | 2 +- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/libopenage/renderer/opengl/shader_program.cpp b/libopenage/renderer/opengl/shader_program.cpp index 078dc50335..10f40b7170 100644 --- a/libopenage/renderer/opengl/shader_program.cpp +++ b/libopenage/renderer/opengl/shader_program.cpp @@ -221,29 +221,37 @@ GlShaderProgram::GlShaderProgram(const std::vector &src glDeleteShader(shdr); } + // uniforms that are present in the source but not in the program, because they were optimized out by OpenGL + std::vector optimized_away; + GLuint tex_unit = 0; + // find the location of every uniform in the shader program for (auto& pair : this->uniforms) { GLint loc = glGetUniformLocation(this->id, pair.first.data()); - pair.second.location = loc; - if (loc == -1) { - log::log(MSG(info) - << "Could not determine the location of OpenGL shader uniform that was found before. Probably optimized away."); + log::log(MSG(warn) + << "OpenGL shader uniform " << pair.first << " was present in the source, but isn't present in the program. Probably optimized away."); + optimized_away.push_back(pair.first); continue; } - GLuint tex_unit = 0; + pair.second.location = loc; + if (pair.second.type == gl_uniform_t::SAMPLER2D) { if (tex_unit >= caps.max_texture_slots) { throw Error(MSG(err) - << "Tried to create shader that uses more texture sampler uniforms" + << "Tried to create shader that uses more texture sampler uniforms " << "than there are texture unit slots available."); } this->texunits_per_unifs.insert(std::make_pair(pair.first, tex_unit)); tex_unit += 1; } + } + // we only want valid uniforms to be present in the map + for (auto& unif : optimized_away) { + this->uniforms.erase(unif); } log::log(MSG(info) << "Created OpenGL shader program"); @@ -296,9 +304,7 @@ void GlShaderProgram::execute_with(const GlUniformInput *unif_in, const Geometry for (auto const &pair : unif_in->update_offs) { uint8_t const* ptr = data + pair.second; auto loc = this->uniforms[pair.first].location; - if(loc == -1) { - continue; - } + switch (this->uniforms[pair.first].type) { case gl_uniform_t::I32: glUniform1i(loc, *reinterpret_cast(ptr)); diff --git a/libopenage/renderer/opengl/shader_program.h b/libopenage/renderer/opengl/shader_program.h index 69eeb2bd6c..513a142231 100644 --- a/libopenage/renderer/opengl/shader_program.h +++ b/libopenage/renderer/opengl/shader_program.h @@ -59,7 +59,7 @@ class GlShaderProgram : public ShaderProgram { void use() const; /// Does what the description of Renderable specifies - updates the uniform values - /// and draws the Geometry of it's not nullptr. + /// and draws the Geometry if it's not nullptr. void execute_with(const GlUniformInput*, const Geometry*); bool has_uniform(const char*) override; From 9d9d1561c2f2354a774e1b61ae1347df8e23e7af Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Fri, 12 May 2017 00:50:40 +0100 Subject: [PATCH 41/52] renderer: add more uniform types --- libopenage/renderer/opengl/shader_program.cpp | 25 +++++++++++++++++-- libopenage/renderer/opengl/shader_program.h | 3 +++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/libopenage/renderer/opengl/shader_program.cpp b/libopenage/renderer/opengl/shader_program.cpp index 10f40b7170..ade4723f81 100644 --- a/libopenage/renderer/opengl/shader_program.cpp +++ b/libopenage/renderer/opengl/shader_program.cpp @@ -38,8 +38,14 @@ size_t uniform_size(gl_uniform_t type) { return 3 * sizeof(float); case gl_uniform_t::V4F32: return 4 * sizeof(float); + case gl_uniform_t::M3F32: + return 9 * sizeof(float); case gl_uniform_t::M4F32: return 16 * sizeof(float); + case gl_uniform_t::V2I32: + return 2 * sizeof(GLint); + case gl_uniform_t::V3I32: + return 3 * sizeof(GLint); case gl_uniform_t::SAMPLER2D: return sizeof(GLint); default: @@ -62,10 +68,16 @@ static gl_uniform_t glsl_str_to_type(std::experimental::string_view str) { return gl_uniform_t::V3F32; else if (str == "vec4") return gl_uniform_t::V4F32; - else if (str == "sampler2D") - return gl_uniform_t::SAMPLER2D; + else if (str == "mat3") + return gl_uniform_t::M3F32; else if (str == "mat4") return gl_uniform_t::M4F32; + else if (str == "ivec2") + return gl_uniform_t::V2I32; + else if (str == "ivec3") + return gl_uniform_t::V3I32; + else if (str == "sampler2D") + return gl_uniform_t::SAMPLER2D; else throw Error(MSG(err) << "Unsupported GLSL uniform type " << str); } @@ -328,9 +340,18 @@ void GlShaderProgram::execute_with(const GlUniformInput *unif_in, const Geometry case gl_uniform_t::V4F32: glUniform4fv(loc, 1, reinterpret_cast(ptr)); break; + case gl_uniform_t::M3F32: + glUniformMatrix3fv(loc, 1, false, reinterpret_cast(ptr)); + break; case gl_uniform_t::M4F32: glUniformMatrix4fv(loc, 1, false, reinterpret_cast(ptr)); break; + case gl_uniform_t::V2I32: + glUniform2iv(loc, 1, reinterpret_cast(ptr)); + break; + case gl_uniform_t::V3I32: + glUniform3iv(loc, 1, reinterpret_cast(ptr)); + break; case gl_uniform_t::SAMPLER2D: { GLuint tex_unit = this->texunits_per_unifs[pair.first]; GLuint tex = *reinterpret_cast(ptr); diff --git a/libopenage/renderer/opengl/shader_program.h b/libopenage/renderer/opengl/shader_program.h index 513a142231..7b4038d22a 100644 --- a/libopenage/renderer/opengl/shader_program.h +++ b/libopenage/renderer/opengl/shader_program.h @@ -26,7 +26,10 @@ enum class gl_uniform_t { V2F32, V3F32, V4F32, + M3F32, M4F32, + V2I32, + V3I32, SAMPLER2D, }; From 197c2b95e3a652765351c7b21f7cce554fb2027a Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Fri, 12 May 2017 19:39:45 +0100 Subject: [PATCH 42/52] renderer: add MeshData, move quad geometry there; make classes final; begin work on meshes --- libopenage/renderer/geometry.cpp | 64 ++------------- libopenage/renderer/geometry.h | 37 ++------- libopenage/renderer/opengl/geometry.cpp | 31 +++----- libopenage/renderer/opengl/geometry.h | 10 ++- libopenage/renderer/opengl/render_target.h | 2 +- libopenage/renderer/opengl/renderer.cpp | 4 +- libopenage/renderer/opengl/renderer.h | 2 +- libopenage/renderer/opengl/shader_program.cpp | 5 +- libopenage/renderer/opengl/shader_program.h | 5 +- libopenage/renderer/opengl/texture.h | 2 +- libopenage/renderer/opengl/uniform_input.h | 2 +- libopenage/renderer/resources/CMakeLists.txt | 1 + libopenage/renderer/resources/mesh_data.cpp | 61 +++++++++++++++ libopenage/renderer/resources/mesh_data.h | 78 +++++++++++++++++++ libopenage/renderer/tests.cpp | 21 +++-- 15 files changed, 196 insertions(+), 129 deletions(-) create mode 100644 libopenage/renderer/resources/mesh_data.cpp create mode 100644 libopenage/renderer/resources/mesh_data.h diff --git a/libopenage/renderer/geometry.cpp b/libopenage/renderer/geometry.cpp index 1b113817e3..70808b37e0 100644 --- a/libopenage/renderer/geometry.cpp +++ b/libopenage/renderer/geometry.cpp @@ -1,68 +1,16 @@ -// Copyright 2015-2016 the openage authors. See copying.md for legal info. +// Copyright 2015-2017 the openage authors. See copying.md for legal info. #include "geometry.h" -#include "vertex_state.h" -#include "vertex_buffer.h" + namespace openage { namespace renderer { -const std::array Geometry::quad_geometry{{ - { Eigen::Vector2f(-1.0f, 1.0f), Eigen::Vector2f(0.0f, 1.0f) }, - { Eigen::Vector2f(-1.0f, -1.0f), Eigen::Vector2f(0.0f, 0.0f) }, - { Eigen::Vector2f(1.0f, 1.0f), Eigen::Vector2f(1.0f, 1.0f) }, - { Eigen::Vector2f(1.0f, -1.0f), Eigen::Vector2f(1.0f, 0.0f) } -}}; - -Geometry::Geometry(VertexState* vaoPtr, VertexBuffer *vboPtr, const geometry_t &geometry_type) - : type(geometry_type), vertex_state(vaoPtr), vertex_buffer(vboPtr) -{ - if (this->type != geometry_t::quad) { - throw std::invalid_argument("Only Quad geometry is currently supported"); - } - this->setup_quad(); -} - -Geometry::~Geometry() -{ - -} - -void Geometry::setup_quad() -{ - std::vector data; - for (const auto& vertex : this->quad_geometry) { - data.push_back(vertex.position(0)); - data.push_back(vertex.position(1)); - data.push_back(vertex.texture_coordinates(0)); - data.push_back(vertex.texture_coordinates(1)); - } - - const size_t byte_size = data.size() * sizeof(decltype(data)::value_type); - this->vertex_buffer->alloc(byte_size); - auto buffer_data_ptr = this->vertex_buffer->get(true); - std::memcpy(buffer_data_ptr, data.data(), byte_size); - - this->vertex_buffer->upload(); - - VertexBuffer::vbo_section position_section{ - 0, - vertex_attribute_type::float_32, - 2u, - 0u, - 16u - }; - VertexBuffer::vbo_section texture_coordinate_section{ - 1, - vertex_attribute_type::float_32, - 2u, - 8u, - 16u - }; +Geometry::Geometry(geometry_t type) + : type(type) {} - this->vertex_buffer->add_section(position_section); - this->vertex_buffer->add_section(texture_coordinate_section); - this->vertex_state->attach_buffer(*(this->vertex_buffer)); +geometry_t Geometry::get_type() const { + return this->type; } }} //openage::renderer diff --git a/libopenage/renderer/geometry.h b/libopenage/renderer/geometry.h index 83901730f8..02f9650775 100644 --- a/libopenage/renderer/geometry.h +++ b/libopenage/renderer/geometry.h @@ -2,54 +2,33 @@ #pragma once -#include -#include -#include namespace openage { namespace renderer { -class VertexState; -class VertexBuffer; - /// The type of geometry. enum class geometry_t { - quad, + /// This passes 4 vertices with undefined positions to the shader. + /// The shader has to set the positions itself (e.g. using gl_VertexID in OpenGL). + bufferless_quad, + /// This passes valid geometry defined by a mesh to the shader. mesh, }; /// A class representing geometry to be passed to a draw call. -// TODO this class class Geometry { public: - struct Vertex { - Eigen::Vector2f position; - Eigen::Vector2f texture_coordinates; - }; - - - /// The default constructor makes a quad. - Geometry(VertexState* vaoPtr, VertexBuffer *vboPtr, const geometry_t &geometry_type); - virtual ~Geometry(); - - // TODO maybe use inheritance instead of enum? - // Geometry(mesh..) + virtual ~Geometry() = default; /// Returns the type of this geometry. - geometry_t get_type() const { return this->type; } - - virtual void draw() const = 0; + geometry_t get_type() const; protected: - std::unique_ptr vertex_state; - std::unique_ptr vertex_buffer; + /// The default constructor makes a quad. + explicit Geometry(geometry_t type); private: - static const std::array quad_geometry; - geometry_t type; - - void setup_quad(); }; }} // openage::renderer diff --git a/libopenage/renderer/opengl/geometry.cpp b/libopenage/renderer/opengl/geometry.cpp index e8d3aca772..cdef4c288a 100644 --- a/libopenage/renderer/opengl/geometry.cpp +++ b/libopenage/renderer/opengl/geometry.cpp @@ -1,33 +1,24 @@ -// Copyright 2015-2016 the openage authors. See copying.md for legal info. +// Copyright 2015-2017 the openage authors. See copying.md for legal info. + +#include "geometry.h" #include -#include "geometry.h" -#include "vertex_state.h" -#include "buffer.h" -#include "../vertex_buffer.h" +#include "../../error/error.h" + namespace openage { namespace renderer { namespace opengl { -GlGeometry::GlGeometry(const geometry_t &geometry_type) - : Geometry(new VertexState(), new VertexBuffer(new Buffer(), Buffer::usage::static_draw), geometry_type) -{ - -} - -GlGeometry::~GlGeometry() -{ +GlGeometry::GlGeometry() + : Geometry(geometry_t::bufferless_quad) {} -} - -void GlGeometry::draw() const -{ - if (this->get_type() != geometry_t::quad) { - throw std::runtime_error("Only drawing quads is currently supported"); +void GlGeometry::draw() const { + if (this->get_type() != geometry_t::bufferless_quad) { + throw Error(MSG(err) << "Only drawing bufferless quads is currently supported."); } - this->vertex_state->bind(); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } diff --git a/libopenage/renderer/opengl/geometry.h b/libopenage/renderer/opengl/geometry.h index 8a7850746c..f170bb2d50 100644 --- a/libopenage/renderer/opengl/geometry.h +++ b/libopenage/renderer/opengl/geometry.h @@ -10,13 +10,15 @@ namespace renderer { namespace opengl { /// The OpenGL class representing geometry to be passed to a draw call. -class GlGeometry : public Geometry { +class GlGeometry final : public Geometry { public: /// The default constructor makes a quad. - GlGeometry(const geometry_t &geometry_type); - virtual ~GlGeometry(); + GlGeometry(); + virtual ~GlGeometry() = default; - virtual void draw() const override; + /// Executes a draw command for the geometry on the currently active context. + /// Assumes bound and valid shader program and all other necessary state. + void draw() const; }; }}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/render_target.h b/libopenage/renderer/opengl/render_target.h index 5f216e870f..c76533d5f4 100644 --- a/libopenage/renderer/opengl/render_target.h +++ b/libopenage/renderer/opengl/render_target.h @@ -22,7 +22,7 @@ enum class gl_render_target_t { // TODO renderbuffers mixed with textures }; -class GlRenderTarget : public RenderTarget { +class GlRenderTarget final : public RenderTarget { public: /// Construct a render target pointed at the default framebuffer - the window. GlRenderTarget(); diff --git a/libopenage/renderer/opengl/renderer.cpp b/libopenage/renderer/opengl/renderer.cpp index adefe16913..7a20017af4 100644 --- a/libopenage/renderer/opengl/renderer.cpp +++ b/libopenage/renderer/opengl/renderer.cpp @@ -7,6 +7,7 @@ #include "texture.h" #include "shader_program.h" #include "uniform_input.h" +#include "geometry.h" namespace openage { @@ -68,7 +69,8 @@ void GlRenderer::render(RenderPass const& pass) { } auto in = dynamic_cast(obj.unif_in); - in->program->execute_with(in, obj.geometry); + auto geom = dynamic_cast(obj.geometry); + in->program->execute_with(in, geom); } } diff --git a/libopenage/renderer/opengl/renderer.h b/libopenage/renderer/opengl/renderer.h index 26c3b0f076..91665a3bf4 100644 --- a/libopenage/renderer/opengl/renderer.h +++ b/libopenage/renderer/opengl/renderer.h @@ -16,7 +16,7 @@ namespace openage { namespace renderer { namespace opengl { -class GlRenderer : public Renderer { +class GlRenderer final : public Renderer { public: GlRenderer(GlContext*); diff --git a/libopenage/renderer/opengl/shader_program.cpp b/libopenage/renderer/opengl/shader_program.cpp index ade4723f81..f3cd2d37d7 100644 --- a/libopenage/renderer/opengl/shader_program.cpp +++ b/libopenage/renderer/opengl/shader_program.cpp @@ -307,7 +307,7 @@ void GlShaderProgram::use() const { } } -void GlShaderProgram::execute_with(const GlUniformInput *unif_in, const Geometry *geom) { +void GlShaderProgram::execute_with(const GlUniformInput *unif_in, const GlGeometry *geom) { assert(unif_in->program == this); this->use(); @@ -368,8 +368,7 @@ void GlShaderProgram::execute_with(const GlUniformInput *unif_in, const Geometry } if (geom != nullptr) { - // TODO read geom and obj.blend + family - //glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + // TODO read obj.blend + family geom->draw(); } } diff --git a/libopenage/renderer/opengl/shader_program.h b/libopenage/renderer/opengl/shader_program.h index 7b4038d22a..3a2c46117e 100644 --- a/libopenage/renderer/opengl/shader_program.h +++ b/libopenage/renderer/opengl/shader_program.h @@ -11,6 +11,7 @@ #include "../renderer.h" #include "uniform_input.h" #include "context.h" +#include "geometry.h" namespace openage { @@ -43,7 +44,7 @@ struct GlUniform { }; /// A handle to an OpenGL shader program -class GlShaderProgram : public ShaderProgram { +class GlShaderProgram final : public ShaderProgram { public: /// Tries to create a shader program from the given sources. /// Throws an exception on compile/link errors. @@ -63,7 +64,7 @@ class GlShaderProgram : public ShaderProgram { /// Does what the description of Renderable specifies - updates the uniform values /// and draws the Geometry if it's not nullptr. - void execute_with(const GlUniformInput*, const Geometry*); + void execute_with(const GlUniformInput*, const GlGeometry*); bool has_uniform(const char*) override; diff --git a/libopenage/renderer/opengl/texture.h b/libopenage/renderer/opengl/texture.h index 0b4d9a4a05..5ded8dd1df 100644 --- a/libopenage/renderer/opengl/texture.h +++ b/libopenage/renderer/opengl/texture.h @@ -20,7 +20,7 @@ namespace opengl { /** * A handle to an OpenGL texture. */ -class GlTexture : public Texture { +class GlTexture final : public Texture { public: /// Constructs a texture and fills it with the given data. explicit GlTexture(const resources::TextureData&); diff --git a/libopenage/renderer/opengl/uniform_input.h b/libopenage/renderer/opengl/uniform_input.h index 7f4549d441..2de0cd6573 100644 --- a/libopenage/renderer/opengl/uniform_input.h +++ b/libopenage/renderer/opengl/uniform_input.h @@ -15,7 +15,7 @@ namespace opengl { class GlShaderProgram; /// Describes OpenGL-specific uniform valuations. -struct GlUniformInput : public UniformInput { +struct GlUniformInput final : public UniformInput { /// The program that this was created for. GlShaderProgram* program; diff --git a/libopenage/renderer/resources/CMakeLists.txt b/libopenage/renderer/resources/CMakeLists.txt index 3546f5dbab..fcc925ee7e 100644 --- a/libopenage/renderer/resources/CMakeLists.txt +++ b/libopenage/renderer/resources/CMakeLists.txt @@ -2,4 +2,5 @@ add_sources(libopenage shader_source.cpp texture_data.cpp texture_info.cpp + mesh_data.cpp ) diff --git a/libopenage/renderer/resources/mesh_data.cpp b/libopenage/renderer/resources/mesh_data.cpp new file mode 100644 index 0000000000..edc6767f8a --- /dev/null +++ b/libopenage/renderer/resources/mesh_data.cpp @@ -0,0 +1,61 @@ +// Copyright 2013-2017 the openage authors. See copying.md for legal info. + +#include "mesh_data.h" + +#include + +#include "../../error/error.h" + + +namespace openage { +namespace renderer { +namespace resources { + + +VertexInputInfo::VertexInputInfo(std::vector inputs, vertex_layout_t layout) + : inputs(inputs) + , layout(layout) {} + +/// Returns the size in bytes of a given per-vertex input type. +static size_t vert_in_size(vertex_input_t in) { + switch(in) { + case vertex_input_t::V2F32: + return 8; + case vertex_input_t::V3F32: + return 12; + default: + throw Error(MSG(err) << "Tried to find size of unknown vertex input type."); + } +} + +size_t VertexInputInfo::size() const { + size_t size = 0; + for (auto in : this->inputs) { + size += vert_in_size(in); + } + return size; +} + +/// Vertices of a quadrilateral filling the whole screen. +/// Format: (pos, tex_coords) = (x, y, u, v) +static const std::array quad_data = { { + -1.0f, 1.0f, 0.0f, 1.0f, + -1.0f, -1.0f, 0.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, -1.0f, 1.0f, 0.0f +} }; + +MeshData::MeshData(const util::Path &/*path*/) { + // asdf + throw "unimplemented lol"; +} + +MeshData::MeshData(init_quad_t) { + auto data_size = quad_data.size() * sizeof(decltype(quad_data)::value_type); + this->data = std::vector(data_size); + std::memcpy(data.data(), reinterpret_cast(quad_data.data()), data_size); + + this->info = { { vertex_input_t::V2F32, vertex_input_t::V2F32 }, vertex_layout_t::AOS }; +} + +}}} diff --git a/libopenage/renderer/resources/mesh_data.h b/libopenage/renderer/resources/mesh_data.h new file mode 100644 index 0000000000..5d4a32bf7b --- /dev/null +++ b/libopenage/renderer/resources/mesh_data.h @@ -0,0 +1,78 @@ +// Copyright 2013-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include +#include +#include +#include + +#include "../../util/path.h" + + +namespace openage { +namespace renderer { +namespace resources { + +/// The type of a single per-vertex input to the shader program. +enum class vertex_input_t { + V2F32, + V3F32, +}; + +enum class vertex_layout_t { + /// Array of structs. XYZUV, XYZUV, XYZUV + AOS, + /// Struct of arrays. XYZXYZXYZ, UVUVUV + SOA, +}; + +/// Information about vertex input data - which components a vertex contains +/// and how vertices are laid out in memory. +class VertexInputInfo { +public: + VertexInputInfo(std::vector, vertex_layout_t); + + ~VertexInputInfo() = default; + + /// Returns the size of a single vertex. + size_t size() const; + +private: + /// What kind of data the vertices contain and how it is laid out in memory. + std::vector inputs; + + /// How the vertices are laid out in `data`. + vertex_layout_t layout; +}; + +/// An empty struct used to initialize mesh data to a quad. +struct init_quad_t {}; + +class MeshData { +public: + /// Tries to load the mesh data from the specified file. + explicit MeshData(const util::Path&); + + /// Initializes the mesh data to a quad. + // TODO the empty struct is ugly + explicit MeshData(init_quad_t); + + ~MeshData() = default; + +private: + /// The raw vertex data. The size is an integer multiple of the size of a single vertex. + std::vector data; + + /// The indices of vertices to be drawn if the mesh is indexed. + /// For array drawing, empty optional. + std::experimental::optional> ids; + + /// Information about how to interpret the data to make vertices. + /// This is optional because initialization is deferred until the constructor + /// obtains the mesh data e.g. from disk, but it should always be present after + /// construction. + std::experimental::optional info; +}; + +}}} diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index 0acefb15fc..18a42ac0f4 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -72,12 +72,16 @@ void renderer_demo_0() { resources::shader_source_t::glsl_vertex, R"s( #version 330 -layout(location=0) in vec2 in_position; uniform mat4 mvp; void main() { - gl_Position = mvp * vec4(in_position, 0.0, 1.0); + gl_Position.x = 2.0 * float(gl_VertexID & 1) - 1.0; + gl_Position.y = 2.0 * float((gl_VertexID & 2) >> 1) - 1.0; + gl_Position.z = 0.0; + gl_Position.w = 1.0; + + gl_Position = mvp * gl_Position; } )s"); @@ -106,14 +110,15 @@ void main() { R"s( #version 330 -layout(location=0) in vec2 in_position; -layout(location=1) in vec2 in_texcoord; out vec2 v_uv; void main() { - gl_Position = vec4(in_position, 0.0, 1.0); + gl_Position.x = 2.0 * float(gl_VertexID & 1) - 1.0; + gl_Position.y = 2.0 * float((gl_VertexID & 2) >> 1) - 1.0; + gl_Position.z = 0.0; + gl_Position.w = 1.0; - v_uv = in_texcoord; + v_uv = gl_Position.xy * 0.5 + 0.5; } )s"); @@ -122,6 +127,7 @@ void main() { resources::shader_source_t::glsl_fragment, R"s( #version 330 + uniform sampler2D color_texture; in vec2 v_uv; @@ -129,7 +135,6 @@ out vec4 col; void main() { col = texture(color_texture, v_uv); - //col = vec4(v_uv, 0.0, 1.0); } )s"); @@ -170,7 +175,7 @@ void main() { "u_id", 3u ); - opengl::GlGeometry quad(geometry_t::quad); + opengl::GlGeometry quad; Renderable obj1 { unif_in1.get(), &quad, From 113388dd9c06bc4c15c6879b49e236a8fa96017c Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Fri, 12 May 2017 21:10:26 +0100 Subject: [PATCH 43/52] renderer: misc fixes and removals --- libopenage/renderer/context_state.cpp | 13 --------- libopenage/renderer/context_state.h | 30 --------------------- libopenage/renderer/geometry.h | 2 +- libopenage/renderer/resources/mesh_data.cpp | 2 +- libopenage/renderer/resources/mesh_data.h | 2 +- libopenage/renderer/shader_program.h | 2 ++ libopenage/renderer/tests.h | 7 ++--- libopenage/renderer/texture.cpp | 1 - 8 files changed, 7 insertions(+), 52 deletions(-) delete mode 100644 libopenage/renderer/context_state.cpp delete mode 100644 libopenage/renderer/context_state.h diff --git a/libopenage/renderer/context_state.cpp b/libopenage/renderer/context_state.cpp deleted file mode 100644 index 764d6b9619..0000000000 --- a/libopenage/renderer/context_state.cpp +++ /dev/null @@ -1,13 +0,0 @@ -// 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 deleted file mode 100644 index 03c20e2c4c..0000000000 --- a/libopenage/renderer/context_state.h +++ /dev/null @@ -1,30 +0,0 @@ -// 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 index 02f9650775..c90e6102ca 100644 --- a/libopenage/renderer/geometry.h +++ b/libopenage/renderer/geometry.h @@ -24,7 +24,7 @@ class Geometry { geometry_t get_type() const; protected: - /// The default constructor makes a quad. + /// Initialize the geometry to a given type. explicit Geometry(geometry_t type); private: diff --git a/libopenage/renderer/resources/mesh_data.cpp b/libopenage/renderer/resources/mesh_data.cpp index edc6767f8a..8cb610de6d 100644 --- a/libopenage/renderer/resources/mesh_data.cpp +++ b/libopenage/renderer/resources/mesh_data.cpp @@ -1,4 +1,4 @@ -// Copyright 2013-2017 the openage authors. See copying.md for legal info. +// Copyright 2017-2017 the openage authors. See copying.md for legal info. #include "mesh_data.h" diff --git a/libopenage/renderer/resources/mesh_data.h b/libopenage/renderer/resources/mesh_data.h index 5d4a32bf7b..cac442f50c 100644 --- a/libopenage/renderer/resources/mesh_data.h +++ b/libopenage/renderer/resources/mesh_data.h @@ -1,4 +1,4 @@ -// Copyright 2013-2017 the openage authors. See copying.md for legal info. +// Copyright 2017-2017 the openage authors. See copying.md for legal info. #pragma once diff --git a/libopenage/renderer/shader_program.h b/libopenage/renderer/shader_program.h index f458a7efae..b25c240912 100644 --- a/libopenage/renderer/shader_program.h +++ b/libopenage/renderer/shader_program.h @@ -1,5 +1,7 @@ // Copyright 2015-2017 the openage authors. See copying.md for legal info. +#pragma once + #include #include diff --git a/libopenage/renderer/tests.h b/libopenage/renderer/tests.h index 327f3123dd..b0de0dfebb 100644 --- a/libopenage/renderer/tests.h +++ b/libopenage/renderer/tests.h @@ -1,7 +1,6 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. +// Copyright 2015-2017 the openage authors. See copying.md for legal info. -#ifndef OPENAGE_RENDERER_TESTS_H_ -#define OPENAGE_RENDERER_TESTS_H_ +#pragma once namespace openage { @@ -12,5 +11,3 @@ namespace tests { void renderer_demo(int demo_id); }}} // openage::renderer::tests - -#endif diff --git a/libopenage/renderer/texture.cpp b/libopenage/renderer/texture.cpp index 98f013c576..8368a96b7d 100644 --- a/libopenage/renderer/texture.cpp +++ b/libopenage/renderer/texture.cpp @@ -3,7 +3,6 @@ #include #include "texture.h" - #include "../error/error.h" From 38bd3ee5332dcc65f25ecf1535cc1342bc64c4eb Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Mon, 15 May 2017 13:44:19 +0100 Subject: [PATCH 44/52] renderer: add support for meshed geometry --- libopenage/renderer/CMakeLists.txt | 9 +- libopenage/renderer/opengl/CMakeLists.txt | 6 +- libopenage/renderer/opengl/buffer.cpp | 107 ++++++++--------- libopenage/renderer/opengl/buffer.h | 82 ++++++------- libopenage/renderer/opengl/context.cpp | 14 +-- libopenage/renderer/opengl/geometry.cpp | 53 ++++++++- libopenage/renderer/opengl/geometry.h | 24 ++++ libopenage/renderer/opengl/renderer.cpp | 8 ++ libopenage/renderer/opengl/renderer.h | 3 + libopenage/renderer/opengl/texture.cpp | 1 + libopenage/renderer/opengl/vertex_array.cpp | 117 +++++++++++++++++++ libopenage/renderer/opengl/vertex_array.h | 61 ++++++++++ libopenage/renderer/opengl/vertex_state.cpp | 95 --------------- libopenage/renderer/opengl/vertex_state.h | 55 --------- libopenage/renderer/renderer.h | 9 ++ libopenage/renderer/resources/CMakeLists.txt | 2 +- libopenage/renderer/resources/mesh_data.cpp | 79 ++++++++++--- libopenage/renderer/resources/mesh_data.h | 65 ++++++++++- libopenage/renderer/tests.cpp | 28 +++-- libopenage/renderer/vertex_buffer.h | 6 +- libopenage/renderer/vertex_state.h | 5 +- 21 files changed, 515 insertions(+), 314 deletions(-) create mode 100644 libopenage/renderer/opengl/vertex_array.cpp create mode 100644 libopenage/renderer/opengl/vertex_array.h delete mode 100644 libopenage/renderer/opengl/vertex_state.cpp delete mode 100644 libopenage/renderer/opengl/vertex_state.h diff --git a/libopenage/renderer/CMakeLists.txt b/libopenage/renderer/CMakeLists.txt index 0e09e46b0b..babe75a043 100644 --- a/libopenage/renderer/CMakeLists.txt +++ b/libopenage/renderer/CMakeLists.txt @@ -1,13 +1,10 @@ add_sources(libopenage + color.cpp + geometry.cpp tests.cpp + text.cpp texture.cpp window.cpp - text.cpp - color.cpp - geometry.cpp - vertex_buffer.cpp - buffer.cpp - vertex_state.cpp ) pxdgen( diff --git a/libopenage/renderer/opengl/CMakeLists.txt b/libopenage/renderer/opengl/CMakeLists.txt index 05290b4b49..54043c475d 100644 --- a/libopenage/renderer/opengl/CMakeLists.txt +++ b/libopenage/renderer/opengl/CMakeLists.txt @@ -1,10 +1,10 @@ add_sources(libopenage + buffer.cpp context.cpp + geometry.cpp render_target.cpp renderer.cpp shader_program.cpp texture.cpp - vertex_state.cpp - buffer.cpp - geometry.cpp + vertex_array.cpp ) diff --git a/libopenage/renderer/opengl/buffer.cpp b/libopenage/renderer/opengl/buffer.cpp index 980540d000..ef94f0a94c 100644 --- a/libopenage/renderer/opengl/buffer.cpp +++ b/libopenage/renderer/opengl/buffer.cpp @@ -1,86 +1,73 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. - -#include "../../config.h" -#if WITH_OPENGL +// Copyright 2015-2017 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} { +GlBuffer::GlBuffer(size_t size, GLenum usage) + : size(size) + , usage(usage) { + GLuint id; + glGenBuffers(1, &id); + this->id = id; - glGenBuffers(1, &this->id); + this->bind(GL_ARRAY_BUFFER); + glBufferData(GL_ARRAY_BUFFER, size, 0, usage); } -Buffer::~Buffer() { - glDeleteBuffers(1, &this->id); +GlBuffer::GlBuffer(const uint8_t *data, size_t size, GLenum usage) + : size(size) + , usage(usage) { + GLuint id; + glGenBuffers(1, &id); + this->id = id; + + this->bind(GL_ARRAY_BUFFER); + glBufferData(GL_ARRAY_BUFFER, size, data, usage); } -void Buffer::upload(bind_target target, usage usage) { - GLenum gl_usage = this->get_usage(usage); - GLenum gl_slot = this->get_target(target); +GlBuffer::GlBuffer(GlBuffer &&other) + : id(other.id) + , size(other.size) + , usage(other.usage) { + other.id = std::experimental::optional(); +} - if (not this->on_gpu) { - this->bind(target); - glBufferData(gl_slot, this->allocd, this->get(), gl_usage); - this->on_gpu = true; - } +GlBuffer &GlBuffer::operator =(GlBuffer &&other) { + this->id = other.id; + this->size = other.size; + this->usage = other.usage; + other.id = std::experimental::optional(); + + return *this; } -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); +GlBuffer::~GlBuffer() { + if (this->id) { + glDeleteBuffers(1, &this->id.value()); + } } +size_t GlBuffer::get_size() const { + return this->size; +} -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!"}; +void GlBuffer::upload_data(const uint8_t *data, size_t offset, size_t size) { + if (unlikely(offset + size > this->size)) { + throw Error(MSG(err) << "Tried to upload more data to OpenGL buffer than can fit."); } + + this->bind(GL_ARRAY_BUFFER); + glBufferSubData(GL_ARRAY_BUFFER, offset, size, data); } -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!"}; - } +void GlBuffer::bind(GLenum target) const { + glBindBuffer(target, *this->id); } }}} // openage::renderer::opengl - -#endif diff --git a/libopenage/renderer/opengl/buffer.h b/libopenage/renderer/opengl/buffer.h index 472d92942f..2fab37ab0d 100644 --- a/libopenage/renderer/opengl/buffer.h +++ b/libopenage/renderer/opengl/buffer.h @@ -1,54 +1,54 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. +// Copyright 2015-2017 the openage authors. See copying.md for legal info. -#ifndef OPENAGE_RENDERER_OPENGL_BUFFER_H_ -#define OPENAGE_RENDERER_OPENGL_BUFFER_H_ +#pragma once -#include "../buffer.h" +#include #include -#include + namespace openage { namespace renderer { namespace opengl { -/** - * OpenGL data buffer. - */ -class Buffer : public renderer::Buffer { +/// OpenGL data buffer on the GPU. +class GlBuffer { 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, 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; + /// Creates an empty buffer of the specified size. + GlBuffer(size_t size, GLenum usage = GL_STATIC_DRAW); + + /// Creates a buffer of the specified size and fills it with the given data. + GlBuffer(const uint8_t *data, size_t size, GLenum usage = GL_STATIC_DRAW); + + ~GlBuffer(); + + /// Moving is supported. + GlBuffer &operator =(GlBuffer&&); + GlBuffer(GlBuffer&&); + + // TODO support copies + GlBuffer(const GlBuffer&) = delete; + GlBuffer &operator =(const GlBuffer&) = delete; + + /// The size in bytes of this buffer. + size_t get_size() const; + + /// Uploads `size` bytes of new data starting at `offset`. + /// `offset + size` has to be less than or equal to `get_size()`. + void upload_data(const uint8_t *data, size_t offset, size_t size); + + /// Bind this buffer to the specified GL target. + void bind(GLenum target) const; + +private: + /// The OpenGL handle to this buffer. Can be empty if the object was moved out of. + std::experimental::optional id; + + /// The size in bytes of this buffer. + size_t size; + + /// The GL usage hint for this buffer. + GLenum usage; }; }}} // openage::renderer::opengl - -#endif diff --git a/libopenage/renderer/opengl/context.cpp b/libopenage/renderer/opengl/context.cpp index 86f080d185..214b924685 100644 --- a/libopenage/renderer/opengl/context.cpp +++ b/libopenage/renderer/opengl/context.cpp @@ -163,35 +163,35 @@ void GlContext::check_error() { // 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. - return "invalid enum passed to opengl call"; + return "GL_INVALID_ENUM"; 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. - return "invalid value passed to opengl call"; + return "GL_INVALID_VALUE"; 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. - return "invalid operation performed during some state"; + return "GL_INVALID_OPERATION"; 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. - return "invalid framebuffer operation"; + return "GL_INVALID_FRAMEBUFFER_OPERATION"; 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. - return "out of memory, wtf?"; + return "GL_OUT_OF_MEMORY"; case GL_STACK_UNDERFLOW: // An attempt has been made to perform an operation that would // cause an internal stack to underflow. - return "stack underflow"; + return "GL_STACK_UNDERFLOW"; case GL_STACK_OVERFLOW: // An attempt has been made to perform an operation that would // cause an internal stack to overflow. - return "stack overflow"; + return "GL_STACK_OVERFLOW"; default: // unknown error state return "unknown error"; diff --git a/libopenage/renderer/opengl/geometry.cpp b/libopenage/renderer/opengl/geometry.cpp index cdef4c288a..0c03c1ee2e 100644 --- a/libopenage/renderer/opengl/geometry.cpp +++ b/libopenage/renderer/opengl/geometry.cpp @@ -5,21 +5,68 @@ #include #include "../../error/error.h" +#include "../../datastructure/constexpr_map.h" namespace openage { namespace renderer { namespace opengl { +static constexpr auto gl_prim = datastructure::create_const_map( + std::make_pair(resources::vertex_primitive_t::POINTS, GL_POINTS), + std::make_pair(resources::vertex_primitive_t::LINES, GL_LINES), + std::make_pair(resources::vertex_primitive_t::LINE_STRIP, GL_LINE_STRIP), + std::make_pair(resources::vertex_primitive_t::TRIANGLES, GL_TRIANGLES), + std::make_pair(resources::vertex_primitive_t::TRIANGLE_STRIP, GL_TRIANGLE_STRIP), + std::make_pair(resources::vertex_primitive_t::TRIANGLE_FAN, GL_TRIANGLE_FAN) +); + +static constexpr auto gl_idx_t = datastructure::create_const_map( + std::make_pair(resources::index_t::U8, GL_UNSIGNED_BYTE), + std::make_pair(resources::index_t::U16, GL_UNSIGNED_SHORT), + std::make_pair(resources::index_t::U32, GL_UNSIGNED_INT) +); + GlGeometry::GlGeometry() : Geometry(geometry_t::bufferless_quad) {} +GlGeometry::GlGeometry(const resources::MeshData &mesh) + : Geometry(geometry_t::mesh) { + GlBuffer verts(mesh.get_data().data(), mesh.get_data().size()); + + this->mesh = GlMesh { + std::move(verts), + {}, + GlVertexArray (verts, mesh.get_info()), + mesh.get_data().size() / mesh.get_info().vert_size(), + gl_prim.get(mesh.get_info().get_primitive()), + {} + }; + + if (mesh.get_ids()) { + this->mesh->indices = GlBuffer(mesh.get_ids()->data(), mesh.get_ids()->size()); + this->mesh->index_type = gl_idx_t.get(*mesh.get_info().get_index_type()); + this->mesh->vert_count = mesh.get_ids()->size() / sizeof(GLuint); + } +} + void GlGeometry::draw() const { - if (this->get_type() != geometry_t::bufferless_quad) { - throw Error(MSG(err) << "Only drawing bufferless quads is currently supported."); + if (this->get_type() == geometry_t::bufferless_quad) { + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } + else if (this->get_type() == geometry_t::mesh) { + auto const& mesh = *this->mesh; + mesh.vao.bind(); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + if (mesh.indices) { + mesh.indices->bind(GL_ELEMENT_ARRAY_BUFFER); + + glDrawElements(mesh.primitive, mesh.vert_count, *mesh.index_type, 0); + } + else { + glDrawArrays(GL_TRIANGLE_STRIP, 0, mesh.vert_count); + } + } } }}} //openage::renderer::opengl diff --git a/libopenage/renderer/opengl/geometry.h b/libopenage/renderer/opengl/geometry.h index f170bb2d50..4c0a8d5091 100644 --- a/libopenage/renderer/opengl/geometry.h +++ b/libopenage/renderer/opengl/geometry.h @@ -2,7 +2,13 @@ #pragma once +#include + #include "../geometry.h" +#include "../resources/mesh_data.h" + +#include "buffer.h" +#include "vertex_array.h" namespace openage { @@ -14,11 +20,29 @@ class GlGeometry final : public Geometry { public: /// The default constructor makes a quad. GlGeometry(); + + /// Initialize a meshed geometry. Relatively costly, has to initialize GL buffers and copy vertex data. + explicit GlGeometry(resources::MeshData const&); + virtual ~GlGeometry() = default; /// Executes a draw command for the geometry on the currently active context. /// Assumes bound and valid shader program and all other necessary state. void draw() const; + +private: + struct GlMesh { + GlBuffer vertices; + std::experimental::optional indices; + GlVertexArray vao; + size_t vert_count; + GLenum primitive; + std::experimental::optional index_type; + }; + + /// Data managing GPU memory and interpretation of mesh data. + /// Only present if the type is a mesh. + std::experimental::optional mesh; }; }}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/renderer.cpp b/libopenage/renderer/opengl/renderer.cpp index 7a20017af4..2c3d852dd3 100644 --- a/libopenage/renderer/opengl/renderer.cpp +++ b/libopenage/renderer/opengl/renderer.cpp @@ -33,6 +33,14 @@ std::unique_ptr GlRenderer::add_shader(std::vector(srcs, this->gl_context->get_capabilities()); } +std::unique_ptr GlRenderer::add_mesh_geometry(resources::MeshData const& mesh) { + return std::make_unique(mesh); +} + +std::unique_ptr GlRenderer::add_bufferless_quad() { + return std::make_unique(); +} + std::unique_ptr GlRenderer::create_texture_target(std::vector textures) { std::vector gl_textures; for (auto tex : textures) { diff --git a/libopenage/renderer/opengl/renderer.h b/libopenage/renderer/opengl/renderer.h index 91665a3bf4..262a246c9c 100644 --- a/libopenage/renderer/opengl/renderer.h +++ b/libopenage/renderer/opengl/renderer.h @@ -25,6 +25,9 @@ class GlRenderer final : public Renderer { std::unique_ptr add_shader(std::vector const&) override; + std::unique_ptr add_mesh_geometry(resources::MeshData const&) override; + std::unique_ptr add_bufferless_quad() override; + std::unique_ptr create_texture_target(std::vector) override; RenderTarget const* get_display_target() override; diff --git a/libopenage/renderer/opengl/texture.cpp b/libopenage/renderer/opengl/texture.cpp index f09bc55145..412d4a8e70 100644 --- a/libopenage/renderer/opengl/texture.cpp +++ b/libopenage/renderer/opengl/texture.cpp @@ -118,6 +118,7 @@ resources::TextureData GlTexture::into_data() { glPixelStorei(GL_PACK_ALIGNMENT, this->info.get_row_alignment()); glBindTexture(GL_TEXTURE_2D, *this->handle); + // TODO use a Pixel Buffer Object instead glGetTexImage(GL_TEXTURE_2D, 0, std::get<1>(fmt_in_out), std::get<2>(fmt_in_out), data.data()); return resources::TextureData(resources::TextureInfo(this->info), std::move(data)); diff --git a/libopenage/renderer/opengl/vertex_array.cpp b/libopenage/renderer/opengl/vertex_array.cpp new file mode 100644 index 0000000000..197a1798fc --- /dev/null +++ b/libopenage/renderer/opengl/vertex_array.cpp @@ -0,0 +1,117 @@ +// Copyright 2015-2017 the openage authors. See copying.md for legal info. + +#include "vertex_array.h" + +#include "../../error/error.h" +#include "../../datastructure/constexpr_map.h" + + +namespace openage { +namespace renderer { +namespace opengl { + +static constexpr auto gl_types = datastructure::create_const_map( + std::make_pair(resources::vertex_input_t::V2F32, GL_FLOAT), + std::make_pair(resources::vertex_input_t::V3F32, GL_FLOAT) +); + +GlVertexArray::GlVertexArray(std::vector> buffers) { + GLuint id; + glGenVertexArrays(1, &id); + this->id = id; + + this->bind(); + + GLuint attrib = 0; + for (auto buf_info : buffers) { + auto const &buf = buf_info.first; + auto const &info = buf_info.second; + + buf.bind(GL_ARRAY_BUFFER); + + if (info.get_layout() == resources::vertex_layout_t::AOS) { + size_t offset = 0; + + for (auto in : info.get_inputs()) { + glEnableVertexAttribArray(attrib); + + glVertexAttribPointer( + attrib, + resources::vertex_input_count(in), + gl_types.get(in), + GL_FALSE, + info.vert_size(), + reinterpret_cast(offset) + ); + + offset += resources::vertex_input_size(in); + attrib += 1; + } + } + else if (info.get_layout() == resources::vertex_layout_t::SOA) { + size_t offset = 0; + size_t vert_count = buf.get_size() / info.vert_size(); + + for (auto in : info.get_inputs()) { + glEnableVertexAttribArray(attrib); + + glVertexAttribPointer( + attrib, + resources::vertex_input_count(in), + gl_types.get(in), + GL_FALSE, + 0, + reinterpret_cast(offset) + ); + + offset += resources::vertex_input_size(in) * vert_count; + attrib += 1; + } + + } + else { + throw Error(MSG(err) << "Unknown vertex layout."); + } + } +} + +GlVertexArray::GlVertexArray(GlBuffer const& buf, resources::VertexInputInfo const& info) + : GlVertexArray( { { buf, info } } ) {} + +GlVertexArray::GlVertexArray() { + GLuint id; + glGenVertexArrays(1, &id); + this->id = id; +} + +GlVertexArray::GlVertexArray(GlVertexArray &&other) + : id(other.id) { + other.id = {}; +} + +GlVertexArray &GlVertexArray::operator =(GlVertexArray &&other) { + this->id = other.id; + other.id = {}; + + return *this; +} + +GlVertexArray::~GlVertexArray() { + if (this->id) { + glDeleteVertexArrays(1, &this->id.value()); + } +} + +void GlVertexArray::bind() const { + glBindVertexArray(*this->id); + + // TODO necessary? + /* + // then enable all contained attribute ids + for (auto &attr_id : this->bound_attributes) { + glEnableVertexAttribArray(attr_id); + } + */ +} + +}}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/vertex_array.h b/libopenage/renderer/opengl/vertex_array.h new file mode 100644 index 0000000000..6b3d831c3e --- /dev/null +++ b/libopenage/renderer/opengl/vertex_array.h @@ -0,0 +1,61 @@ +// Copyright 2015-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include +#include + +#include "../resources/mesh_data.h" +#include "buffer.h" + + +namespace openage { +namespace renderer { +namespace opengl { + +/// An OpenGL Vertex Array Object. It represents a mapping between a buffer +/// that contains vertex data and a way for the context to interpret it. +class GlVertexArray { +public: + /// Initializes a vertex array from the given mesh data. + /// The inputs are parsed as follows - each subsequent (GlBuffer, VertexInputInfo) pair is added + /// to the VAO in the same order as they appear in the vector. Each buffer is bound according to its + /// input info and each subsequent vertex attribute is attached to an id in ascending order. + /// For example, the inputs: + /// in vec3 pos; + /// in vec2 uv; + /// in mat3 rot; + /// could be bound to two buffers like so: + /// GlBuffer buf1(data1, size1); + /// GlBuffer buf2(data2, size2); + /// GlVertexArray( { + /// std::make_pair(buf1, { { vertex_input_t::V3F32, vertex_input_t::V2F32 }, vertex_layout_t::SOA }, + /// std::make_pair(buf2, { { vertex_input_t::M3F32 }, vertex_input_t::AOS /*or ::SOA, doesn't matter*/ }, + /// } ); + /// Not that anyone would ever want to bind more than one buffer to a single mesh.. + GlVertexArray(std::vector> buffers); + + /// Executes the buffer list constructor with one element. + GlVertexArray(GlBuffer const&, resources::VertexInputInfo const&); + + /// The default constructor initializes an empty VAO with no attributes. + /// This is useful for bufferless drawing. + GlVertexArray(); + + GlVertexArray(GlVertexArray&&); + GlVertexArray &operator =(GlVertexArray&&); + + GlVertexArray(GlVertexArray const&) = delete; + GlVertexArray &operator =(GlVertexArray const&) = delete; + + ~GlVertexArray(); + + /// Make this vertex array object the current one. + void bind() const; + +protected: + /// OpenGL handle to this object. + std::experimental::optional id; +}; + +}}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/vertex_state.cpp b/libopenage/renderer/opengl/vertex_state.cpp deleted file mode 100644 index 02f794d515..0000000000 --- a/libopenage/renderer/opengl/vertex_state.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// 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(VertexBuffer &buf) { - // make the vao active, it will store the section assignment - this->bind(); - - // push the buffer to the gpu, if neccessary. - 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, section.stride, - 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(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 deleted file mode 100644 index f8c7261f78..0000000000 --- a/libopenage/renderer/opengl/vertex_state.h +++ /dev/null @@ -1,55 +0,0 @@ -// 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 uploads the buffer to the gpu. - * This sets pointers to the given buffer to prepare usage. - */ - void attach_buffer(VertexBuffer &buf) override; - - /** - * Remove attribute buffer section assignments. - */ - void detach_buffer(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/renderer.h b/libopenage/renderer/renderer.h index dee43a6bd6..4660c7e035 100644 --- a/libopenage/renderer/renderer.h +++ b/libopenage/renderer/renderer.h @@ -13,6 +13,7 @@ namespace resources { class TextureData; class TextureInfo; class ShaderSource; +class MeshData; } class ShaderProgram; @@ -81,6 +82,14 @@ class Renderer { /// for graphics rendering. virtual std::unique_ptr add_shader(std::vector const&) = 0; + /// Creates a Geometry object from the given mesh data, uploading it to the GPU by creating appropriate buffer. + /// The vertex attributes will be passed to the shader as described in the mesh data. + virtual std::unique_ptr add_mesh_geometry(resources::MeshData const&) = 0; + + /// Adds a Geometry object that passes a simple 4-vertex drawing command with no vertex attributes to the shader. + /// Useful for generating positions in the vertex shader. + virtual std::unique_ptr add_bufferless_quad() = 0; + /// Constructs a render target from the given textures. All subsequent drawing operations pointed at this /// target will write to these textures. The textures are attached to the render target according to their /// internal format, for example RGB will be attached as a color component and DEPTH will be attached as a diff --git a/libopenage/renderer/resources/CMakeLists.txt b/libopenage/renderer/resources/CMakeLists.txt index fcc925ee7e..4c58d2c733 100644 --- a/libopenage/renderer/resources/CMakeLists.txt +++ b/libopenage/renderer/resources/CMakeLists.txt @@ -1,6 +1,6 @@ add_sources(libopenage + mesh_data.cpp shader_source.cpp texture_data.cpp texture_info.cpp - mesh_data.cpp ) diff --git a/libopenage/renderer/resources/mesh_data.cpp b/libopenage/renderer/resources/mesh_data.cpp index 8cb610de6d..4b83ab817b 100644 --- a/libopenage/renderer/resources/mesh_data.cpp +++ b/libopenage/renderer/resources/mesh_data.cpp @@ -5,40 +5,71 @@ #include #include "../../error/error.h" +#include "../../datastructure/constexpr_map.h" namespace openage { namespace renderer { namespace resources { +static constexpr auto vin_size = datastructure::create_const_map( + std::make_pair(vertex_input_t::V2F32, 8), + std::make_pair(vertex_input_t::V3F32, 12), + std::make_pair(vertex_input_t::M3F32, 36) +); -VertexInputInfo::VertexInputInfo(std::vector inputs, vertex_layout_t layout) - : inputs(inputs) - , layout(layout) {} - -/// Returns the size in bytes of a given per-vertex input type. -static size_t vert_in_size(vertex_input_t in) { - switch(in) { - case vertex_input_t::V2F32: - return 8; - case vertex_input_t::V3F32: - return 12; - default: - throw Error(MSG(err) << "Tried to find size of unknown vertex input type."); - } +static constexpr auto vin_count = datastructure::create_const_map( + std::make_pair(vertex_input_t::V2F32, 2), + std::make_pair(vertex_input_t::V3F32, 3), + std::make_pair(vertex_input_t::M3F32, 9) +); + +size_t vertex_input_size(vertex_input_t in) { + return vin_size.get(in); } -size_t VertexInputInfo::size() const { +size_t vertex_input_count(vertex_input_t in) { + return vin_count.get(in); +} + +VertexInputInfo::VertexInputInfo(std::vector inputs, vertex_layout_t layout, vertex_primitive_t primitive) + : inputs(inputs) + , layout(layout) + , primitive(primitive) {} + +VertexInputInfo::VertexInputInfo(std::vector inputs, vertex_layout_t layout, vertex_primitive_t primitive, index_t index_type) + : inputs(inputs) + , layout(layout) + , primitive(primitive) + , index_type(index_type) {} + +size_t VertexInputInfo::vert_size() const { size_t size = 0; for (auto in : this->inputs) { - size += vert_in_size(in); + size += vertex_input_size(in); } return size; } +const std::vector &VertexInputInfo::get_inputs() const { + return this->inputs; +} + +vertex_layout_t VertexInputInfo::get_layout() const { + return this->layout; +} + +vertex_primitive_t VertexInputInfo::get_primitive() const { + return this->primitive; +} + +std::experimental::optional VertexInputInfo::get_index_type() const { + return this->index_type; +} + /// Vertices of a quadrilateral filling the whole screen. /// Format: (pos, tex_coords) = (x, y, u, v) -static const std::array quad_data = { { +static constexpr std::array quad_data = { { -1.0f, 1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, @@ -55,7 +86,19 @@ MeshData::MeshData(init_quad_t) { this->data = std::vector(data_size); std::memcpy(data.data(), reinterpret_cast(quad_data.data()), data_size); - this->info = { { vertex_input_t::V2F32, vertex_input_t::V2F32 }, vertex_layout_t::AOS }; + this->info = { { vertex_input_t::V2F32, vertex_input_t::V2F32 }, vertex_layout_t::AOS, vertex_primitive_t::TRIANGLE_STRIP }; +} + +std::vector const &MeshData::get_data() const { + return this->data; +} + +std::experimental::optional> const &MeshData::get_ids() const { + return this->ids; +} + +VertexInputInfo MeshData::get_info() const { + return *this->info; } }}} diff --git a/libopenage/renderer/resources/mesh_data.h b/libopenage/renderer/resources/mesh_data.h index cac442f50c..85151dc41a 100644 --- a/libopenage/renderer/resources/mesh_data.h +++ b/libopenage/renderer/resources/mesh_data.h @@ -18,8 +18,33 @@ namespace resources { enum class vertex_input_t { V2F32, V3F32, + M3F32, }; +/// The primitive type that the vertices in a mesh combine into. +enum class vertex_primitive_t { + POINTS, + LINES, + LINE_STRIP, + TRIANGLES, + TRIANGLE_STRIP, + TRIANGLE_FAN, +}; + +/// The type of indices used for indexed rendering. +enum class index_t { + U8, + U16, + U32, +}; + +/// Returns the size in bytes of a per-vertex input. +size_t vertex_input_size(vertex_input_t); + +/// Returns the number of elements of the underlying type in a given per-vertex input type. +/// For example, V3F32 has 3 float elements, so the value is 3. +size_t vertex_input_count(vertex_input_t); + enum class vertex_layout_t { /// Array of structs. XYZUV, XYZUV, XYZUV AOS, @@ -31,19 +56,40 @@ enum class vertex_layout_t { /// and how vertices are laid out in memory. class VertexInputInfo { public: - VertexInputInfo(std::vector, vertex_layout_t); + /// Initialize an input info for array rendering. + VertexInputInfo(std::vector, vertex_layout_t, vertex_primitive_t); + + /// Initialize an input info for indexed rendering. + VertexInputInfo(std::vector, vertex_layout_t, vertex_primitive_t, index_t); - ~VertexInputInfo() = default; + /// Returns the list of per-vertex inputs. + const std::vector &get_inputs() const; + + /// Returns the layout of vertices in memory. + vertex_layout_t get_layout() const; /// Returns the size of a single vertex. - size_t size() const; + size_t vert_size() const; + + /// Returns the primitive interpretation mode. + vertex_primitive_t get_primitive() const; + + /// Returns the type of indices used for indexed drawing, + /// which may not be present if array drawing is used. + std::experimental::optional get_index_type() const; private: /// What kind of data the vertices contain and how it is laid out in memory. std::vector inputs; - /// How the vertices are laid out in `data`. + /// How the vertices are laid out in the data buffer. vertex_layout_t layout; + + /// The primitive type. + vertex_primitive_t primitive; + + /// The type of indices if they exist. + std::experimental::optional index_type; }; /// An empty struct used to initialize mesh data to a quad. @@ -58,7 +104,14 @@ class MeshData { // TODO the empty struct is ugly explicit MeshData(init_quad_t); - ~MeshData() = default; + /// Returns the raw vertex data. + std::vector const &get_data() const; + + /// Returns the indices used for indexed drawing if they exist. + std::experimental::optional> const &get_ids() const; + + /// Returns information describing the vertices in this mesh. + VertexInputInfo get_info() const; private: /// The raw vertex data. The size is an integer multiple of the size of a single vertex. @@ -66,7 +119,7 @@ class MeshData { /// The indices of vertices to be drawn if the mesh is indexed. /// For array drawing, empty optional. - std::experimental::optional> ids; + std::experimental::optional> ids; /// Information about how to interpret the data to make vertices. /// This is optional because initialization is deferred until the constructor diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index 18a42ac0f4..1735690b6a 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -8,7 +8,6 @@ #include #include "../log/log.h" -#include "opengl/geometry.h" #include "../error/error.h" #include "resources/shader_source.h" #include "opengl/renderer.h" @@ -73,15 +72,20 @@ void renderer_demo_0() { R"s( #version 330 +layout(location=0) in vec2 position; +layout(location=1) in vec2 uv; uniform mat4 mvp; void main() { +/* gl_Position.x = 2.0 * float(gl_VertexID & 1) - 1.0; gl_Position.y = 2.0 * float((gl_VertexID & 2) >> 1) - 1.0; gl_Position.z = 0.0; gl_Position.w = 1.0; gl_Position = mvp * gl_Position; +*/ +gl_Position = mvp * vec4(position, 0.0, 1.0); } )s"); @@ -110,15 +114,22 @@ void main() { R"s( #version 330 + +layout(location=0) in vec2 position; +layout(location=1) in vec2 uv; out vec2 v_uv; void main() { +/* gl_Position.x = 2.0 * float(gl_VertexID & 1) - 1.0; gl_Position.y = 2.0 * float((gl_VertexID & 2) >> 1) - 1.0; gl_Position.z = 0.0; gl_Position.w = 1.0; v_uv = gl_Position.xy * 0.5 + 0.5; +*/ +gl_Position = vec4(position, 0.0, 1.0); +v_uv = uv; } )s"); @@ -175,24 +186,24 @@ void main() { "u_id", 3u ); - opengl::GlGeometry quad; + auto quad = renderer->add_mesh_geometry( resources::MeshData { resources::init_quad_t {} } ); Renderable obj1 { unif_in1.get(), - &quad, + quad.get(), true, true, }; Renderable obj2{ unif_in2.get(), - &quad, + quad.get(), true, true, }; Renderable obj3 { unif_in3.get(), - &quad, + quad.get(), true, true, }; @@ -206,7 +217,7 @@ void main() { auto color_texture_uniform = shader_display->new_uniform_input("color_texture", color_texture.get()); Renderable display_obj { color_texture_uniform.get(), - &quad, + quad.get(), false, false, }; @@ -231,11 +242,6 @@ void main() { glDepthRange(0.0, 1.0); // what is this glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - // TODO put in renderer - GLuint vao; - glGenVertexArrays(1, &vao); - glBindVertexArray(vao); // stores all the vertex attrib state.*/ }, // frame [&]() { diff --git a/libopenage/renderer/vertex_buffer.h b/libopenage/renderer/vertex_buffer.h index 4ef8e464be..7f259c42f6 100644 --- a/libopenage/renderer/vertex_buffer.h +++ b/libopenage/renderer/vertex_buffer.h @@ -1,13 +1,13 @@ // Copyright 2015-2015 the openage authors. See copying.md for legal info. -#ifndef OPENAGE_RENDERER_VERTEX_BUFFER_H_ -#define OPENAGE_RENDERER_VERTEX_BUFFER_H_ +#pragma once #include #include "buffer.h" #include "../datastructure/constexpr_map.h" + namespace openage { namespace renderer { @@ -128,5 +128,3 @@ class VertexBuffer { }; }} // openage::renderer - -#endif diff --git a/libopenage/renderer/vertex_state.h b/libopenage/renderer/vertex_state.h index b1e8a29dc7..2f4b99ed5e 100644 --- a/libopenage/renderer/vertex_state.h +++ b/libopenage/renderer/vertex_state.h @@ -1,7 +1,6 @@ // Copyright 2015-2015 the openage authors. See copying.md for legal info. -#ifndef OPENAGE_RENDERER_VERTEX_STATE_H_ -#define OPENAGE_RENDERER_VERTEX_STATE_H_ +#pragma once #include #include @@ -68,5 +67,3 @@ class VertexState { }; }} // openage::renderer - -#endif From 1b178ac292b98bdb87c58e11ded4b83253019dc7 Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Mon, 15 May 2017 15:53:41 +0100 Subject: [PATCH 45/52] renderer: delete old files --- libopenage/renderer/buffer.cpp | 39 -------- libopenage/renderer/buffer.h | 100 -------------------- libopenage/renderer/task.cpp | 24 ----- libopenage/renderer/task.h | 126 ------------------------- libopenage/renderer/vertex_buffer.cpp | 64 ------------- libopenage/renderer/vertex_buffer.h | 130 -------------------------- libopenage/renderer/vertex_state.cpp | 15 --- libopenage/renderer/vertex_state.h | 69 -------------- 8 files changed, 567 deletions(-) delete mode 100644 libopenage/renderer/buffer.cpp delete mode 100644 libopenage/renderer/buffer.h delete mode 100644 libopenage/renderer/task.cpp delete mode 100644 libopenage/renderer/task.h delete mode 100644 libopenage/renderer/vertex_buffer.cpp delete mode 100644 libopenage/renderer/vertex_buffer.h delete mode 100644 libopenage/renderer/vertex_state.cpp delete mode 100644 libopenage/renderer/vertex_state.h diff --git a/libopenage/renderer/buffer.cpp b/libopenage/renderer/buffer.cpp deleted file mode 100644 index 7319b9cdfa..0000000000 --- a/libopenage/renderer/buffer.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// 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}, - on_gpu{false} { - - if (size > 0) { - this->create(size); - } -} - -size_t Buffer::size() const { - return this->allocd; -} - -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; -} - -}} // openage::renderer diff --git a/libopenage/renderer/buffer.h b/libopenage/renderer/buffer.h deleted file mode 100644 index e132e6babb..0000000000 --- a/libopenage/renderer/buffer.h +++ /dev/null @@ -1,100 +0,0 @@ -// 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, - element_indices, - query, - }; - - enum class usage { - static_draw, - static_read, - stream_draw, - stream_read, - stream_copy, - static_copy, - dynamic_draw, - dynamic_read, - dynamic_copy, - }; - - std::shared_ptr wow; - 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(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. - */ - void create(size_t size); - - /** - * Returns the allocated buffer size. - */ - size_t size() const; - - /** - * 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 bind(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; - - /** - * True, when the buffer is present on the GPU. - */ - bool on_gpu; -}; - -}} // openage::renderer - -#endif diff --git a/libopenage/renderer/task.cpp b/libopenage/renderer/task.cpp deleted file mode 100644 index 4a8f170c3b..0000000000 --- a/libopenage/renderer/task.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. - -/** @file - * render task implementation to tell the renderer to - * perform any drawing on the screen. - */ - -#include "task.h" - - -namespace openage { -namespace renderer { - - -bool Task::operator <(const Task &other) const { - return true; -} - -TaskState::TaskState(Task *task, Renderer *renderer) - : - task{task}, - renderer{renderer} {} - -}} // openage::renderer diff --git a/libopenage/renderer/task.h b/libopenage/renderer/task.h deleted file mode 100644 index 397247b400..0000000000 --- a/libopenage/renderer/task.h +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. - -#ifndef OPENAGE_RENDERER_TASK_H_ -#define OPENAGE_RENDERER_TASK_H_ - -#include "../coord/phys3.h" -#include "../util/quaternion.h" - -namespace openage { -namespace renderer { - -class Geometry; -class Material; -class Renderer; - -class Uniform { -public: - - -private: - - void* value; -}; - -class UniformMap { -public: - static UniformMap defaults_from_shader(Shader const&); - - - void add_uniform(Uniform, value); - - void remove_uniform(Uniform); - - void set_uniform(); - -private: - map uniforms; - - shader; <- uniforms - - uniform values; <- pos, rot -}; - -/** - * Instructs the renderer to draw something. - */ -class Task { -public: - void set_geometry(Geometry const*); - - /** - * The concept of materials is as follows: - * - a material is defined as a mapping between shader uniforms and their values - * - more than one material can be applied to a model, provided there are no two materials map the same uniform to a value - * Materials can be, for example, used like this: - * one shared texture and color material between many similar-looking objects - * each object has its own transform matrix material - */ - void add_material(Material const*); - - - /** - * This immediately invalidates all attached materials and removes them. */ - void set_shader(Shader const*); - - /** - * This operation preserves all materials that fit the new shader. - */ - void set_shader_preserve_mappings(Shader const*); - - -private: - bool geometry_dirty; - /** - * 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; - - bool position_dirty; - /** - * Position to draw the geometry at. - */ - coord::phys3 position; - - bool rotation_dirty; - /** - * Rotation for the geometry. - */ - util::Quaternion<> rotation; - - /** - * Ordering condition to draw tasks in the correct order. - */ - bool operator <(const Task &other) const; -}; - - -/** - * corresponding state storage for a render task. - */ -class TaskState { -public: - TaskState(Task *t, Renderer *r); - ~TaskState(); - - /** - * true if the assigned task has been rendered on screen. - */ - bool rendered; - -protected: - Task *const task; - Renderer *const renderer; -}; - - -}} // openage::renderer - -#endif diff --git a/libopenage/renderer/vertex_buffer.cpp b/libopenage/renderer/vertex_buffer.cpp deleted file mode 100644 index 8c2ac79965..0000000000 --- a/libopenage/renderer/vertex_buffer.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. - -#include "vertex_buffer.h" - -namespace openage { -namespace renderer { - -/*VertexBuffer::VertexBuffer(Context *ctx, Buffer::usage usage) - : - usage{usage} { - - this->buffer = ctx->create_buffer(); -} -*/ -VertexBuffer::VertexBuffer(Buffer *bufferPtr, Buffer::usage usage) - : - usage{usage} { - - this->buffer = std::unique_ptr(bufferPtr); -} - -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() { - this->buffer->upload(Buffer::bind_target::vertex_attributes, this->usage); -} - -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(bool will_modify) { - return this->buffer->get(will_modify); -} - -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 deleted file mode 100644 index 7f259c42f6..0000000000 --- a/libopenage/renderer/vertex_buffer.h +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. - -#pragma once - -#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. - * A a vertex buffer supplies potentially different information - * for each vertex, e.g. its position, texture coordinates, color, etc. - * - * This is 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; //!< attribute layout id. - vertex_attribute_type type; //!< attribute data type - size_t dimension; //!< attribute dimension - size_t offset; //!< start offset in the buffer - size_t stride; //!< byte offset between consecutive attributes, 0 means tightly packed - }; - - //VertexBuffer(Context *ctx, - // Buffer::usage usage=Buffer::usage::static_draw); - - VertexBuffer(Buffer *bufferPtr, - 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 buffer slot. - */ - void upload(); - - /** - * 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(bool will_modify=false); - - /** - * 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; - - /** - * 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 deleted file mode 100644 index b7a6fb00bd..0000000000 --- a/libopenage/renderer/vertex_state.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// 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 deleted file mode 100644 index 2f4b99ed5e..0000000000 --- a/libopenage/renderer/vertex_state.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2015-2015 the openage authors. See copying.md for legal info. - -#pragma once - -#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. - * - * In OpenGL, this represents a vertex array object (vao). - * 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. - * - * This just enables the buffer to be used when this vertex - * state is active. - */ - virtual void attach_buffer(VertexBuffer &buf) = 0; - - /** - * Remove the attributes of the given vertex buffer from the - * active list. - */ - virtual void detach_buffer(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 From b0fa07fe89bedf8da9f1a883d8f4dcb31413fb22 Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Mon, 15 May 2017 15:54:30 +0100 Subject: [PATCH 46/52] renderer: shader improvements, uniform and attribute parsing --- libopenage/renderer/opengl/shader_program.cpp | 343 +++++++++--------- libopenage/renderer/opengl/shader_program.h | 65 ++-- libopenage/renderer/tests.cpp | 1 - 3 files changed, 197 insertions(+), 212 deletions(-) diff --git a/libopenage/renderer/opengl/shader_program.cpp b/libopenage/renderer/opengl/shader_program.cpp index f3cd2d37d7..4812e0898a 100644 --- a/libopenage/renderer/opengl/shader_program.cpp +++ b/libopenage/renderer/opengl/shader_program.cpp @@ -1,19 +1,11 @@ // Copyright 2013-2017 the openage authors. See copying.md for legal info. -#include "../../config.h" - #include "shader_program.h" -#include -#include -#include -#include - #include "../../error/error.h" #include "../../log/log.h" -#include "../../util/compiler.h" -#include "../../util/file.h" -#include "../../util/strings.h" +#include "../../datastructure/constexpr_map.h" + #include "texture.h" #include "geometry.h" @@ -22,126 +14,31 @@ namespace openage { namespace renderer { namespace opengl { -size_t uniform_size(gl_uniform_t type) { - switch (type) { - case gl_uniform_t::I32: - return sizeof(GLint); - case gl_uniform_t::U32: - return sizeof(GLuint); - case gl_uniform_t::F32: - return sizeof(float); - case gl_uniform_t::F64: - return sizeof(double); - case gl_uniform_t::V2F32: - return 2 * sizeof(float); - case gl_uniform_t::V3F32: - return 3 * sizeof(float); - case gl_uniform_t::V4F32: - return 4 * sizeof(float); - case gl_uniform_t::M3F32: - return 9 * sizeof(float); - case gl_uniform_t::M4F32: - return 16 * sizeof(float); - case gl_uniform_t::V2I32: - return 2 * sizeof(GLint); - case gl_uniform_t::V3I32: - return 3 * sizeof(GLint); - case gl_uniform_t::SAMPLER2D: - return sizeof(GLint); - default: - throw Error(MSG(err) << "Tried to find size of unknown GL uniform type."); - } -} - -static gl_uniform_t glsl_str_to_type(std::experimental::string_view str) { - if (str == "int") - return gl_uniform_t::I32; - else if (str == "uint") - return gl_uniform_t::U32; - else if (str == "float") - return gl_uniform_t::F32; - else if (str == "double") - return gl_uniform_t::F64; - else if (str == "vec2") - return gl_uniform_t::V2F32; - else if (str == "vec3") - return gl_uniform_t::V3F32; - else if (str == "vec4") - return gl_uniform_t::V4F32; - else if (str == "mat3") - return gl_uniform_t::M3F32; - else if (str == "mat4") - return gl_uniform_t::M4F32; - else if (str == "ivec2") - return gl_uniform_t::V2I32; - else if (str == "ivec3") - return gl_uniform_t::V3I32; - else if (str == "sampler2D") - return gl_uniform_t::SAMPLER2D; - else - throw Error(MSG(err) << "Unsupported GLSL uniform type " << str); -} - -void parse_glsl(std::map &uniforms, const char *code) { - // this will match all uniform declarations - std::regex const unif_r("uniform\\s+\\w+\\s+\\w+(?=\\s*;)"); - std::regex const word_r("\\w+"); - - const char *end = code; - while (*end != '\0') { - end += 1; - } - - auto unif_iter = std::cregex_iterator(code, end, unif_r); - auto unif_iter_end = std::cregex_iterator(); - for (; unif_iter != unif_iter_end; unif_iter++) { - std::string unif = (*unif_iter).str(); - - // remove "uniform" - unif = unif.substr(7); - - auto word_iter = std::sregex_iterator(unif.begin(), unif.end(), word_r); - - // first word is the type - gl_uniform_t type = glsl_str_to_type((*word_iter).str()); - - // second word is the uniform name - word_iter++; - - // second word is the uniform name - uniforms.insert(std::pair( - (*word_iter).str(), - GlUniform { - type, - 0, - } - )); - - } -} - -static GLuint src_type_to_gl(resources::shader_source_t type) { - using resources::shader_source_t; - - switch (type) { - case shader_source_t::glsl_vertex: - return GL_VERTEX_SHADER; - case shader_source_t::glsl_geometry: - return GL_GEOMETRY_SHADER; - case shader_source_t::glsl_tesselation_control: - return GL_TESS_CONTROL_SHADER; - case shader_source_t::glsl_tesselation_evaluation: - return GL_TESS_EVALUATION_SHADER; - case shader_source_t::glsl_fragment: - return GL_FRAGMENT_SHADER; - default: - throw Error(MSG(err) << "Unsupported GLSL shader type."); - } -} +static constexpr auto glsl_to_gl_type = datastructure::create_const_map( + std::make_pair("int", GL_INT), + std::make_pair("uint", GL_UNSIGNED_INT), + std::make_pair("float", GL_FLOAT), + std::make_pair("double", GL_DOUBLE), + std::make_pair("vec2", GL_FLOAT_VEC2), + std::make_pair("vec3", GL_FLOAT_VEC3), + std::make_pair("mat3", GL_FLOAT_MAT3), + std::make_pair("mat4", GL_FLOAT_MAT4), + std::make_pair("ivec2", GL_INT_VEC2), + std::make_pair("ivec3", GL_INT_VEC3), + std::make_pair("sampler2D", GL_SAMPLER_2D) +); + +static constexpr auto gl_shdr_type = datastructure::create_const_map( + std::make_pair(resources::shader_source_t::glsl_vertex, GL_VERTEX_SHADER), + std::make_pair(resources::shader_source_t::glsl_geometry, GL_GEOMETRY_SHADER), + std::make_pair(resources::shader_source_t::glsl_tesselation_control, GL_TESS_CONTROL_SHADER), + std::make_pair(resources::shader_source_t::glsl_tesselation_evaluation, GL_TESS_EVALUATION_SHADER), + std::make_pair(resources::shader_source_t::glsl_fragment, GL_FRAGMENT_SHADER) +); static GLuint compile_shader(const resources::ShaderSource& src) { // allocate shader in opengl - GLuint id = glCreateShader(src_type_to_gl(src.get_type())); + GLuint id = glCreateShader(gl_shdr_type.get(src.get_type())); if (unlikely(id == 0)) { throw Error{MSG(err) << "Unable to create OpenGL shader. WTF?!", true}; @@ -204,6 +101,32 @@ static void check_program_status(GLuint program, GLenum what_to_check) { } } +static constexpr auto gl_type_size = datastructure::create_const_map( + std::make_pair(GL_FLOAT, 4), + std::make_pair(GL_FLOAT_VEC2, 8), + std::make_pair(GL_FLOAT_VEC3, 12), + std::make_pair(GL_FLOAT_VEC4, 16), + std::make_pair(GL_INT, 4), + std::make_pair(GL_INT_VEC2, 8), + std::make_pair(GL_INT_VEC3, 12), + std::make_pair(GL_INT_VEC4, 16), + std::make_pair(GL_UNSIGNED_INT, 4), + std::make_pair(GL_UNSIGNED_INT_VEC2, 8), + std::make_pair(GL_UNSIGNED_INT_VEC3, 12), + std::make_pair(GL_UNSIGNED_INT_VEC4, 16), + std::make_pair(GL_BOOL, 1), + std::make_pair(GL_BOOL_VEC2, 2), + std::make_pair(GL_BOOL_VEC3, 3), + std::make_pair(GL_BOOL_VEC4, 4), + std::make_pair(GL_FLOAT_MAT2, 16), + std::make_pair(GL_FLOAT_MAT3, 36), + std::make_pair(GL_FLOAT_MAT4, 64), + std::make_pair(GL_SAMPLER_1D, 4), + std::make_pair(GL_SAMPLER_2D, 4), + std::make_pair(GL_SAMPLER_3D, 4), + std::make_pair(GL_SAMPLER_CUBE, 4) +); + GlShaderProgram::GlShaderProgram(const std::vector &srcs, const gl_context_capabilities &caps) { this->id = glCreateProgram(); @@ -213,12 +136,9 @@ GlShaderProgram::GlShaderProgram(const std::vector &src std::vector shaders; for (auto src : srcs) { - parse_glsl(this->uniforms, src.get_source()); - shaders.push_back(compile_shader(src)); - } - - for (GLuint shdr : shaders) { - glAttachShader(this->id, shdr); + GLuint id = compile_shader(src); + shaders.push_back(id); + glAttachShader(this->id, id); } glLinkProgram(this->id); @@ -233,40 +153,99 @@ GlShaderProgram::GlShaderProgram(const std::vector &src glDeleteShader(shdr); } - // uniforms that are present in the source but not in the program, because they were optimized out by OpenGL - std::vector optimized_away; - GLuint tex_unit = 0; + // query program information + GLint val; + glGetProgramiv(this->id, GL_ACTIVE_ATTRIBUTES, &val); + size_t attrib_count = val; + glGetProgramiv(this->id, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &val); + size_t attrib_maxlen = val; + glGetProgramiv(this->id, GL_ACTIVE_UNIFORMS, &val); + size_t unif_count = val; + glGetProgramiv(this->id, GL_ACTIVE_UNIFORM_MAX_LENGTH, &val); + size_t unif_maxlen = val; - // find the location of every uniform in the shader program - for (auto& pair : this->uniforms) { - GLint loc = glGetUniformLocation(this->id, pair.first.data()); + std::vector name(std::max(unif_maxlen, attrib_maxlen)); - if (loc == -1) { - log::log(MSG(warn) - << "OpenGL shader uniform " << pair.first << " was present in the source, but isn't present in the program. Probably optimized away."); - optimized_away.push_back(pair.first); - continue; + GLuint tex_unit = 0; + for (GLuint i_unif = 0; i_unif < unif_count; ++i_unif) { + GLint count; + GLenum type; + glGetActiveUniform( + this->id, + i_unif, + name.size(), + 0, + &count, + &type, + name.data() + ); + + this->uniforms.insert(std::make_pair( + name.data(), + GlUniform { + type, + GLint(i_unif), + size_t(count), + size_t(count) * gl_type_size.get(type), + } + )); + + if (count != 1) { + // TODO support them + log::log(MSG(warn) << "Found array uniform " << name.data() << " in shader. Arrays are unsupported."); } - pair.second.location = loc; - - if (pair.second.type == gl_uniform_t::SAMPLER2D) { + if (type == GL_SAMPLER_2D) { if (tex_unit >= caps.max_texture_slots) { throw Error(MSG(err) << "Tried to create shader that uses more texture sampler uniforms " << "than there are texture unit slots available."); } - this->texunits_per_unifs.insert(std::make_pair(pair.first, tex_unit)); + this->texunits_per_unifs.insert(std::make_pair(name.data(), tex_unit)); tex_unit += 1; } + + // TODO optimized away detection + if (0 == -1) { + log::log(MSG(warn) + << "OpenGL shader uniform " << name.data() << " was present in the source, but isn't present in the program. Probably optimized away."); + continue; + } } - // we only want valid uniforms to be present in the map - for (auto& unif : optimized_away) { - this->uniforms.erase(unif); + for (GLuint i_attrib = 0; i_attrib < attrib_count; ++i_attrib) { + GLint size; + GLenum type; + glGetActiveAttrib( + this->id, + i_attrib, + name.size(), + 0, + &size, + &type, + name.data() + ); + + this->attribs.insert(std::make_pair( + name.data(), + GlVertexAttrib { + type, + GLint(i_attrib), + size, + } + )); } log::log(MSG(info) << "Created OpenGL shader program"); + + log::log(MSG(dbg) << "Uniforms: "); + for (auto const &pair : this->uniforms) { + log::log(MSG(dbg) << "(" << pair.second.location << ") " << pair.first << ": " << pair.second.type); + } + log::log(MSG(dbg) << "Vertex attributes: "); + for (auto const &pair : this->attribs) { + log::log(MSG(dbg) << "(" << pair.second.location << ") " << pair.first << ": " << pair.second.type); + } } GlShaderProgram::GlShaderProgram(GlShaderProgram &&other) @@ -318,41 +297,41 @@ void GlShaderProgram::execute_with(const GlUniformInput *unif_in, const GlGeomet auto loc = this->uniforms[pair.first].location; switch (this->uniforms[pair.first].type) { - case gl_uniform_t::I32: + case GL_INT: glUniform1i(loc, *reinterpret_cast(ptr)); break; - case gl_uniform_t::U32: + case GL_UNSIGNED_INT: glUniform1ui(loc, *reinterpret_cast(ptr)); break; - case gl_uniform_t::F32: + case GL_FLOAT: glUniform1f(loc, *reinterpret_cast(ptr)); break; - case gl_uniform_t::F64: + case GL_DOUBLE: // TODO requires an extension glUniform1d(loc, *reinterpret_cast(ptr)); break; - case gl_uniform_t::V2F32: + case GL_FLOAT_VEC2: glUniform2fv(loc, 1, reinterpret_cast(ptr)); break; - case gl_uniform_t::V3F32: + case GL_FLOAT_VEC3: glUniform3fv(loc, 1, reinterpret_cast(ptr)); break; - case gl_uniform_t::V4F32: + case GL_FLOAT_VEC4: glUniform4fv(loc, 1, reinterpret_cast(ptr)); break; - case gl_uniform_t::M3F32: + case GL_FLOAT_MAT3: glUniformMatrix3fv(loc, 1, false, reinterpret_cast(ptr)); break; - case gl_uniform_t::M4F32: + case GL_FLOAT_MAT4: glUniformMatrix4fv(loc, 1, false, reinterpret_cast(ptr)); break; - case gl_uniform_t::V2I32: + case GL_INT_VEC2: glUniform2iv(loc, 1, reinterpret_cast(ptr)); break; - case gl_uniform_t::V3I32: + case GL_INT_VEC3: glUniform3iv(loc, 1, reinterpret_cast(ptr)); break; - case gl_uniform_t::SAMPLER2D: { + case GL_SAMPLER_2D: { GLuint tex_unit = this->texunits_per_unifs[pair.first]; GLuint tex = *reinterpret_cast(ptr); glActiveTexture(GL_TEXTURE0 + tex_unit); @@ -374,22 +353,29 @@ void GlShaderProgram::execute_with(const GlUniformInput *unif_in, const GlGeomet } std::unique_ptr GlShaderProgram::new_unif_in() { - GlUniformInput *in = new GlUniformInput; + auto in = std::make_unique(); in->program = this; - return std::unique_ptr(in); + return in; } bool GlShaderProgram::has_uniform(const char* name) { return this->uniforms.count(name) == 1; } -void GlShaderProgram::set_unif(UniformInput *in, const char *unif, void const* val) { +void GlShaderProgram::set_unif(UniformInput *in, const char *unif, void const* val, GLenum type) { GlUniformInput *unif_in = static_cast(in); - // will throw if uniform doesn't exist, that's ok - // TODO rethrow with nicer message? + + if (this->uniforms.count(unif) == 0) { + throw Error(MSG(err) << "Tried to set uniform " << unif << " that does not exist in the shader program."); + } + auto const& unif_data = this->uniforms.at(unif); - size_t size = uniform_size(unif_data.type); + if (unlikely(type != unif_data.type)) { + throw Error(MSG(err) << "Tried to set uniform " << unif << " to a value of the wrong type."); + } + + size_t size = gl_type_size.get(unif_data.type); if (unif_in->update_offs.count(unif) == 1) { // already wrote to this uniform since last upload @@ -405,41 +391,42 @@ void GlShaderProgram::set_unif(UniformInput *in, const char *unif, void const* v } void GlShaderProgram::set_i32(UniformInput *in, const char *unif, int32_t val) { - this->set_unif(in, unif, &val); + this->set_unif(in, unif, &val, GL_INT); } void GlShaderProgram::set_u32(UniformInput *in, const char *unif, uint32_t val) { - this->set_unif(in, unif, &val); + this->set_unif(in, unif, &val, GL_UNSIGNED_INT); } void GlShaderProgram::set_f32(UniformInput *in, const char *unif, float val) { - this->set_unif(in, unif, &val); + this->set_unif(in, unif, &val, GL_FLOAT); } void GlShaderProgram::set_f64(UniformInput *in, const char *unif, double val) { - this->set_unif(in, unif, &val); + // TODO requires extension + this->set_unif(in, unif, &val, GL_DOUBLE); } void GlShaderProgram::set_v2f32(UniformInput *in, const char *unif, Eigen::Vector2f const& val) { - this->set_unif(in, unif, &val); + this->set_unif(in, unif, &val, GL_FLOAT_VEC2); } void GlShaderProgram::set_v3f32(UniformInput *in, const char *unif, Eigen::Vector3f const& val) { - this->set_unif(in, unif, &val); + this->set_unif(in, unif, &val, GL_FLOAT_VEC3); } void GlShaderProgram::set_v4f32(UniformInput *in, const char *unif, Eigen::Vector4f const& val) { - this->set_unif(in, unif, &val); + this->set_unif(in, unif, &val, GL_FLOAT_VEC4); } void GlShaderProgram::set_m4f32(UniformInput *in, const char *unif, Eigen::Matrix4f const& val) { - this->set_unif(in, unif, val.data()); + this->set_unif(in, unif, val.data(), GL_FLOAT_MAT4); } void GlShaderProgram::set_tex(UniformInput *in, const char *unif, Texture const* val) { auto const& tex = *static_cast(val); GLuint handle = tex.get_handle(); - this->set_unif(in, unif, &handle); + this->set_unif(in, unif, &handle, GL_SAMPLER_2D); } }}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/shader_program.h b/libopenage/renderer/opengl/shader_program.h index 3a2c46117e..811f30718c 100644 --- a/libopenage/renderer/opengl/shader_program.h +++ b/libopenage/renderer/opengl/shader_program.h @@ -2,13 +2,15 @@ #pragma once -#include -#include #include +#include +#include + #include "../shader_program.h" #include "../resources/shader_source.h" #include "../renderer.h" + #include "uniform_input.h" #include "context.h" #include "geometry.h" @@ -18,32 +20,7 @@ namespace openage { namespace renderer { namespace opengl { -/// GLSL uniform types -enum class gl_uniform_t { - I32, - U32, - F32, - F64, - V2F32, - V3F32, - V4F32, - M3F32, - M4F32, - V2I32, - V3I32, - SAMPLER2D, -}; - -/// Returns the size in bytes of a GLSL uniform type -size_t uniform_size(gl_uniform_t); - -/// Represents a uniform location in the shader program -struct GlUniform { - gl_uniform_t type; - GLint location; -}; - -/// A handle to an OpenGL shader program +/// A handle to an OpenGL shader program. class GlShaderProgram final : public ShaderProgram { public: /// Tries to create a shader program from the given sources. @@ -69,7 +46,6 @@ class GlShaderProgram final : public ShaderProgram { bool has_uniform(const char*) override; protected: - void set_unif(UniformInput*, const char*, void const*); std::unique_ptr new_unif_in() override; void set_i32(UniformInput*, const char*, int32_t) override; void set_u32(UniformInput*, const char*, uint32_t) override; @@ -82,8 +58,31 @@ class GlShaderProgram final : public ShaderProgram { void set_tex(UniformInput*, const char*, Texture const*) override; private: - /// A map of uniform locations from their names - std::map uniforms; + void set_unif(UniformInput*, const char*, void const*, GLenum); + + /// Represents a uniform location in the shader program. + struct GlUniform { + GLenum type; + GLint location; + /// For arrays, the number of elements. For scalars, 1. + size_t count; + /// The size in bytes of the whole uniform (whole array if it's one). + size_t size; + }; + + /// Represents a per-vertex input to the shader program. + struct GlVertexAttrib { + GLenum type; + GLint location; + // TODO what is this? + GLint size; + }; + + /// A map of uniform names to their descriptions. + std::unordered_map uniforms; + + /// A map of per-vertex attribute names to their descriptions. + std::unordered_map attribs; // TODO parse uniform buffer structure ugh // std::unordered_map uniform_buffers; @@ -92,9 +91,9 @@ class GlShaderProgram final : public ShaderProgram { /// The GL shader program handle GLuint id; - /// A map from sampler uniform names to their assigned texture units + /// A map from sampler uniform names to their assigned texture units. std::unordered_map texunits_per_unifs; - /// A map from texture units to the texture handles that are currently bound to them + /// A map from texture units to the texture handles that are currently bound to them. std::unordered_map textures_per_texunits; }; diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index 1735690b6a..a87dff6226 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -73,7 +73,6 @@ void renderer_demo_0() { #version 330 layout(location=0) in vec2 position; -layout(location=1) in vec2 uv; uniform mat4 mvp; void main() { From eabebbb4d61c9d75574a7989532cead438421744 Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Mon, 15 May 2017 15:54:38 +0100 Subject: [PATCH 47/52] add self to mailmap --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index d99a4bb399..042023e657 100644 --- a/.mailmap +++ b/.mailmap @@ -13,3 +13,4 @@ Henry Snoek coop shell (Michael Enßlin, Jonas Jelten, Andre Kupka) Franz-Niclas Muschter Niklas Fiekas +Wojciech Nawrocki From 4037e6232b9a59f3742b12d293ba6e38f9a848ad Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Mon, 15 May 2017 17:53:57 +0100 Subject: [PATCH 48/52] renderer: fix IDs and add more textures support --- libopenage/renderer/opengl/texture.cpp | 36 +++++++++---------- libopenage/renderer/opengl/texture.h | 4 +-- .../renderer/resources/texture_info.cpp | 26 +++++++------- libopenage/renderer/resources/texture_info.h | 6 ++-- libopenage/renderer/tests.cpp | 4 +-- 5 files changed, 35 insertions(+), 41 deletions(-) diff --git a/libopenage/renderer/opengl/texture.cpp b/libopenage/renderer/opengl/texture.cpp index 412d4a8e70..824ad53728 100644 --- a/libopenage/renderer/opengl/texture.cpp +++ b/libopenage/renderer/opengl/texture.cpp @@ -3,9 +3,12 @@ #include "texture.h" #include + #include #include "../../error/error.h" +#include "../../datastructure/constexpr_map.h" + #include "../resources/texture_data.h" #include "render_target.h" @@ -14,23 +17,16 @@ namespace openage { namespace renderer { namespace opengl { -/// Returns the input and output formats for GL. -inline static std::tuple gl_format(resources::pixel_format fmt) { - switch (fmt) { - case resources::pixel_format::r16ui: - return std::make_tuple(GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT); - case resources::pixel_format::rgb8: - return std::make_tuple(GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE); - case resources::pixel_format::rgba8: - return std::make_tuple(GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE); - case resources::pixel_format::rgba8ui: - return std::make_tuple(GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE); - case resources::pixel_format::depth24: - return std::make_tuple(GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE); - default: - throw Error(MSG(err) << "invalid texture format passed to OpenGL."); - } -} +/// The input and output formats for GL. +static constexpr auto gl_format = datastructure::create_const_map>( + // TODO check correctness of formats here + std::make_pair(resources::pixel_format::r16ui, std::make_tuple(GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_INT)), + std::make_pair(resources::pixel_format::r32ui, std::make_tuple(GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT)), + std::make_pair(resources::pixel_format::rgb8, std::make_tuple(GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE)), + std::make_pair(resources::pixel_format::depth24, std::make_tuple(GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE)), + std::make_pair(resources::pixel_format::rgba8, std::make_tuple(GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE)), + std::make_pair(resources::pixel_format::rgba8ui, std::make_tuple(GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE)) +); GlTexture::GlTexture(const resources::TextureData& data) : Texture(data.get_info()) @@ -40,7 +36,7 @@ GlTexture::GlTexture(const resources::TextureData& data) glBindTexture(GL_TEXTURE_2D, *this->handle); // select pixel format - auto fmt_in_out = gl_format(this->info.get_format()); + auto fmt_in_out = gl_format.get(this->info.get_format()); // store raw pixels to gpu auto size = this->info.get_size(); @@ -65,7 +61,7 @@ GlTexture::GlTexture(const resources::TextureInfo &info) glGenTextures(1, &*this->handle); glBindTexture(GL_TEXTURE_2D, *this->handle); - auto fmt_in_out = gl_format(this->info.get_format()); + auto fmt_in_out = gl_format.get(this->info.get_format()); auto dims = this->info.get_size(); @@ -113,7 +109,7 @@ GLuint GlTexture::get_handle() const { } resources::TextureData GlTexture::into_data() { - auto fmt_in_out = gl_format(this->info.get_format()); + auto fmt_in_out = gl_format.get(this->info.get_format()); std::vector data(this->info.get_data_size()); glPixelStorei(GL_PACK_ALIGNMENT, this->info.get_row_alignment()); diff --git a/libopenage/renderer/opengl/texture.h b/libopenage/renderer/opengl/texture.h index 5ded8dd1df..9528a05d22 100644 --- a/libopenage/renderer/opengl/texture.h +++ b/libopenage/renderer/opengl/texture.h @@ -17,9 +17,7 @@ class TextureData; namespace opengl { -/** - * A handle to an OpenGL texture. - */ +/// A handle to an OpenGL texture. class GlTexture final : public Texture { public: /// Constructs a texture and fills it with the given data. diff --git a/libopenage/renderer/resources/texture_info.cpp b/libopenage/renderer/resources/texture_info.cpp index 510e859c25..dd791c973a 100644 --- a/libopenage/renderer/resources/texture_info.cpp +++ b/libopenage/renderer/resources/texture_info.cpp @@ -2,26 +2,24 @@ #include "texture_info.h" +#include "../../datastructure/constexpr_map.h" + namespace openage { namespace renderer { namespace resources { +static constexpr auto pix_size = datastructure::create_const_map( + std::make_pair(pixel_format::r16ui, 2), + std::make_pair(pixel_format::r32ui, 4), + std::make_pair(pixel_format::rgb8, 3), + std::make_pair(pixel_format::depth24, 3), + std::make_pair(pixel_format::rgba8, 4), + std::make_pair(pixel_format::rgba8ui, 4) +); + size_t pixel_size(pixel_format fmt) { - switch (fmt) { - case pixel_format::r16ui: - return 2; - case pixel_format::rgb8: - return 3; - case pixel_format::depth24: - return 3; - case pixel_format::rgba8: - return 4; - case pixel_format::rgba8ui: - return 4; - default: - throw Error(MSG(err) << "Unknown pixel format"); - } + return pix_size.get(fmt); } TextureInfo::TextureInfo(size_t width, size_t height, pixel_format fmt, size_t row_alignment, std::vector &&subs) diff --git a/libopenage/renderer/resources/texture_info.h b/libopenage/renderer/resources/texture_info.h index 77f424cf36..31f3a9f5d2 100644 --- a/libopenage/renderer/resources/texture_info.h +++ b/libopenage/renderer/resources/texture_info.h @@ -14,15 +14,17 @@ namespace resources { /// How the pixels are represented in a texture. enum class pixel_format { - /// 16 bits per pixel, unsigned integer + /// 16 bits per pixel, unsigned integer, single channel r16ui, + /// 32 bits per pixel, unsigned integer, single channel + r32ui, /// 24 bits per pixel, float rgb8, /// 24 bits per pixel, depth texture depth24, /// 32 bits per pixel, float, alpha channel rgba8, - /// 32 bits per pixel, unsigned integer + /// 32 bits per pixel, unsigned integer, alpha channel rgba8ui, }; diff --git a/libopenage/renderer/tests.cpp b/libopenage/renderer/tests.cpp index a87dff6226..66d7a6c568 100644 --- a/libopenage/renderer/tests.cpp +++ b/libopenage/renderer/tests.cpp @@ -209,7 +209,7 @@ void main() { auto size = window.get_size(); auto color_texture = renderer->add_texture(resources::TextureInfo(size.x, size.y, resources::pixel_format::rgba8)); - auto id_texture = renderer->add_texture(resources::TextureInfo(size.x, size.y, resources::pixel_format::rgba8ui)); + auto id_texture = renderer->add_texture(resources::TextureInfo(size.x, size.y, resources::pixel_format::r32ui)); auto depth_texture = renderer->add_texture(resources::TextureInfo(size.x, size.y, resources::pixel_format::depth24)); auto fbo = renderer->create_texture_target( { color_texture.get(), id_texture.get(), depth_texture.get() } ); @@ -257,7 +257,7 @@ void main() { // resize fbo color_texture = renderer->add_texture(resources::TextureInfo(new_size.x, new_size.y, resources::pixel_format::rgba8)); - id_texture = renderer->add_texture(resources::TextureInfo(new_size.x, new_size.y, resources::pixel_format::rgba8ui)); + id_texture = renderer->add_texture(resources::TextureInfo(new_size.x, new_size.y, resources::pixel_format::r32ui)); depth_texture = renderer->add_texture(resources::TextureInfo(new_size.x, new_size.y, resources::pixel_format::depth24)); fbo = renderer->create_texture_target( { color_texture.get(), id_texture.get(), depth_texture.get() } ); From 5a9d6be025dcdac32e4f2307809a3babf193c51f Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Mon, 15 May 2017 19:03:27 +0100 Subject: [PATCH 49/52] renderer: fix build --- libopenage/renderer/resources/mesh_data.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libopenage/renderer/resources/mesh_data.cpp b/libopenage/renderer/resources/mesh_data.cpp index 4b83ab817b..fdd95a3ca1 100644 --- a/libopenage/renderer/resources/mesh_data.cpp +++ b/libopenage/renderer/resources/mesh_data.cpp @@ -86,7 +86,7 @@ MeshData::MeshData(init_quad_t) { this->data = std::vector(data_size); std::memcpy(data.data(), reinterpret_cast(quad_data.data()), data_size); - this->info = { { vertex_input_t::V2F32, vertex_input_t::V2F32 }, vertex_layout_t::AOS, vertex_primitive_t::TRIANGLE_STRIP }; + this->info = VertexInputInfo { { vertex_input_t::V2F32, vertex_input_t::V2F32 }, vertex_layout_t::AOS, vertex_primitive_t::TRIANGLE_STRIP }; } std::vector const &MeshData::get_data() const { From 1593ff2edd1c50454c5e78965259b265079f283b Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Mon, 15 May 2017 19:23:58 +0100 Subject: [PATCH 50/52] renderer: add headers without implementations to CMakeLists to make QtCreator recognize them --- libopenage/renderer/CMakeLists.txt | 2 ++ libopenage/renderer/opengl/CMakeLists.txt | 2 ++ 2 files changed, 4 insertions(+) diff --git a/libopenage/renderer/CMakeLists.txt b/libopenage/renderer/CMakeLists.txt index babe75a043..b0be6529ae 100644 --- a/libopenage/renderer/CMakeLists.txt +++ b/libopenage/renderer/CMakeLists.txt @@ -1,6 +1,8 @@ add_sources(libopenage color.cpp geometry.cpp + renderer.h + shader_program.h tests.cpp text.cpp texture.cpp diff --git a/libopenage/renderer/opengl/CMakeLists.txt b/libopenage/renderer/opengl/CMakeLists.txt index 54043c475d..308d02e135 100644 --- a/libopenage/renderer/opengl/CMakeLists.txt +++ b/libopenage/renderer/opengl/CMakeLists.txt @@ -4,7 +4,9 @@ add_sources(libopenage geometry.cpp render_target.cpp renderer.cpp + scrapped.h shader_program.cpp texture.cpp + uniform_input.h vertex_array.cpp ) From 76ca26f07213b978d5b25e21e50e70fec1e60fd1 Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Wed, 17 May 2017 15:37:23 +0100 Subject: [PATCH 51/52] renderer: refactor --- libopenage/renderer/opengl/CMakeLists.txt | 3 + libopenage/renderer/opengl/buffer.cpp | 42 ++----- libopenage/renderer/opengl/buffer.h | 22 +--- libopenage/renderer/opengl/framebuffer.cpp | 50 ++++++++ libopenage/renderer/opengl/framebuffer.h | 32 +++++ libopenage/renderer/opengl/geometry.cpp | 4 +- libopenage/renderer/opengl/geometry.h | 7 +- libopenage/renderer/opengl/render_target.cpp | 67 ++-------- libopenage/renderer/opengl/render_target.h | 25 ++-- libopenage/renderer/opengl/renderer.cpp | 2 +- libopenage/renderer/opengl/renderer.h | 3 +- libopenage/renderer/opengl/shader.cpp | 54 ++++++++ libopenage/renderer/opengl/shader.h | 27 ++++ libopenage/renderer/opengl/shader_program.cpp | 115 ++++-------------- libopenage/renderer/opengl/shader_program.h | 17 +-- libopenage/renderer/opengl/simple_object.cpp | 41 +++++++ libopenage/renderer/opengl/simple_object.h | 47 +++++++ libopenage/renderer/opengl/texture.cpp | 35 +----- libopenage/renderer/opengl/texture.h | 29 +---- libopenage/renderer/opengl/uniform_input.h | 2 +- libopenage/renderer/opengl/vertex_array.cpp | 41 +++---- libopenage/renderer/opengl/vertex_array.h | 18 +-- 22 files changed, 343 insertions(+), 340 deletions(-) create mode 100644 libopenage/renderer/opengl/framebuffer.cpp create mode 100644 libopenage/renderer/opengl/framebuffer.h create mode 100644 libopenage/renderer/opengl/shader.cpp create mode 100644 libopenage/renderer/opengl/shader.h create mode 100644 libopenage/renderer/opengl/simple_object.cpp create mode 100644 libopenage/renderer/opengl/simple_object.h diff --git a/libopenage/renderer/opengl/CMakeLists.txt b/libopenage/renderer/opengl/CMakeLists.txt index 308d02e135..e1294e7393 100644 --- a/libopenage/renderer/opengl/CMakeLists.txt +++ b/libopenage/renderer/opengl/CMakeLists.txt @@ -1,11 +1,14 @@ add_sources(libopenage buffer.cpp context.cpp + framebuffer.cpp geometry.cpp render_target.cpp renderer.cpp scrapped.h + shader.cpp shader_program.cpp + simple_object.cpp texture.cpp uniform_input.h vertex_array.cpp diff --git a/libopenage/renderer/opengl/buffer.cpp b/libopenage/renderer/opengl/buffer.cpp index ef94f0a94c..5259cece12 100644 --- a/libopenage/renderer/opengl/buffer.cpp +++ b/libopenage/renderer/opengl/buffer.cpp @@ -10,49 +10,29 @@ namespace renderer { namespace opengl { GlBuffer::GlBuffer(size_t size, GLenum usage) - : size(size) + : GlSimpleObject([] (GLuint handle) { glDeleteBuffers(1, &handle); } ) + , size(size) , usage(usage) { - GLuint id; - glGenBuffers(1, &id); - this->id = id; + GLuint handle; + glGenBuffers(1, &handle); + this->handle = handle; this->bind(GL_ARRAY_BUFFER); glBufferData(GL_ARRAY_BUFFER, size, 0, usage); } GlBuffer::GlBuffer(const uint8_t *data, size_t size, GLenum usage) - : size(size) + : GlSimpleObject([] (GLuint handle) { glDeleteBuffers(1, &handle); } ) + , size(size) , usage(usage) { - GLuint id; - glGenBuffers(1, &id); - this->id = id; + GLuint handle; + glGenBuffers(1, &handle); + this->handle = handle; this->bind(GL_ARRAY_BUFFER); glBufferData(GL_ARRAY_BUFFER, size, data, usage); } -GlBuffer::GlBuffer(GlBuffer &&other) - : id(other.id) - , size(other.size) - , usage(other.usage) { - other.id = std::experimental::optional(); -} - -GlBuffer &GlBuffer::operator =(GlBuffer &&other) { - this->id = other.id; - this->size = other.size; - this->usage = other.usage; - other.id = std::experimental::optional(); - - return *this; -} - -GlBuffer::~GlBuffer() { - if (this->id) { - glDeleteBuffers(1, &this->id.value()); - } -} - size_t GlBuffer::get_size() const { return this->size; } @@ -67,7 +47,7 @@ void GlBuffer::upload_data(const uint8_t *data, size_t offset, size_t size) { } void GlBuffer::bind(GLenum target) const { - glBindBuffer(target, *this->id); + glBindBuffer(target, *this->handle); } }}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/buffer.h b/libopenage/renderer/opengl/buffer.h index 2fab37ab0d..8e37ae50db 100644 --- a/libopenage/renderer/opengl/buffer.h +++ b/libopenage/renderer/opengl/buffer.h @@ -2,17 +2,16 @@ #pragma once -#include - -#include +#include "simple_object.h" namespace openage { namespace renderer { namespace opengl { -/// OpenGL data buffer on the GPU. -class GlBuffer { +/// Represents an OpenGL buffer of memory +/// allocated on the GPU. +class GlBuffer final : public GlSimpleObject { public: /// Creates an empty buffer of the specified size. GlBuffer(size_t size, GLenum usage = GL_STATIC_DRAW); @@ -20,16 +19,6 @@ class GlBuffer { /// Creates a buffer of the specified size and fills it with the given data. GlBuffer(const uint8_t *data, size_t size, GLenum usage = GL_STATIC_DRAW); - ~GlBuffer(); - - /// Moving is supported. - GlBuffer &operator =(GlBuffer&&); - GlBuffer(GlBuffer&&); - - // TODO support copies - GlBuffer(const GlBuffer&) = delete; - GlBuffer &operator =(const GlBuffer&) = delete; - /// The size in bytes of this buffer. size_t get_size() const; @@ -41,9 +30,6 @@ class GlBuffer { void bind(GLenum target) const; private: - /// The OpenGL handle to this buffer. Can be empty if the object was moved out of. - std::experimental::optional id; - /// The size in bytes of this buffer. size_t size; diff --git a/libopenage/renderer/opengl/framebuffer.cpp b/libopenage/renderer/opengl/framebuffer.cpp new file mode 100644 index 0000000000..f975c01cb1 --- /dev/null +++ b/libopenage/renderer/opengl/framebuffer.cpp @@ -0,0 +1,50 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#include "framebuffer.h" + + +namespace openage { +namespace renderer { +namespace opengl { + +// TODO the validity of this object is contingent +// on its texture existing. use shared_ptr? +GlFramebuffer::GlFramebuffer(std::vector textures) + : GlSimpleObject([] (GLuint handle) { glDeleteFramebuffers(1, &handle); } ) +{ + GLuint handle; + glGenFramebuffers(1, &handle); + this->handle = handle; + + glBindFramebuffer(GL_FRAMEBUFFER, handle); + + std::vector drawBuffers; + + size_t colorTextureCount = 0; + for (size_t i = 0; i < textures.size(); i++) { + // TODO figure out attachment points from pixel formats + if (textures[i]->get_info().get_format() == resources::pixel_format::depth24) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, textures[i]->get_handle(), 0); + } else { + auto attachmentPoint = GL_COLOR_ATTACHMENT0 + colorTextureCount++; + glFramebufferTexture2D(GL_FRAMEBUFFER, attachmentPoint, GL_TEXTURE_2D, textures[i]->get_handle(), 0); + drawBuffers.push_back(attachmentPoint); + } + } + + glDrawBuffers(drawBuffers.size(), drawBuffers.data()); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + throw Error(MSG(err) << "Could not create OpenGL framebuffer."); + } +} + +void GlFramebuffer::bind_read() const { + glBindFramebuffer(GL_READ_FRAMEBUFFER, *this->handle); +} + +void GlFramebuffer::bind_write() const { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, *this->handle); +} + +}}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/framebuffer.h b/libopenage/renderer/opengl/framebuffer.h new file mode 100644 index 0000000000..65ffc82710 --- /dev/null +++ b/libopenage/renderer/opengl/framebuffer.h @@ -0,0 +1,32 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include + +#include "texture.h" +#include "simple_object.h" + + +namespace openage { +namespace renderer { +namespace opengl { + +/// Represents an OpenGL Framebuffer Object. +/// It is a collection of bitmap targets that can be drawn into +/// and read from. +class GlFramebuffer final : public GlSimpleObject { +public: + /// Construct a framebuffer pointing at the given textures. + /// Texture are attached to points specific to their pixel format, + /// e.g. a depth texture will be set as the depth target. + GlFramebuffer(std::vector textures); + + /// Bind this framebuffer to GL_READ_FRAMEBUFFER. + void bind_read() const; + + /// Bind this framebuffer to GL_DRAW_FRAMEBUFFER. + void bind_write() const; +}; + +}}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/geometry.cpp b/libopenage/renderer/opengl/geometry.cpp index 0c03c1ee2e..a41039f933 100644 --- a/libopenage/renderer/opengl/geometry.cpp +++ b/libopenage/renderer/opengl/geometry.cpp @@ -36,11 +36,11 @@ GlGeometry::GlGeometry(const resources::MeshData &mesh) this->mesh = GlMesh { std::move(verts), - {}, GlVertexArray (verts, mesh.get_info()), + {}, + {}, mesh.get_data().size() / mesh.get_info().vert_size(), gl_prim.get(mesh.get_info().get_primitive()), - {} }; if (mesh.get_ids()) { diff --git a/libopenage/renderer/opengl/geometry.h b/libopenage/renderer/opengl/geometry.h index 4c0a8d5091..b96fdfb3b3 100644 --- a/libopenage/renderer/opengl/geometry.h +++ b/libopenage/renderer/opengl/geometry.h @@ -24,20 +24,19 @@ class GlGeometry final : public Geometry { /// Initialize a meshed geometry. Relatively costly, has to initialize GL buffers and copy vertex data. explicit GlGeometry(resources::MeshData const&); - virtual ~GlGeometry() = default; - /// Executes a draw command for the geometry on the currently active context. /// Assumes bound and valid shader program and all other necessary state. void draw() const; private: + /// All the pieces of OpenGL state that represent a mesh. struct GlMesh { GlBuffer vertices; - std::experimental::optional indices; GlVertexArray vao; + std::experimental::optional indices; + std::experimental::optional index_type; size_t vert_count; GLenum primitive; - std::experimental::optional index_type; }; /// Data managing GPU memory and interpretation of mesh data. diff --git a/libopenage/renderer/opengl/render_target.cpp b/libopenage/renderer/opengl/render_target.cpp index 31b40136cc..ea35fb6644 100644 --- a/libopenage/renderer/opengl/render_target.cpp +++ b/libopenage/renderer/opengl/render_target.cpp @@ -8,66 +8,19 @@ namespace renderer { namespace opengl { GlRenderTarget::GlRenderTarget() - : type(gl_render_target_t::display) - , handle(0) {} + : type(gl_render_target_t::display) {} -// TODO the validity of this object is contingent -// on its texture existing. use shared_ptr? -GlRenderTarget::GlRenderTarget(std::vector textures) { - glGenFramebuffers(1, &*this->handle); - glBindFramebuffer(GL_FRAMEBUFFER, *this->handle); +GlRenderTarget::GlRenderTarget(std::vector textures) + : type(gl_render_target_t::textures) + , framebuffer(textures) {} - std::vector drawBuffers; - - size_t colorTextureCount = 0; - for (size_t i = 0; i < textures.size(); i++) { - // TODO figure out attachment points from pixel formats - if (textures[i]->get_info().get_format() == resources::pixel_format::depth24) { - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, textures[i]->get_handle(), 0); - } else { - auto attachmentPoint = GL_COLOR_ATTACHMENT0 + colorTextureCount++; - glFramebufferTexture2D(GL_FRAMEBUFFER, attachmentPoint, GL_TEXTURE_2D, textures[i]->get_handle(), 0); - drawBuffers.push_back(attachmentPoint); - } - } - - glDrawBuffers(drawBuffers.size(), drawBuffers.data()); - - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - throw Error(MSG(err) << "Could not create OpenGL framebuffer."); +void GlRenderTarget::bind_write() const { + if (this->type == gl_render_target_t::textures) { + this->framebuffer->bind_write(); } -} - -GlRenderTarget::GlRenderTarget(GlRenderTarget &&other) - : type(other.type) - , handle(other.handle) { - other.handle = std::experimental::optional(); -} - -GlRenderTarget &GlRenderTarget::operator =(GlRenderTarget&& other) { - if (this->type == gl_render_target_t::textures && this->handle) { - glDeleteFramebuffers(1, &*this->handle); + else { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } - - this->type = other.type; - this->handle = other.handle; - other.handle = std::experimental::optional(); - - return *this; -} - -GlRenderTarget::~GlRenderTarget() { - if (this->type == gl_render_target_t::textures && this->handle) { - glDeleteFramebuffers(1, &*this->handle); - } -} - -void GlRenderTarget::bind_read() const { - glBindFramebuffer(GL_READ_FRAMEBUFFER, *this->handle); -} - -void GlRenderTarget::bind_write() const { - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, *this->handle); } -}}} +}}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/render_target.h b/libopenage/renderer/opengl/render_target.h index c76533d5f4..8346d52803 100644 --- a/libopenage/renderer/opengl/render_target.h +++ b/libopenage/renderer/opengl/render_target.h @@ -6,7 +6,7 @@ #include "../renderer.h" #include "texture.h" -#include "../../error/error.h" +#include "framebuffer.h" namespace openage { @@ -22,37 +22,26 @@ enum class gl_render_target_t { // TODO renderbuffers mixed with textures }; +/// Represents an OpenGL target that can be drawn into. +/// It can be either a framebuffer or the display (the window). class GlRenderTarget final : public RenderTarget { public: /// Construct a render target pointed at the default framebuffer - the window. GlRenderTarget(); - ~GlRenderTarget(); /// Construct a render target pointing at the given textures. /// Texture are attached to points specific to their pixel format, /// e.g. a depth texture will be set as the depth target. GlRenderTarget(std::vector textures); - /// Cannot copy a framebuffer. - GlRenderTarget(const GlRenderTarget&) = delete; - GlRenderTarget &operator =(const GlRenderTarget&) = delete; - - /// Can move the object. - GlRenderTarget(GlRenderTarget&&); - GlRenderTarget &operator =(GlRenderTarget&&); - - /// Bind this framebuffer to GL_READ_FRAMEBUFFER. - void bind_read() const; - - /// Bind this framebuffer to GL_DRAW_FRAMEBUFFER. + /// Bind this render target to be drawn into. void bind_write() const; private: - /// The type. gl_render_target_t type; - /// The handle to the underlying OpenGL object. - std::experimental::optional handle; + /// For textures target type, the framebuffer. + std::experimental::optional framebuffer; }; -}}} +}}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/renderer.cpp b/libopenage/renderer/opengl/renderer.cpp index 2c3d852dd3..9fdd61aa2f 100644 --- a/libopenage/renderer/opengl/renderer.cpp +++ b/libopenage/renderer/opengl/renderer.cpp @@ -82,4 +82,4 @@ void GlRenderer::render(RenderPass const& pass) { } } -}}} +}}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/renderer.h b/libopenage/renderer/opengl/renderer.h index 262a246c9c..ae16f0adc8 100644 --- a/libopenage/renderer/opengl/renderer.h +++ b/libopenage/renderer/opengl/renderer.h @@ -16,6 +16,7 @@ namespace openage { namespace renderer { namespace opengl { +/// The OpenGL specialization of the rendering interface. class GlRenderer final : public Renderer { public: GlRenderer(GlContext*); @@ -34,7 +35,7 @@ class GlRenderer final : public Renderer { void render(RenderPass const&) override; private: - /* The GL context. */ + /// The GL context. GlContext *gl_context; std::vector shaders; diff --git a/libopenage/renderer/opengl/shader.cpp b/libopenage/renderer/opengl/shader.cpp new file mode 100644 index 0000000000..556bba6d54 --- /dev/null +++ b/libopenage/renderer/opengl/shader.cpp @@ -0,0 +1,54 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#include "shader.h" + +#include "../../datastructure/constexpr_map.h" + + +namespace openage { +namespace renderer { +namespace opengl { + +static constexpr auto gl_shdr_type = datastructure::create_const_map( + std::make_pair(resources::shader_source_t::glsl_vertex, GL_VERTEX_SHADER), + std::make_pair(resources::shader_source_t::glsl_geometry, GL_GEOMETRY_SHADER), + std::make_pair(resources::shader_source_t::glsl_tesselation_control, GL_TESS_CONTROL_SHADER), + std::make_pair(resources::shader_source_t::glsl_tesselation_evaluation, GL_TESS_EVALUATION_SHADER), + std::make_pair(resources::shader_source_t::glsl_fragment, GL_FRAGMENT_SHADER) +); + +GlShader::GlShader(const resources::ShaderSource &src) + : GlSimpleObject([] (GLuint handle) { glDeleteShader(handle); } ) + , type(gl_shdr_type.get(src.get_type())) { + + // allocate shader in opengl + GLuint handle = glCreateShader(this->type); + this->handle = handle; + + // load shader source + const char* data = src.get_source(); + glShaderSource(handle, 1, &data, 0); + + // compile shader source + glCompileShader(handle); + + // check compiliation result + GLint status; + glGetShaderiv(handle, GL_COMPILE_STATUS, &status); + + if (status != GL_TRUE) { + GLint loglen; + glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &loglen); + + std::vector infolog(loglen); + glGetShaderInfoLog(handle, loglen, 0, infolog.data()); + + throw Error(MSG(err) << "Failed to compiler shader:\n" << infolog.data() ); + } +} + +GLenum GlShader::get_type() const { + return this->type; +} + +}}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/shader.h b/libopenage/renderer/opengl/shader.h new file mode 100644 index 0000000000..974eee6d3e --- /dev/null +++ b/libopenage/renderer/opengl/shader.h @@ -0,0 +1,27 @@ +// Copyright 2017-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include "../resources/shader_source.h" +#include "simple_object.h" + + +namespace openage { +namespace renderer { +namespace opengl { + +/// A single OpenGL shader stage. +class GlShader final : public GlSimpleObject { +public: + /// Compiles the shader from the given source. + explicit GlShader(const resources::ShaderSource&); + + /// Returns the stage of the rendering pipeline this shader defines. + GLenum get_type() const; + +private: + /// Which stage of the rendering pipeline this shader defines. + GLenum type; +}; + +}}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/shader_program.cpp b/libopenage/renderer/opengl/shader_program.cpp index 4812e0898a..f15a9256a1 100644 --- a/libopenage/renderer/opengl/shader_program.cpp +++ b/libopenage/renderer/opengl/shader_program.cpp @@ -7,6 +7,7 @@ #include "../../datastructure/constexpr_map.h" #include "texture.h" +#include "shader.h" #include "geometry.h" @@ -28,51 +29,6 @@ static constexpr auto glsl_to_gl_type = datastructure::create_const_map( - std::make_pair(resources::shader_source_t::glsl_vertex, GL_VERTEX_SHADER), - std::make_pair(resources::shader_source_t::glsl_geometry, GL_GEOMETRY_SHADER), - std::make_pair(resources::shader_source_t::glsl_tesselation_control, GL_TESS_CONTROL_SHADER), - std::make_pair(resources::shader_source_t::glsl_tesselation_evaluation, GL_TESS_EVALUATION_SHADER), - std::make_pair(resources::shader_source_t::glsl_fragment, GL_FRAGMENT_SHADER) -); - -static GLuint compile_shader(const resources::ShaderSource& src) { - // allocate shader in opengl - GLuint id = glCreateShader(gl_shdr_type.get(src.get_type())); - - if (unlikely(id == 0)) { - throw Error{MSG(err) << "Unable to create OpenGL shader. WTF?!", true}; - } - - // load shader source - const char* data = src.get_source(); - glShaderSource(id, 1, &data, 0); - - // compile shader source - glCompileShader(id); - - // check compiliation result - GLint status; - glGetShaderiv(id, GL_COMPILE_STATUS, &status); - - if (status != GL_TRUE) { - GLint loglen; - glGetShaderiv(id, GL_INFO_LOG_LENGTH, &loglen); - - std::vector infolog(loglen); - glGetShaderInfoLog(id, loglen, 0, infolog.data()); - - auto errmsg = MSG(err); - errmsg << "Failed to compile shader:\n" << infolog.data(); - - glDeleteShader(id); - - throw Error{errmsg, true}; - } - - return id; -} - static void check_program_status(GLuint program, GLenum what_to_check) { GLint status; glGetProgramiv(program, what_to_check, &status); @@ -127,41 +83,38 @@ static constexpr auto gl_type_size = datastructure::create_const_map &srcs, const gl_context_capabilities &caps) { - this->id = glCreateProgram(); +GlShaderProgram::GlShaderProgram(const std::vector &srcs, const gl_context_capabilities &caps) + : GlSimpleObject([] (GLuint handle) { glDeleteProgram(handle); } ) { + GLuint handle = glCreateProgram(); + this->handle = handle; - if (unlikely(this->id == 0)) { - throw Error(MSG(err) << "Unable to create OpenGL shader program. WTF?!"); - } - - std::vector shaders; + std::vector shaders; for (auto src : srcs) { - GLuint id = compile_shader(src); - shaders.push_back(id); - glAttachShader(this->id, id); + GlShader shader(src); + glAttachShader(handle, shader.get_handle()); + shaders.push_back(std::move(shader)); } - glLinkProgram(this->id); - check_program_status(this->id, GL_LINK_STATUS); + glLinkProgram(handle); + check_program_status(handle, GL_LINK_STATUS); - glValidateProgram(this->id); - check_program_status(this->id, GL_VALIDATE_STATUS); + glValidateProgram(handle); + check_program_status(handle, GL_VALIDATE_STATUS); // after linking we can delete the shaders - for (GLuint shdr : shaders) { - glDetachShader(this->id, shdr); - glDeleteShader(shdr); + for (auto const& shdr : shaders) { + glDetachShader(handle, shdr.get_handle()); } // query program information GLint val; - glGetProgramiv(this->id, GL_ACTIVE_ATTRIBUTES, &val); + glGetProgramiv(handle, GL_ACTIVE_ATTRIBUTES, &val); size_t attrib_count = val; - glGetProgramiv(this->id, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &val); + glGetProgramiv(handle, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &val); size_t attrib_maxlen = val; - glGetProgramiv(this->id, GL_ACTIVE_UNIFORMS, &val); + glGetProgramiv(handle, GL_ACTIVE_UNIFORMS, &val); size_t unif_count = val; - glGetProgramiv(this->id, GL_ACTIVE_UNIFORM_MAX_LENGTH, &val); + glGetProgramiv(handle, GL_ACTIVE_UNIFORM_MAX_LENGTH, &val); size_t unif_maxlen = val; std::vector name(std::max(unif_maxlen, attrib_maxlen)); @@ -171,7 +124,7 @@ GlShaderProgram::GlShaderProgram(const std::vector &src GLint count; GLenum type; glGetActiveUniform( - this->id, + handle, i_unif, name.size(), 0, @@ -217,7 +170,7 @@ GlShaderProgram::GlShaderProgram(const std::vector &src GLint size; GLenum type; glGetActiveAttrib( - this->id, + handle, i_attrib, name.size(), 0, @@ -248,32 +201,8 @@ GlShaderProgram::GlShaderProgram(const std::vector &src } } -GlShaderProgram::GlShaderProgram(GlShaderProgram &&other) - : uniforms(std::move(other.uniforms)) - , id(other.id) { - other.id = 0; -} - -GlShaderProgram& GlShaderProgram::operator=(GlShaderProgram&& other) { - if (this->id != 0) { - glDeleteProgram(this->id); - } - - this->uniforms = std::move(other.uniforms); - this->id = other.id; - other.id = 0; - - return *this; -} - -GlShaderProgram::~GlShaderProgram() { - if (this->id != 0) { - glDeleteProgram(this->id); - } -} - void GlShaderProgram::use() const { - glUseProgram(this->id); + glUseProgram(*this->handle); for (auto const &pair : this->textures_per_texunits) { // We have to bind the texture to their texture units here because diff --git a/libopenage/renderer/opengl/shader_program.h b/libopenage/renderer/opengl/shader_program.h index 811f30718c..70b75eb52c 100644 --- a/libopenage/renderer/opengl/shader_program.h +++ b/libopenage/renderer/opengl/shader_program.h @@ -2,8 +2,6 @@ #pragma once -#include - #include #include @@ -14,6 +12,7 @@ #include "uniform_input.h" #include "context.h" #include "geometry.h" +#include "simple_object.h" namespace openage { @@ -21,20 +20,11 @@ namespace renderer { namespace opengl { /// A handle to an OpenGL shader program. -class GlShaderProgram final : public ShaderProgram { +class GlShaderProgram final : public ShaderProgram, public GlSimpleObject { public: /// Tries to create a shader program from the given sources. /// Throws an exception on compile/link errors. explicit GlShaderProgram(const std::vector&, const gl_context_capabilities&); - ~GlShaderProgram(); - - /// No copying. - GlShaderProgram(const GlShaderProgram&) = delete; - GlShaderProgram& operator=(const GlShaderProgram&) = delete; - - /// Moving is allowed. - GlShaderProgram(GlShaderProgram&&); - GlShaderProgram& operator=(GlShaderProgram&&); /// Bind this program as the currently used one in the OpenGL context. void use() const; @@ -88,9 +78,6 @@ class GlShaderProgram final : public ShaderProgram { // std::unordered_map uniform_buffers; // GlVertexInputInfo; - /// The GL shader program handle - GLuint id; - /// A map from sampler uniform names to their assigned texture units. std::unordered_map texunits_per_unifs; /// A map from texture units to the texture handles that are currently bound to them. diff --git a/libopenage/renderer/opengl/simple_object.cpp b/libopenage/renderer/opengl/simple_object.cpp new file mode 100644 index 0000000000..8a68ee171b --- /dev/null +++ b/libopenage/renderer/opengl/simple_object.cpp @@ -0,0 +1,41 @@ +// Copyright 2015-2017 the openage authors. See copying.md for legal info. + +#include "simple_object.h" + + +namespace openage { +namespace renderer { +namespace opengl { + +GlSimpleObject::GlSimpleObject(std::function delete_fun) + : delete_fun(delete_fun) {} + +GlSimpleObject::GlSimpleObject(GlSimpleObject&& other) + : handle(other.handle) + , delete_fun(std::move(other.delete_fun)) { + other.handle = {}; +} + +GlSimpleObject &GlSimpleObject::operator =(GlSimpleObject&& other) { + if (this->handle) { + this->delete_fun(*this->handle); + } + + this->handle = other.handle; + this->delete_fun = std::move(other.delete_fun); + other.handle = {}; + + return *this; +} + +GlSimpleObject::~GlSimpleObject() { + if (this->handle) { + this->delete_fun(*this->handle); + } +} + +GLuint GlSimpleObject::get_handle() const { + return *this->handle; +} + +}}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/simple_object.h b/libopenage/renderer/opengl/simple_object.h new file mode 100644 index 0000000000..a4d4fb18eb --- /dev/null +++ b/libopenage/renderer/opengl/simple_object.h @@ -0,0 +1,47 @@ +// Copyright 2015-2017 the openage authors. See copying.md for legal info. + +#pragma once + +#include +#include + +#include + + +namespace openage { +namespace renderer { +namespace opengl { + +/// A base class for all classes representing OpenGL Objects to inherit from. +/// It allows moving the object, but not copying it through the copy constructor. +/// It has unique_ptr-like semantics. It is called 'simple', because in the future +/// we might want to add collections of objects and similar more advanced features. +class GlSimpleObject { +public: + // Moving the representation is okay. + GlSimpleObject(GlSimpleObject&&); + GlSimpleObject &operator =(GlSimpleObject&&); + + // Generally, copying GL objects is costly and if we want to allow it, + // we do so through an explicit copy() function. + GlSimpleObject(GlSimpleObject const&) = delete; + GlSimpleObject &operator =(GlSimpleObject const&) = delete; + + /// Uses delete_fun to destroy the underlying object, + /// but only if the handle is present (hasn't been moved out). + virtual ~GlSimpleObject(); + + /// Returns the handle to the underlying OpenGL Object. + GLuint get_handle() const; + +protected: + explicit GlSimpleObject(std::function delete_fun); + + /// The handle to the OpenGL Object that this class represents. + std::experimental::optional handle; + + /// The function that deletes the underlying OpenGL Object. + std::function delete_fun; +}; + +}}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/texture.cpp b/libopenage/renderer/opengl/texture.cpp index 824ad53728..85bd231947 100644 --- a/libopenage/renderer/opengl/texture.cpp +++ b/libopenage/renderer/opengl/texture.cpp @@ -30,6 +30,7 @@ static constexpr auto gl_format = datastructure::create_const_maphandle); @@ -56,7 +57,9 @@ GlTexture::GlTexture(const resources::TextureData& data) } GlTexture::GlTexture(const resources::TextureInfo &info) - : Texture(info) { + : Texture(info) + , GlSimpleObject([] (GLuint handle) { glDeleteTextures(1, &handle); } ) +{ // generate opengl texture handle glGenTextures(1, &*this->handle); glBindTexture(GL_TEXTURE_2D, *this->handle); @@ -78,36 +81,6 @@ GlTexture::GlTexture(const resources::TextureInfo &info) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } -GlTexture::GlTexture(GlTexture &&other) - : Texture(std::move(other.info)) - , handle(std::move(other.handle)) { - // make the other handle empty - other.handle = std::experimental::optional(); -} - -GlTexture &GlTexture::operator =(GlTexture &&other) { - if (this->handle) { - glDeleteTextures(1, &*this->handle); - } - - this->info = std::move(other.info); - this->handle = std::move(other.handle); - // make the other handle empty - other.handle = std::experimental::optional(); - - return *this; -} - -GlTexture::~GlTexture() { - if (this->handle) { - glDeleteTextures(1, &*this->handle); - } -} - -GLuint GlTexture::get_handle() const { - return *this->handle; -} - resources::TextureData GlTexture::into_data() { auto fmt_in_out = gl_format.get(this->info.get_format()); std::vector data(this->info.get_data_size()); diff --git a/libopenage/renderer/opengl/texture.h b/libopenage/renderer/opengl/texture.h index 9528a05d22..a87342f49a 100644 --- a/libopenage/renderer/opengl/texture.h +++ b/libopenage/renderer/opengl/texture.h @@ -2,47 +2,26 @@ #pragma once -#include -#include - +#include "../resources/texture_data.h" #include "../texture.h" +#include "simple_object.h" namespace openage { namespace renderer { - -namespace resources { -class TextureData; -} - namespace opengl { /// A handle to an OpenGL texture. -class GlTexture final : public Texture { +class GlTexture final : public Texture, public GlSimpleObject { public: /// Constructs a texture and fills it with the given data. explicit GlTexture(const resources::TextureData&); /// Constructs an empty texture with the given parameters. GlTexture(resources::TextureInfo const&); - ~GlTexture(); - - // TODO support copying in the future - GlTexture(const GlTexture&) = delete; - GlTexture &operator =(const GlTexture&) = delete; - - /// Moving is supported. - GlTexture(GlTexture&&); - GlTexture &operator =(GlTexture&&); - - /// Returns the OpenGL handle to this texture. - GLuint get_handle() const; + /// Downloads the texture from the GPU and returns it as CPU-accessible data. resources::TextureData into_data() override; - -private: - /// The OpenGL handle to this texture. - std::experimental::optional handle; }; }}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/uniform_input.h b/libopenage/renderer/opengl/uniform_input.h index 2de0cd6573..09cd385f5f 100644 --- a/libopenage/renderer/opengl/uniform_input.h +++ b/libopenage/renderer/opengl/uniform_input.h @@ -29,4 +29,4 @@ struct GlUniformInput final : public UniformInput { std::vector update_data; }; -}}} +}}} // openage::renderer::opengl diff --git a/libopenage/renderer/opengl/vertex_array.cpp b/libopenage/renderer/opengl/vertex_array.cpp index 197a1798fc..889ec46b49 100644 --- a/libopenage/renderer/opengl/vertex_array.cpp +++ b/libopenage/renderer/opengl/vertex_array.cpp @@ -10,15 +10,18 @@ namespace openage { namespace renderer { namespace opengl { +/// The type of a single element in a per-vertex attribute. static constexpr auto gl_types = datastructure::create_const_map( std::make_pair(resources::vertex_input_t::V2F32, GL_FLOAT), std::make_pair(resources::vertex_input_t::V3F32, GL_FLOAT) ); -GlVertexArray::GlVertexArray(std::vector> buffers) { - GLuint id; - glGenVertexArrays(1, &id); - this->id = id; +GlVertexArray::GlVertexArray(std::vector> buffers) + : GlSimpleObject([] (GLuint handle) { glDeleteVertexArrays(1, &handle); } ) +{ + GLuint handle; + glGenVertexArrays(1, &handle); + this->handle = handle; this->bind(); @@ -78,32 +81,16 @@ GlVertexArray::GlVertexArray(std::vectorid = id; -} - -GlVertexArray::GlVertexArray(GlVertexArray &&other) - : id(other.id) { - other.id = {}; -} - -GlVertexArray &GlVertexArray::operator =(GlVertexArray &&other) { - this->id = other.id; - other.id = {}; - - return *this; -} - -GlVertexArray::~GlVertexArray() { - if (this->id) { - glDeleteVertexArrays(1, &this->id.value()); - } +GlVertexArray::GlVertexArray() + : GlSimpleObject([] (GLuint handle) { glDeleteVertexArrays(1, &handle); } ) +{ + GLuint handle; + glGenVertexArrays(1, &handle); + this->handle = handle; } void GlVertexArray::bind() const { - glBindVertexArray(*this->id); + glBindVertexArray(*this->handle); // TODO necessary? /* diff --git a/libopenage/renderer/opengl/vertex_array.h b/libopenage/renderer/opengl/vertex_array.h index 6b3d831c3e..f23a9a1e92 100644 --- a/libopenage/renderer/opengl/vertex_array.h +++ b/libopenage/renderer/opengl/vertex_array.h @@ -2,11 +2,9 @@ #pragma once -#include -#include - #include "../resources/mesh_data.h" #include "buffer.h" +#include "simple_object.h" namespace openage { @@ -15,7 +13,7 @@ namespace opengl { /// An OpenGL Vertex Array Object. It represents a mapping between a buffer /// that contains vertex data and a way for the context to interpret it. -class GlVertexArray { +class GlVertexArray final : public GlSimpleObject { public: /// Initializes a vertex array from the given mesh data. /// The inputs are parsed as follows - each subsequent (GlBuffer, VertexInputInfo) pair is added @@ -42,20 +40,8 @@ class GlVertexArray { /// This is useful for bufferless drawing. GlVertexArray(); - GlVertexArray(GlVertexArray&&); - GlVertexArray &operator =(GlVertexArray&&); - - GlVertexArray(GlVertexArray const&) = delete; - GlVertexArray &operator =(GlVertexArray const&) = delete; - - ~GlVertexArray(); - /// Make this vertex array object the current one. void bind() const; - -protected: - /// OpenGL handle to this object. - std::experimental::optional id; }; }}} // openage::renderer::opengl From d52cc0bedd9a68e125aca74cc13c94ea5a58d5d0 Mon Sep 17 00:00:00 2001 From: Wojciech Nawrocki Date: Fri, 19 May 2017 21:01:26 +0200 Subject: [PATCH 52/52] renderer: fix compiler errors after rebase --- doc/renderer/doc.md | 2 +- libopenage/renderer/color.h | 4 ---- openage/testing/testlist.py | 2 -- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/doc/renderer/doc.md b/doc/renderer/doc.md index 5caed73dbe..73cc8b2150 100644 --- a/doc/renderer/doc.md +++ b/doc/renderer/doc.md @@ -113,7 +113,7 @@ std::unique_ptr tex = renderer->add_texture(tex_data); resources::ShaderSource vsrc = resources::ShaderSource::read_from_file("/path.vert", resources::shader_t::glsl_vertex); resources::ShaderSource fsrc = resources::ShaderSource::read_from_file("/path.frag", resources::shader_t::glsl_fragment); - + std::unique_ptr prog = renderer->add_shader( { vsrc, fsrc } ); auto input = prog->new_uniform_input( diff --git a/libopenage/renderer/color.h b/libopenage/renderer/color.h index 586dd78567..e1edb57589 100644 --- a/libopenage/renderer/color.h +++ b/libopenage/renderer/color.h @@ -14,12 +14,8 @@ class Color { 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); - bool operator==(const Color &other) const; - bool operator ==(const Color &other) const; - bool operator !=(const Color &other) const; color_channel_t r; diff --git a/openage/testing/testlist.py b/openage/testing/testlist.py index a203b871b2..3241cbce8d 100644 --- a/openage/testing/testlist.py +++ b/openage/testing/testlist.py @@ -107,8 +107,6 @@ 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") def benchmark_cpp():