← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/use_image_cache into lp:widelands

 

SirVer has proposed merging lp:~widelands-dev/widelands/use_image_cache into lp:widelands with lp:~widelands-dev/widelands/full_texture_atlas as a prerequisite.

Commit message:
- Build a texture atlas for the GPU that Widelands is using. The atlas is cached in ~/.widelands/cache for the current build_id() and deleted and rebuild if the build_id() does not match. If the atlas is already available on disk, it is loaded directly after the graphics system is initialized. This makes startup slightly slower.
- Show a text while the graphics are loaded or the atlas is build. This required mild refactorings in the graphic initialization.
- Add a caching mechanism to remember OpenGL state like bound textures, framebuffers, and so on. Use this to avoid unneeded calls into the driver.
- Combine all different blit programs into one and use a if in the fragment shader. This allows for more batching, taking load from the CPU and transferring it to the GPU.

These are the improvements that this buys. All benchmarks where run at 60 FPS, 3440x1440 resolution & fullscreen, on a map with lots of buildings, lots of textures on the screen, lots of different map objects. The game was paused, so that no logic code ate CPU.

pre renderqueue (revision 7691): ~90% CPU load, 41000 OpenGL calls per frame.
post renderqueue (revision 7695): ~65% CPU load, 18300 OpenGL calls per frame.
This commit with a texture atlas of 2048x2048 (worst performance): 28% CPU load, 3000 OpenGL calls per frame.
This commit with a texture altas of 16384x16384 (best performance): 18% CPU load, 209 OpenGL calls.


Worst to best rendering now only takes 20% CPU and 0.5% GL calls of what it used to. There is no stutter due to image loading during play anymore, but startup is slower and there is a one time cost of building the atlas. 

Possible improvements: The atlas creating could also compress the images by finding unchanging areas in animation. This would drastically reduce the size of the atlas and the use of GPU memory. I am not motivated to work on this now though and I only consider it a nice to have. 

Requested reviews:
  Widelands Developers (widelands-dev)

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/use_image_cache/+merge/282106

Use the texture atlas + GL optimization. I am reasonably happy with the draw performance of Widelands now.


-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/use_image_cache into lp:widelands.
=== modified file 'src/editor/ui_menus/editor_tool_set_terrain_options_menu.cc'
--- src/editor/ui_menus/editor_tool_set_terrain_options_menu.cc	2016-01-08 21:00:39 +0000
+++ src/editor/ui_menus/editor_tool_set_terrain_options_menu.cc	2016-01-09 20:23:46 +0000
@@ -51,7 +51,7 @@
 	std::vector<std::string> tooltips;
 
 	// Blit the main terrain image
-	const Texture& terrain_texture = terrain_descr.get_texture(0);
+	const Image& terrain_texture = terrain_descr.get_texture(0);
 	Texture* texture = new Texture(terrain_texture.width(), terrain_texture.height());
 	texture->blit(Rect(0, 0, terrain_texture.width(), terrain_texture.height()),
 					  terrain_texture,

=== modified file 'src/graphic/CMakeLists.txt'
--- src/graphic/CMakeLists.txt	2016-01-04 20:54:08 +0000
+++ src/graphic/CMakeLists.txt	2016-01-09 20:23:46 +0000
@@ -3,19 +3,20 @@
 # TODO(sirver): Separate this directory into a base directory and one
 # that is Widelands aware (can include logic stuff).
 
-# A binary that creates and writes out a texture atlas of all images in
-# a directory.
-wl_binary(wl_make_texture_atlas
+wl_library(graphic_build_texture_atlas
   SRCS
-    make_texture_atlas_main.cc
+    build_texture_atlas.h
+    build_texture_atlas.cc
   DEPENDS
-    base_log
+    build_info
     graphic
     graphic_image_io
     graphic_texture_atlas
     helper
     io_filesystem
     io_stream
+    scripting_lua_interface
+    scripting_lua_table
 )
 
 wl_library(graphic_color
@@ -32,7 +33,6 @@
   DEPENDS
     base_exceptions
     base_geometry
-    base_log
     base_macros
     graphic_color
     graphic_terrain_programs
@@ -78,6 +78,10 @@
     graphic_image_io
     graphic_surface
     graphic_texture_atlas
+    io_fileread
+    io_filesystem
+    scripting_lua_table
+    scripting_lua_interface
 )
 
 wl_library(graphic_sdl_utils
@@ -128,6 +132,7 @@
 wl_library(graphic_draw_programs
   SRCS
     blend_mode.h
+    blit_mode.h
     gl/blit_program.cc
     gl/blit_program.h
     gl/draw_line_program.cc
@@ -242,10 +247,11 @@
     base_geometry
     base_i18n
     base_log
-    graphic_draw_programs
     base_macros
     build_info
+    graphic_build_texture_atlas
     graphic_color
+    graphic_draw_programs
     graphic_gl_utils
     graphic_image_cache
     graphic_image_io

=== added file 'src/graphic/blit_mode.h'
--- src/graphic/blit_mode.h	1970-01-01 00:00:00 +0000
+++ src/graphic/blit_mode.h	2016-01-09 20:23:46 +0000
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2006-2016 by the Widelands Development Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef WL_GRAPHIC_BLIT_MODE_H
+#define WL_GRAPHIC_BLIT_MODE_H
+
+// The type of blit performed.
+enum class BlitMode {
+	// Blit texture unchanged.
+	kDirect,
+
+	// Blit texture desaturated and maybe tinted with a color.
+	kMonochrome,
+
+	// Blit texture tinted with a color everywhere where a mask is not
+	// transparent,
+	kBlendedWithMask,
+};
+
+#endif  // end of include guard: WL_GRAPHIC_BLIT_MODE_H

=== renamed file 'src/graphic/make_texture_atlas_main.cc' => 'src/graphic/build_texture_atlas.cc'
--- src/graphic/make_texture_atlas_main.cc	2016-01-09 20:23:45 +0000
+++ src/graphic/build_texture_atlas.cc	2016-01-09 20:23:46 +0000
@@ -17,6 +17,8 @@
  *
  */
 
+#include "graphic/build_texture_atlas.h"
+
 #include <fstream>
 #include <iostream>
 #include <memory>
@@ -25,13 +27,10 @@
 #include <unordered_set>
 #include <vector>
 
-#include <SDL.h>
 #include <boost/algorithm/string/predicate.hpp>
 #include <boost/format.hpp>
 
-#undef main // No, we do not want SDL_main
-
-#include "base/log.h"
+#include "build_info.h"
 #include "config.h"
 #include "graphic/graphic.h"
 #include "graphic/image_io.h"
@@ -40,6 +39,8 @@
 #include "io/filesystem/filesystem.h"
 #include "io/filesystem/layered_filesystem.h"
 #include "io/streamwrite.h"
+#include "scripting/lua_interface.h"
+#include "scripting/lua_table.h"
 
 namespace {
 
@@ -47,6 +48,8 @@
 // threshold, but not background pictures.
 constexpr int kMaxAreaForTextureAtlas = 240 * 240;
 
+// A graphics card must at least support this size for texture for Widelands to
+// run.
 constexpr int kMinimumSizeForTextures = 2048;
 
 // An image can either be Type::kPacked inside a texture atlas, in which case
@@ -63,31 +66,6 @@
 	Rect rect;
 };
 
-int parse_arguments(
-   int argc, char** argv, int* max_size)
-{
-	if (argc < 2) {
-		std::cout << "Usage: wl_make_texture_atlas [max_size]" << std::endl << std::endl
-		          << "Will write output.png in the current directory." << std::endl;
-		return 1;
-	}
-	*max_size = atoi(argv[1]);
-	if (*max_size < kMinimumSizeForTextures) {
-		std::cout << "Widelands requires at least 2048 for the smallest texture size." << std::endl;
-		return 1;
-	}
-	return 0;
-}
-
-// Setup the static objects Widelands needs to operate and initializes systems.
-void initialize() {
-	SDL_Init(SDL_INIT_VIDEO);
-
-	g_fs = new LayeredFileSystem();
-	g_fs->add_file_system(&FileSystem::create(INSTALL_DATADIR));
-	g_gr = new Graphic(1, 1, false);
-}
-
 // Returns true if 'filename' is ends with a image extension.
 bool is_image(const std::string& filename) {
 	return boost::ends_with(filename, ".png") || boost::ends_with(filename, ".jpg");
@@ -116,16 +94,23 @@
 void dump_result(const std::map<std::string, PackInfo>& pack_info,
                  std::vector<std::unique_ptr<Texture>>* texture_atlases,
                  FileSystem* fs) {
+	// TODO(sirver): Maybe we want to use the cache directory for other things
+	// in the future too, so nuking it here is probably rather extreme.
+	if (fs->is_directory("cache")) {
+		fs->fs_unlink("cache");
+	}
+	fs->ensure_directory_exists("cache");
 
 	for (size_t i = 0; i < texture_atlases->size(); ++i) {
 		std::unique_ptr<StreamWrite> sw(
-		   fs->open_stream_write((boost::format("output_%02i.png") % i).str()));
+		   fs->open_stream_write((boost::format("cache/texture_atlas_%02i.png") % i).str()));
 		save_to_png(texture_atlases->at(i).get(), sw.get(), ColorType::RGBA);
 	}
 
 	{
-		std::unique_ptr<StreamWrite> sw(fs->open_stream_write("output.lua"));
+		std::unique_ptr<StreamWrite> sw(fs->open_stream_write("cache/texture_atlas.lua"));
 		sw->text("return {\n");
+		sw->text((boost::format("   build_id = \"%s\",\n") % build_id()).str());
 		for (const auto& pair : pack_info) {
 			sw->text("   [\"");
 			sw->text(pair.first);
@@ -200,18 +185,11 @@
 
 }  // namespace
 
-int main(int argc, char** argv) {
-	int max_size;
-	if (parse_arguments(argc, argv, &max_size))
-		return 1;
-
-	if (SDL_Init(SDL_INIT_VIDEO) < 0) {
-		std::cerr << "SDLInit did not succeed: " << SDL_GetError() << std::endl;
-		return 1;
+void make_texture_atlas(const int max_size) {
+	if (max_size < kMinimumSizeForTextures) {
+		throw wexception("The texture atlas must use at least %d as size (%d was given)",
+		                 kMinimumSizeForTextures, max_size);
 	}
-	initialize();
-
-
 	// For performance reasons, we need to have some images in the first texture
 	// atlas, so that OpenGL texture switches do not happen during (for example)
 	// terrain or road rendering. To ensure this, we separate all images into
@@ -242,9 +220,8 @@
 	auto first_texture_atlas = pack_images(images_that_must_be_in_first_atlas, max_size,
 	                                       &first_texture_atlas_pack_info, nullptr, nullptr);
 	if (first_texture_atlas.size() != 1) {
-		std::cout << "Not all images that should fit in the first texture atlas did actually fit."
-		          << std::endl;
-		return 1;
+		throw wexception("Not all images that should fit in the first texture atlas did actually "
+		                 "fit. Widelands has now more images than before.");
 	}
 
 	std::map<std::string, PackInfo> pack_info;
@@ -266,10 +243,16 @@
 
 	// Make sure we have all images.
 	assert(all_images.size() == pack_info.size());
-
-	std::unique_ptr<FileSystem> output_fs(&FileSystem::create("."));
-	dump_result(pack_info, &texture_atlases, output_fs.get());
-
-	SDL_Quit();
-	return 0;
+	dump_result(pack_info, &texture_atlases, g_fs);
+}
+
+bool is_texture_atlas_current() {
+	if (!g_fs->file_exists("cache/texture_atlas.lua")) {
+		return false;
+	}
+
+	LuaInterface lua;
+	auto config = lua.run_script("cache/texture_atlas.lua");
+	config->do_not_warn_about_unaccessed_keys();
+	return config->get_string("build_id") == build_id();
 }

=== modified file 'src/graphic/font_handler1.cc'
--- src/graphic/font_handler1.cc	2016-01-04 20:54:08 +0000
+++ src/graphic/font_handler1.cc	2016-01-09 20:23:46 +0000
@@ -139,8 +139,8 @@
 	ImageCache* const image_cache_;  // not owned
 };
 
-IFontHandler1 * create_fonthandler(Graphic* gr) {
-	return new FontHandler1(&gr->images());
+IFontHandler1 * create_fonthandler(ImageCache* image_cache) {
+	return new FontHandler1(image_cache);
 }
 
 IFontHandler1 * g_fh1 = nullptr;

=== modified file 'src/graphic/font_handler1.h'
--- src/graphic/font_handler1.h	2014-12-06 09:07:19 +0000
+++ src/graphic/font_handler1.h	2016-01-09 20:23:46 +0000
@@ -32,7 +32,7 @@
 
 class FileSystem;
 class Image;
-class Graphic;
+class ImageCache;
 
 namespace UI {
 
@@ -61,8 +61,8 @@
 	DISALLOW_COPY_AND_ASSIGN(IFontHandler1);
 };
 
-// Create a new FontHandler1. Ownership for the objects is not taken.
-IFontHandler1 * create_fonthandler(Graphic* gr);
+// Create a new FontHandler1.
+IFontHandler1 * create_fonthandler(ImageCache* image_cache);
 
 extern IFontHandler1 * g_fh1;
 

=== modified file 'src/graphic/gl/blit_program.cc'
--- src/graphic/gl/blit_program.cc	2016-01-05 11:28:54 +0000
+++ src/graphic/gl/blit_program.cc	2016-01-09 20:23:46 +0000
@@ -22,6 +22,7 @@
 #include <vector>
 
 #include "base/log.h"
+#include "graphic/blit_mode.h"
 #include "graphic/gl/blit_data.h"
 #include "graphic/gl/coordinate_conversion.h"
 #include "graphic/gl/utils.h"
@@ -36,52 +37,23 @@
 attribute vec2 attr_texture_position;
 attribute vec3 attr_position;
 attribute vec4 attr_blend;
+attribute float attr_program_flavor;
 
 varying vec2 out_mask_texture_coordinate;
 varying vec2 out_texture_coordinate;
 varying vec4 out_blend;
+varying float out_program_flavor;
 
 void main() {
 	out_mask_texture_coordinate = attr_mask_texture_position;
 	out_texture_coordinate = attr_texture_position;
 	out_blend = attr_blend;
+	out_program_flavor = attr_program_flavor;
 	gl_Position = vec4(attr_position, 1.);
 }
 )";
 
-const char kVanillaBlitFragmentShader[] = R"(
-#version 120
-
-uniform sampler2D u_texture;
-
-varying vec2 out_texture_coordinate;
-varying vec4 out_blend;
-
-void main() {
-	vec4 color = texture2D(u_texture, out_texture_coordinate);
-	gl_FragColor = color * out_blend;
-}
-)";
-
-const char kMonochromeBlitFragmentShader[] = R"(
-#version 120
-
-uniform sampler2D u_texture;
-
-varying vec2 out_texture_coordinate;
-varying vec4 out_blend;
-
-void main() {
-	vec4 texture_color = texture2D(u_texture, out_texture_coordinate);
-
-	// See http://en.wikipedia.org/wiki/YUV.
-	float luminance = dot(vec3(0.299, 0.587, 0.114), texture_color.rgb);
-
-	gl_FragColor = vec4(vec3(luminance) * out_blend.rgb, out_blend.a * texture_color.a);
-}
-)";
-
-const char kBlendedBlitFragmentShader[] = R"(
+const char kBlitFragmentShader[] = R"(
 #version 120
 
 uniform sampler2D u_texture;
@@ -90,16 +62,25 @@
 varying vec2 out_mask_texture_coordinate;
 varying vec2 out_texture_coordinate;
 varying vec4 out_blend;
+varying float out_program_flavor;
 
 void main() {
 	vec4 texture_color = texture2D(u_texture, out_texture_coordinate);
-	vec4 mask_color = texture2D(u_mask, out_mask_texture_coordinate);
 
 	// See http://en.wikipedia.org/wiki/YUV.
 	float luminance = dot(vec3(0.299, 0.587, 0.114), texture_color.rgb);
-	float blend_influence = mask_color.r * mask_color.a;
-	gl_FragColor = vec4(
-		mix(texture_color.rgb, out_blend.rgb * luminance, blend_influence), out_blend.a * texture_color.a);
+
+	if (out_program_flavor == 0.) {
+		gl_FragColor = vec4(texture_color.rgb, out_blend.a * texture_color.a);
+	} else if (out_program_flavor == 1.) {
+		gl_FragColor = vec4(vec3(luminance) * out_blend.rgb, out_blend.a * texture_color.a);
+	} else {
+		vec4 mask_color = texture2D(u_mask, out_mask_texture_coordinate);
+		float blend_influence = mask_color.r * mask_color.a;
+		gl_FragColor = vec4(
+			mix(texture_color.rgb, out_blend.rgb * luminance, blend_influence),
+				out_blend.a * texture_color.a);
+	}
 }
 )";
 
@@ -117,104 +98,31 @@
 
 }  // namespace
 
-class BlitProgram {
-public:
-	struct Arguments {
-		FloatRect destination_rect;
-		float z_value;
-		BlitData texture;
-		BlitData mask;
-		RGBAColor blend;
-		BlendMode blend_mode;
-	};
-	BlitProgram(const std::string& fragment_shader);
-
-	void activate();
-
-	void draw_and_deactivate(const std::vector<Arguments>& arguments);
-
-	int program_object() const {
-		return gl_program_.object();
-	}
-
-private:
-	struct PerVertexData {
-		PerVertexData(float init_gl_x,
-		              float init_gl_y,
-		              float init_gl_z,
-		              float init_texture_x,
-		              float init_texture_y,
-		              float init_mask_texture_x,
-		              float init_mask_texture_y,
-		              float init_blend_r,
-		              float init_blend_g,
-		              float init_blend_b,
-		              float init_blend_a)
-		   : gl_x(init_gl_x),
-		     gl_y(init_gl_y),
-		     gl_z(init_gl_z),
-		     texture_x(init_texture_x),
-		     texture_y(init_texture_y),
-		     mask_texture_x(init_mask_texture_x),
-		     mask_texture_y(init_mask_texture_y),
-		     blend_r(init_blend_r),
-		     blend_g(init_blend_g),
-		     blend_b(init_blend_b),
-		     blend_a(init_blend_a) {
-		}
-
-		float gl_x, gl_y, gl_z;
-		float texture_x, texture_y;
-		float mask_texture_x, mask_texture_y;
-		float blend_r, blend_g, blend_b, blend_a;
-	};
-	static_assert(sizeof(PerVertexData) == 44, "Wrong padding.");
-
-	// The buffer that will contain the quad for rendering.
-	Gl::Buffer<PerVertexData> gl_array_buffer_;
-
-	// The program.
-	Gl::Program gl_program_;
-
-	// Attributes.
-	GLint attr_blend_;
-	GLint attr_mask_texture_position_;
-	GLint attr_position_;
-	GLint attr_texture_position_;
-
-	// Uniforms.
-	GLint u_texture_;
-	GLint u_mask_;
-
-	// Cached for efficiency.
-	std::vector<PerVertexData> vertices_;
-
-	DISALLOW_COPY_AND_ASSIGN(BlitProgram);
-};
-
-BlitProgram::BlitProgram(const std::string& fragment_shader) {
-	gl_program_.build(kBlitVertexShader, fragment_shader.c_str());
+BlitProgram::BlitProgram() {
+	gl_program_.build(kBlitVertexShader, kBlitFragmentShader);
 
 	attr_blend_ = glGetAttribLocation(gl_program_.object(), "attr_blend");
 	attr_mask_texture_position_ = glGetAttribLocation(gl_program_.object(), "attr_mask_texture_position");
 	attr_position_ = glGetAttribLocation(gl_program_.object(), "attr_position");
 	attr_texture_position_ = glGetAttribLocation(gl_program_.object(), "attr_texture_position");
+	attr_program_flavor_ = glGetAttribLocation(gl_program_.object(), "attr_program_flavor");
 
 	u_texture_ = glGetUniformLocation(gl_program_.object(), "u_texture");
 	u_mask_ = glGetUniformLocation(gl_program_.object(), "u_mask");
 }
 
-void BlitProgram::activate() {
+BlitProgram::~BlitProgram() {}
+
+void BlitProgram::draw(const std::vector<Arguments>& arguments) {
 	glUseProgram(gl_program_.object());
 
-	glEnableVertexAttribArray(attr_blend_);
-	glEnableVertexAttribArray(attr_mask_texture_position_);
-	glEnableVertexAttribArray(attr_position_);
-	glEnableVertexAttribArray(attr_texture_position_);
-}
+	auto& gl_state = Gl::State::instance();
 
-void BlitProgram::draw_and_deactivate(const std::vector<Arguments>& arguments) {
-	size_t i = 0;
+	gl_state.enable_vertex_attrib_array({attr_blend_,
+	                                   attr_mask_texture_position_,
+	                                   attr_position_,
+	                                   attr_texture_position_,
+	                                   attr_program_flavor_});
 
 	gl_array_buffer_.bind();
 
@@ -226,6 +134,8 @@
 	Gl::vertex_attrib_pointer(attr_position_, 3, sizeof(PerVertexData), offsetof(PerVertexData, gl_x));
 	Gl::vertex_attrib_pointer(
 	   attr_texture_position_, 2, sizeof(PerVertexData), offsetof(PerVertexData, texture_x));
+	Gl::vertex_attrib_pointer(
+	   attr_program_flavor_, 1, sizeof(PerVertexData), offsetof(PerVertexData, program_flavor));
 
 	glUniform1i(u_texture_, 0);
 	glUniform1i(u_mask_, 1);
@@ -234,15 +144,18 @@
 	std::vector<DrawBatch> draw_batches;
 	int offset = 0;
 	vertices_.clear();
+
+	size_t i = 0;
 	while (i < arguments.size()) {
-		const Arguments& template_args = arguments[i];
+		const auto& template_args = arguments[i];
 
 		// Batch common blit operations up.
 		while (i < arguments.size()) {
-			const Arguments& current_args = arguments[i];
+			const auto& current_args = arguments[i];
 			if (current_args.blend_mode != template_args.blend_mode ||
-					current_args.texture.texture_id != template_args.texture.texture_id ||
-					current_args.mask.texture_id != template_args.mask.texture_id) {
+			    current_args.texture.texture_id != template_args.texture.texture_id ||
+			    (current_args.mask.texture_id != 0 &&
+			     current_args.mask.texture_id != template_args.mask.texture_id)) {
 				break;
 			}
 
@@ -253,56 +166,71 @@
 
 			const FloatRect texture_rect = to_gl_texture(current_args.texture);
 			const FloatRect mask_rect = to_gl_texture(current_args.mask);
-			vertices_.emplace_back(current_args.destination_rect.x,
-					current_args.destination_rect.y,
-					current_args.z_value,
-					texture_rect.x,
-					texture_rect.y,
-					mask_rect.x,
-					mask_rect.y,
-					blend_r,
-					blend_g,
-					blend_b,
-					blend_a);
-
-			vertices_.emplace_back(current_args.destination_rect.x + current_args.destination_rect.w,
-					current_args.destination_rect.y,
-					current_args.z_value,
-					texture_rect.x + texture_rect.w,
-					texture_rect.y,
-					mask_rect.x + mask_rect.w,
-					mask_rect.y,
-					blend_r,
-					blend_g,
-					blend_b,
-					blend_a);
-
-			vertices_.emplace_back(current_args.destination_rect.x,
-					current_args.destination_rect.y + current_args.destination_rect.h,
-					current_args.z_value,
-					texture_rect.x,
-					texture_rect.y + texture_rect.h,
-					mask_rect.x,
-					mask_rect.y + mask_rect.h,
-					blend_r,
-					blend_g,
-					blend_b,
-					blend_a);
-
-			vertices_.emplace_back(vertices_.at(vertices_.size() - 2));
-			vertices_.emplace_back(vertices_.at(vertices_.size() - 2));
-
-			vertices_.emplace_back(current_args.destination_rect.x + current_args.destination_rect.w,
-					current_args.destination_rect.y + current_args.destination_rect.h,
-					current_args.z_value,
-					texture_rect.x + texture_rect.w,
-					texture_rect.y + texture_rect.h,
-					mask_rect.x + mask_rect.w,
-					mask_rect.y + mask_rect.h,
-					blend_r,
-					blend_g,
-					blend_b,
-					blend_a);
+			float program_flavor;
+			switch (current_args.blit_mode) {
+				case BlitMode::kDirect:
+					program_flavor = 0.;
+					break;
+
+				case BlitMode::kMonochrome:
+					program_flavor = 1.;
+					break;
+
+				case BlitMode::kBlendedWithMask:
+					program_flavor = 2.;
+					break;
+			}
+
+			vertices_.emplace_back(current_args.destination_rect.x,
+					current_args.destination_rect.y,
+					current_args.z_value,
+					texture_rect.x,
+					texture_rect.y,
+					mask_rect.x,
+					mask_rect.y,
+					blend_r,
+					blend_g,
+					blend_b,
+					blend_a, program_flavor);
+
+			vertices_.emplace_back(current_args.destination_rect.x + current_args.destination_rect.w,
+					current_args.destination_rect.y,
+					current_args.z_value,
+					texture_rect.x + texture_rect.w,
+					texture_rect.y,
+					mask_rect.x + mask_rect.w,
+					mask_rect.y,
+					blend_r,
+					blend_g,
+					blend_b,
+					blend_a, program_flavor);
+
+			vertices_.emplace_back(current_args.destination_rect.x,
+					current_args.destination_rect.y + current_args.destination_rect.h,
+					current_args.z_value,
+					texture_rect.x,
+					texture_rect.y + texture_rect.h,
+					mask_rect.x,
+					mask_rect.y + mask_rect.h,
+					blend_r,
+					blend_g,
+					blend_b,
+					blend_a, program_flavor);
+
+			vertices_.emplace_back(vertices_.at(vertices_.size() - 2));
+			vertices_.emplace_back(vertices_.at(vertices_.size() - 2));
+
+			vertices_.emplace_back(current_args.destination_rect.x + current_args.destination_rect.w,
+					current_args.destination_rect.y + current_args.destination_rect.h,
+					current_args.z_value,
+					texture_rect.x + texture_rect.w,
+					texture_rect.y + texture_rect.h,
+					mask_rect.x + mask_rect.w,
+					mask_rect.y + mask_rect.h,
+					blend_r,
+					blend_g,
+					blend_b,
+					blend_a, program_flavor);
 			++i;
 		}
 
@@ -316,12 +244,11 @@
 	gl_array_buffer_.update(vertices_);
 
 	// Now do the draw calls.
+	int last = 0;
 	for (const auto& draw_arg : draw_batches) {
-		glActiveTexture(GL_TEXTURE0);
-		glBindTexture(GL_TEXTURE_2D, draw_arg.texture);
-
-		glActiveTexture(GL_TEXTURE1);
-		glBindTexture(GL_TEXTURE_2D, draw_arg.mask);
+		gl_state.bind(GL_TEXTURE0, draw_arg.texture);
+		last = draw_arg.texture;
+		gl_state.bind(GL_TEXTURE1, draw_arg.mask);
 
 		if (draw_arg.blend_mode == BlendMode::Copy) {
 			glBlendFunc(GL_ONE, GL_ZERO);
@@ -332,130 +259,38 @@
 			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 		}
 	}
-
-	glDisableVertexAttribArray(attr_blend_);
-	glDisableVertexAttribArray(attr_mask_texture_position_);
-	glDisableVertexAttribArray(attr_position_);
-	glDisableVertexAttribArray(attr_texture_position_);
-
-	glBindTexture(GL_TEXTURE_2D, 0);
-
-	glActiveTexture(GL_TEXTURE0);
-	glBindTexture(GL_TEXTURE_2D, 0);
-
-	glBindBuffer(GL_ARRAY_BUFFER, 0);
-}
-
-// static
-VanillaBlitProgram& VanillaBlitProgram::instance() {
-	static VanillaBlitProgram blit_program;
-	return blit_program;
-}
-
-VanillaBlitProgram::~VanillaBlitProgram() {
-}
-
-VanillaBlitProgram::VanillaBlitProgram() {
-	blit_program_.reset(new BlitProgram(kVanillaBlitFragmentShader));
-}
-
-void VanillaBlitProgram::draw(const FloatRect& gl_dest_rect,
+}
+
+void BlitProgram::draw(const FloatRect& gl_dest_rect,
                               const float z_value,
 										const BlitData& texture,
-                              const float opacity,
-                              const BlendMode blend_mode) {
-	draw({Arguments{gl_dest_rect, z_value, texture, opacity, blend_mode}});
-}
-
-void VanillaBlitProgram::draw(const std::vector<Arguments>& arguments) {
-	std::vector<BlitProgram::Arguments> blit_arguments;
-	for (const Arguments arg : arguments) {
-		blit_arguments.emplace_back(BlitProgram::Arguments{
-		   arg.destination_rect,
-		   arg.z_value,
-		   arg.texture,
-		   BlitData{0, 0, 0, Rect()},
-		   RGBAColor(255, 255, 255, arg.opacity * 255),
-		   arg.blend_mode,
-		});
-	}
-
-	blit_program_->activate();
-	blit_program_->draw_and_deactivate(blit_arguments);
-}
-
-
-// static
-MonochromeBlitProgram& MonochromeBlitProgram::instance() {
-	static MonochromeBlitProgram blit_program;
-	return blit_program;
-}
-
-MonochromeBlitProgram::~MonochromeBlitProgram() {
-}
-
-MonochromeBlitProgram::MonochromeBlitProgram() {
-	blit_program_.reset(new BlitProgram(kMonochromeBlitFragmentShader));
-}
-
-void MonochromeBlitProgram::draw(const FloatRect& dest_rect,
+										const BlitData& mask,
+                              const RGBAColor& blend,
+										const BlendMode& blend_mode) {
+	draw({Arguments{gl_dest_rect,
+	                z_value,
+	                texture,
+	                mask,
+	                blend,
+	                blend_mode,
+	                mask.texture_id != 0 ? BlitMode::kBlendedWithMask : BlitMode::kDirect}});
+}
+
+void BlitProgram::draw_monochrome(const FloatRect& dest_rect,
                                  const float z_value,
 											const BlitData& texture,
                                  const RGBAColor& blend) {
-	draw({Arguments{dest_rect, z_value, texture, blend, BlendMode::UseAlpha}});
-}
-
-void MonochromeBlitProgram::draw(const std::vector<Arguments>& arguments) {
-	std::vector<BlitProgram::Arguments> blit_arguments;
-	for (const Arguments arg : arguments) {
-		blit_arguments.emplace_back(BlitProgram::Arguments{
-		   arg.destination_rect,
-		   arg.z_value,
-		   arg.texture,
-		   BlitData{0, 0, 0, Rect()},
-		   arg.blend,
-		   arg.blend_mode,
-		});
-	}
-
-	blit_program_->activate();
-	blit_program_->draw_and_deactivate(blit_arguments);
+	draw({Arguments{dest_rect,
+	                z_value,
+	                texture,
+	                BlitData{0, 0, 0, Rect()},
+	                blend,
+	                BlendMode::UseAlpha,
+	                BlitMode::kMonochrome}});
 }
 
 // static
-BlendedBlitProgram& BlendedBlitProgram::instance() {
-	static BlendedBlitProgram blit_program;
+BlitProgram& BlitProgram::instance() {
+	static BlitProgram blit_program;
 	return blit_program;
 }
-
-BlendedBlitProgram::~BlendedBlitProgram() {
-}
-
-BlendedBlitProgram::BlendedBlitProgram() {
-	blit_program_.reset(new BlitProgram(kBlendedBlitFragmentShader));
-}
-
-void BlendedBlitProgram::draw(const FloatRect& gl_dest_rect,
-                              const float z_value,
-										const BlitData& texture,
-										const BlitData& mask,
-                              const RGBAColor& blend) {
-	draw({Arguments{gl_dest_rect, z_value, texture, mask, blend, BlendMode::UseAlpha}});
-}
-
-void BlendedBlitProgram::draw(const std::vector<Arguments>& arguments) {
-	std::vector<BlitProgram::Arguments> blit_arguments;
-	for (const Arguments arg : arguments) {
-		blit_arguments.emplace_back(BlitProgram::Arguments{
-		   arg.destination_rect,
-		   arg.z_value,
-		   arg.texture,
-			arg.mask,
-		   arg.blend,
-		   arg.blend_mode,
-		});
-	}
-
-	blit_program_->activate();
-	blit_program_->draw_and_deactivate(blit_arguments);
-}

=== modified file 'src/graphic/gl/blit_program.h'
--- src/graphic/gl/blit_program.h	2016-01-04 20:54:08 +0000
+++ src/graphic/gl/blit_program.h	2016-01-09 20:23:46 +0000
@@ -26,84 +26,14 @@
 #include "base/macros.h"
 #include "base/rect.h"
 #include "graphic/blend_mode.h"
+#include "graphic/blit_mode.h"
 #include "graphic/color.h"
 #include "graphic/gl/blit_data.h"
 #include "graphic/gl/system_headers.h"
-
-class BlitProgram;
-
-
-class VanillaBlitProgram {
-public:
-	struct Arguments {
-		FloatRect destination_rect;
-		float z_value;
-		BlitData texture;
-		float opacity;
-		BlendMode blend_mode;
-	};
-
-	// Returns the (singleton) instance of this class.
-	static VanillaBlitProgram& instance();
-	~VanillaBlitProgram();
-
-	// Draws the rectangle 'gl_src_rect' from the texture with the name
-	// 'texture' to 'gl_dest_rect' in the currently bound framebuffer. All alpha
-	// values are multiplied by 'opacity' during the blit.
-	// All coordinates are in the OpenGL frame. The 'blend_mode' defines if the
-	// values are copied or if alpha values are used.
-	void draw(const FloatRect& gl_dest_rect,
-				 const float z_value,
-				 const BlitData& texture,
-				 float opacity,
-	          const BlendMode blend_mode);
-
-	// Draws a bunch of items at once.
-	void draw(const std::vector<Arguments>& arguments);
-
-private:
-	VanillaBlitProgram();
-
-	std::unique_ptr<BlitProgram> blit_program_;
-
-	DISALLOW_COPY_AND_ASSIGN(VanillaBlitProgram);
-};
-
-class MonochromeBlitProgram {
-public:
-	struct Arguments {
-		FloatRect destination_rect;
-		float z_value;
-		BlitData texture;
-		RGBAColor blend;
-		BlendMode blend_mode;
-	};
-
-	// Returns the (singleton) instance of this class.
-	static MonochromeBlitProgram& instance();
-	~MonochromeBlitProgram();
-
-	// Draws the rectangle 'gl_src_rect' from the texture with the name
-	// 'texture' to 'gl_dest_rect' in the currently bound framebuffer. All
-	// coordinates are in the OpenGL frame. The image is first converted to
-	// luminance, then all values are multiplied with blend.
-	void draw(const FloatRect& gl_dest_rect,
-				 const float z_value,
-				 const BlitData& blit_source,
-				 const RGBAColor& blend);
-
-	// Draws a bunch of items at once.
-	void draw(const std::vector<Arguments>& arguments);
-
-private:
-	MonochromeBlitProgram();
-
-	std::unique_ptr<BlitProgram> blit_program_;
-
-	DISALLOW_COPY_AND_ASSIGN(MonochromeBlitProgram);
-};
-
-class BlendedBlitProgram {
+#include "graphic/gl/utils.h"
+
+// Blits images. Can blend them with PlayerColor or make the monochrome.
+class BlitProgram {
 public:
 	struct Arguments {
 		FloatRect destination_rect;
@@ -112,11 +42,12 @@
 		BlitData mask;
 		RGBAColor blend;
 		BlendMode blend_mode;
+		BlitMode blit_mode;
 	};
 
 	// Returns the (singleton) instance of this class.
-	static BlendedBlitProgram& instance();
-	~BlendedBlitProgram();
+	static BlitProgram& instance();
+	~BlitProgram();
 
 	// Draws the rectangle 'gl_src_rect' from the texture with the name
 	// 'gl_texture_image' to 'gl_dest_rect' in the currently bound framebuffer. All
@@ -126,17 +57,81 @@
 	          const float z_value,
 				 const BlitData& texture,
 				 const BlitData& mask,
-	          const RGBAColor& blend);
+	          const RGBAColor& blend,
+				 const BlendMode& blend_mode);
+
+	// Draws the rectangle 'gl_src_rect' from the texture with the name
+	// 'texture' to 'gl_dest_rect' in the currently bound framebuffer. All
+	// coordinates are in the OpenGL frame. The image is first converted to
+	// luminance, then all values are multiplied with blend.
+	void draw_monochrome(const FloatRect& gl_dest_rect,
+				 const float z_value,
+				 const BlitData& blit_source,
+				 const RGBAColor& blend);
+
 
 	// Draws a bunch of items at once.
 	void draw(const std::vector<Arguments>& arguments);
 
 private:
-	BlendedBlitProgram();
-
-	std::unique_ptr<BlitProgram> blit_program_;
-
-	DISALLOW_COPY_AND_ASSIGN(BlendedBlitProgram);
+	BlitProgram();
+
+	struct PerVertexData {
+		PerVertexData(float init_gl_x,
+		              float init_gl_y,
+		              float init_gl_z,
+		              float init_texture_x,
+		              float init_texture_y,
+		              float init_mask_texture_x,
+		              float init_mask_texture_y,
+		              float init_blend_r,
+		              float init_blend_g,
+		              float init_blend_b,
+		              float init_blend_a,
+		              float init_program_flavor)
+		   : gl_x(init_gl_x),
+		     gl_y(init_gl_y),
+		     gl_z(init_gl_z),
+		     texture_x(init_texture_x),
+		     texture_y(init_texture_y),
+		     mask_texture_x(init_mask_texture_x),
+		     mask_texture_y(init_mask_texture_y),
+		     blend_r(init_blend_r),
+		     blend_g(init_blend_g),
+		     blend_b(init_blend_b),
+		     blend_a(init_blend_a),
+		     program_flavor(init_program_flavor) {
+		}
+
+		float gl_x, gl_y, gl_z;
+		float texture_x, texture_y;
+		float mask_texture_x, mask_texture_y;
+		float blend_r, blend_g, blend_b, blend_a;
+		float program_flavor;
+	};
+	static_assert(sizeof(PerVertexData) == 48, "Wrong padding.");
+
+	// The buffer that will contain the quad for rendering.
+	Gl::Buffer<PerVertexData> gl_array_buffer_;
+
+	// The program.
+	Gl::Program gl_program_;
+
+	// Attributes.
+	GLint attr_blend_;
+	GLint attr_mask_texture_position_;
+	GLint attr_position_;
+	GLint attr_texture_position_;
+	GLint attr_program_flavor_;
+
+	// Uniforms.
+	GLint u_texture_;
+	GLint u_mask_;
+
+	// Cached for efficiency.
+	std::vector<PerVertexData> vertices_;
+
+	DISALLOW_COPY_AND_ASSIGN(BlitProgram);
 };
 
 #endif  // end of include guard: WL_GRAPHIC_GL_BLIT_PROGRAM_H

=== modified file 'src/graphic/gl/dither_program.cc'
--- src/graphic/gl/dither_program.cc	2016-01-05 11:28:54 +0000
+++ src/graphic/gl/dither_program.cc	2016-01-09 20:23:46 +0000
@@ -22,6 +22,7 @@
 #include "base/wexception.h"
 #include "graphic/gl/coordinate_conversion.h"
 #include "graphic/gl/fields_to_draw.h"
+#include "graphic/gl/utils.h"
 #include "graphic/image_io.h"
 #include "graphic/texture.h"
 #include "io/filesystem/layered_filesystem.h"
@@ -100,12 +101,11 @@
 
 	dither_mask_.reset(new Texture(load_image_as_sdl_surface("world/pics/edge.png", g_fs), true));
 
-	glBindTexture(GL_TEXTURE_2D, dither_mask_->blit_data().texture_id);
+	Gl::State::instance().bind(GL_TEXTURE0, dither_mask_->blit_data().texture_id);
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, static_cast<GLint>(GL_CLAMP_TO_EDGE));
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, static_cast<GLint>(GL_CLAMP_TO_EDGE));
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, static_cast<GLint>(GL_LINEAR));
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, static_cast<GLint>(GL_NEAREST));
-	glBindTexture(GL_TEXTURE_2D, 0);
 }
 
 DitherProgram::~DitherProgram() {}
@@ -169,11 +169,12 @@
 void DitherProgram::gl_draw(int gl_texture, float texture_w, float texture_h, const float z_value) {
 	glUseProgram(gl_program_.object());
 
-	glEnableVertexAttribArray(attr_brightness_);
-	glEnableVertexAttribArray(attr_dither_texture_position_);
-	glEnableVertexAttribArray(attr_position_);
-	glEnableVertexAttribArray(attr_texture_offset_);
-	glEnableVertexAttribArray(attr_texture_position_);
+	auto& gl_state = Gl::State::instance();
+	gl_state.enable_vertex_attrib_array({attr_brightness_,
+	                                   attr_dither_texture_position_,
+	                                   attr_position_,
+	                                   attr_texture_offset_,
+	                                   attr_texture_position_});
 
 	gl_array_buffer_.bind();
 	gl_array_buffer_.update(vertices_);
@@ -190,14 +191,8 @@
 	Gl::vertex_attrib_pointer(
 	   attr_texture_position_, 2, sizeof(PerVertexData), offsetof(PerVertexData, texture_x));
 
-	glBindBuffer(GL_ARRAY_BUFFER, 0);
-
-	// Set the sampler texture unit to 0
-	glActiveTexture(GL_TEXTURE0);
-	glBindTexture(GL_TEXTURE_2D, dither_mask_->blit_data().texture_id);
-
-	glActiveTexture(GL_TEXTURE1);
-	glBindTexture(GL_TEXTURE_2D, gl_texture);
+	gl_state.bind(GL_TEXTURE0, dither_mask_->blit_data().texture_id);
+	gl_state.bind(GL_TEXTURE1, gl_texture);
 
 	glUniform1f(u_z_value_, z_value);
 	glUniform1i(u_dither_texture_, 0);
@@ -205,16 +200,6 @@
 	glUniform2f(u_texture_dimensions_, texture_w, texture_h);
 
 	glDrawArrays(GL_TRIANGLES, 0, vertices_.size());
-
-	glBindTexture(GL_TEXTURE_2D, 0);
-	glActiveTexture(GL_TEXTURE0);
-	glBindTexture(GL_TEXTURE_2D, 0);
-
-	glDisableVertexAttribArray(attr_brightness_);
-	glDisableVertexAttribArray(attr_dither_texture_position_);
-	glDisableVertexAttribArray(attr_position_);
-	glDisableVertexAttribArray(attr_texture_offset_);
-	glDisableVertexAttribArray(attr_texture_position_);
 }
 
 void DitherProgram::draw(const uint32_t gametime,

=== modified file 'src/graphic/gl/draw_line_program.cc'
--- src/graphic/gl/draw_line_program.cc	2015-03-01 09:23:10 +0000
+++ src/graphic/gl/draw_line_program.cc	2016-01-09 20:23:46 +0000
@@ -89,8 +89,11 @@
 	size_t i = 0;
 
 	glUseProgram(gl_program_.object());
-	glEnableVertexAttribArray(attr_position_);
-	glEnableVertexAttribArray(attr_color_);
+
+	auto& gl_state = Gl::State::instance();
+	gl_state.enable_vertex_attrib_array({
+	   attr_position_, attr_color_,
+	});
 
 	gl_array_buffer_.bind();
 
@@ -140,11 +143,4 @@
 		glLineWidth(draw_arg.line_width);
 		glDrawArrays(GL_LINES, draw_arg.offset, draw_arg.count);
 	}
-
-	glBindBuffer(GL_ARRAY_BUFFER, 0);
-
-	glDisableVertexAttribArray(attr_position_);
-	glDisableVertexAttribArray(attr_color_);
-
-	glUseProgram(0);
 }

=== modified file 'src/graphic/gl/fill_rect_program.cc'
--- src/graphic/gl/fill_rect_program.cc	2015-03-01 09:23:10 +0000
+++ src/graphic/gl/fill_rect_program.cc	2016-01-09 20:23:46 +0000
@@ -112,8 +112,10 @@
 
 		gl_array_buffer_.bind();
 
-		glEnableVertexAttribArray(attr_position_);
-		glEnableVertexAttribArray(attr_color_);
+		auto& gl_state = Gl::State::instance();
+		gl_state.enable_vertex_attrib_array({
+		   attr_position_, attr_color_,
+		});
 
 		// Batch common rectangles up.
 		while (i < arguments.size()) {
@@ -199,8 +201,4 @@
 			break;
 		}
 	}
-
-	glDisableVertexAttribArray(attr_position_);
-	glDisableVertexAttribArray(attr_color_);
-	glBindBuffer(GL_ARRAY_BUFFER, 0);
 }

=== modified file 'src/graphic/gl/road_program.cc'
--- src/graphic/gl/road_program.cc	2016-01-04 20:54:08 +0000
+++ src/graphic/gl/road_program.cc	2016-01-09 20:23:46 +0000
@@ -25,6 +25,7 @@
 #include "base/log.h"
 #include "graphic/gl/coordinate_conversion.h"
 #include "graphic/gl/fields_to_draw.h"
+#include "graphic/gl/utils.h"
 #include "graphic/graphic.h"
 #include "graphic/image_io.h"
 #include "graphic/texture.h"
@@ -111,7 +112,7 @@
 	const float road_thickness_x = (-delta_y / vector_length) * kRoadThicknessInPixels;
 	const float road_thickness_y = (delta_x / vector_length) * kRoadThicknessInPixels;
 
-	const Texture& texture =
+	const Image& texture =
 	   road_type == Widelands::RoadType::kNormal ?
 	      start.road_textures->get_normal_texture(start.fx, start.fy, direction) :
 	      start.road_textures->get_busy_texture(start.fx, start.fy, direction);
@@ -234,9 +235,10 @@
 
 	glUseProgram(gl_program_.object());
 
-	glEnableVertexAttribArray(attr_position_);
-	glEnableVertexAttribArray(attr_texture_position_);
-	glEnableVertexAttribArray(attr_brightness_);
+	auto& gl_state = Gl::State::instance();
+	gl_state.enable_vertex_attrib_array({
+		attr_position_, attr_texture_position_, attr_brightness_
+	});
 
 	gl_array_buffer_.bind();
 	gl_array_buffer_.update(vertices_);
@@ -248,19 +250,9 @@
 	Gl::vertex_attrib_pointer(
 	   attr_brightness_, 1, sizeof(PerVertexData), offsetof(PerVertexData, brightness));
 
-	glBindBuffer(GL_ARRAY_BUFFER, 0);
-
-	// Bind the textures.
-	glActiveTexture(GL_TEXTURE0);
-	glBindTexture(GL_TEXTURE_2D, gl_texture);
-
+	gl_state.bind(GL_TEXTURE0, gl_texture);
 	glUniform1i(u_texture_, 0);
-
 	glUniform1f(u_z_value_, z_value);
 
 	glDrawArrays(GL_TRIANGLES, 0, vertices_.size());
-
-	glDisableVertexAttribArray(attr_position_);
-	glDisableVertexAttribArray(attr_texture_position_);
-	glDisableVertexAttribArray(attr_brightness_);
 }

=== modified file 'src/graphic/gl/terrain_program.cc'
--- src/graphic/gl/terrain_program.cc	2016-01-04 20:54:08 +0000
+++ src/graphic/gl/terrain_program.cc	2016-01-09 20:23:46 +0000
@@ -21,6 +21,7 @@
 
 #include "graphic/gl/coordinate_conversion.h"
 #include "graphic/gl/fields_to_draw.h"
+#include "graphic/gl/utils.h"
 #include "graphic/texture.h"
 
 namespace  {
@@ -103,10 +104,9 @@
 void TerrainProgram::gl_draw(int gl_texture, float texture_w, float texture_h, float z_value) {
 	glUseProgram(gl_program_.object());
 
-	glEnableVertexAttribArray(attr_brightness_);
-	glEnableVertexAttribArray(attr_position_);
-	glEnableVertexAttribArray(attr_texture_offset_);
-	glEnableVertexAttribArray(attr_texture_position_);
+	auto& gl_state = Gl::State::instance();
+	gl_state.enable_vertex_attrib_array(
+	   {attr_brightness_, attr_position_, attr_texture_offset_, attr_texture_position_});
 
 	gl_array_buffer_.bind();
 	gl_array_buffer_.update(vertices_);
@@ -119,23 +119,13 @@
 	Gl::vertex_attrib_pointer(
 	   attr_texture_position_, 2, sizeof(PerVertexData), offsetof(PerVertexData, texture_x));
 
-	glBindBuffer(GL_ARRAY_BUFFER, 0);
-
-	glActiveTexture(GL_TEXTURE0);
-	glBindTexture(GL_TEXTURE_2D, gl_texture);
+	gl_state.bind(GL_TEXTURE0, gl_texture);
 
 	glUniform1f(u_z_value_, z_value);
 	glUniform1i(u_terrain_texture_, 0);
 	glUniform2f(u_texture_dimensions_, texture_w, texture_h);
 
 	glDrawArrays(GL_TRIANGLES, 0, vertices_.size());
-
-	glBindTexture(GL_TEXTURE_2D, 0);
-
-	glDisableVertexAttribArray(attr_brightness_);
-	glDisableVertexAttribArray(attr_position_);
-	glDisableVertexAttribArray(attr_texture_offset_);
-	glDisableVertexAttribArray(attr_texture_position_);
 }
 
 void TerrainProgram::add_vertex(const FieldsToDraw::Field& field,

=== modified file 'src/graphic/gl/utils.cc'
--- src/graphic/gl/utils.cc	2015-02-20 07:45:49 +0000
+++ src/graphic/gl/utils.cc	2016-01-09 20:23:46 +0000
@@ -166,6 +166,78 @@
 	}
 }
 
+State::State()
+   : last_active_texture_(0), current_framebuffer_(0), current_framebuffer_texture_(0) {
+}
+
+void State::bind(const GLenum target, const GLuint texture) {
+	if (texture == 0)  {
+		return;
+	}
+	do_bind(target, texture);
+}
+
+void State::do_bind(const GLenum target, const GLuint texture) {
+	const auto currently_bound_texture = target_to_texture_[target];
+	if (currently_bound_texture == texture) {
+		return;
+	}
+	if (last_active_texture_ != target) {
+		glActiveTexture(target);
+		last_active_texture_ = target;
+	}
+	glBindTexture(GL_TEXTURE_2D, texture);
+
+	target_to_texture_[target] = texture;
+	texture_to_target_[currently_bound_texture] = 0;
+	texture_to_target_[texture] = target;
+}
+
+void State::unbind_texture_if_bound(const GLuint texture) {
+	if (texture == 0) {
+		return;
+	}
+	const auto target = texture_to_target_[texture];
+	if (target != 0) {
+		do_bind(target, 0);
+	}
+}
+
+void State::bind_framebuffer(const GLuint framebuffer, const GLuint texture) {
+	if (current_framebuffer_ == framebuffer && current_framebuffer_texture_ == texture) {
+		return;
+	}
+
+	glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
+	if (framebuffer != 0) {
+		unbind_texture_if_bound(texture);
+		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
+	}
+	current_framebuffer_ = framebuffer;
+	current_framebuffer_texture_ = texture;
+}
+
+void State::enable_vertex_attrib_array(std::unordered_set<GLint> entries) {
+	for (const auto e : entries) {
+		if (!enabled_attrib_arrays_.count(e)) {
+			glEnableVertexAttribArray(e);
+		}
+	}
+	for (const auto e : enabled_attrib_arrays_) {
+		if (!entries.count(e)) {
+			glDisableVertexAttribArray(e);
+		}
+	}
+	enabled_attrib_arrays_ = entries;
+}
+
+// static
+State& State::instance() {
+	static State binder;
+	return binder;
+}
+
+
 void vertex_attrib_pointer(int vertex_index, int num_items, int stride, int offset) {
 	glVertexAttribPointer(
 	   vertex_index, num_items, GL_FLOAT, GL_FALSE, stride, reinterpret_cast<void*>(offset));

=== modified file 'src/graphic/gl/utils.h'
--- src/graphic/gl/utils.h	2016-01-05 11:28:54 +0000
+++ src/graphic/gl/utils.h	2016-01-09 20:23:46 +0000
@@ -20,6 +20,8 @@
 #define WL_GRAPHIC_GL_UTILS_H
 
 #include <memory>
+#include <unordered_map>
+#include <unordered_set>
 #include <vector>
 
 #include <stdint.h>
@@ -100,6 +102,45 @@
 	DISALLOW_COPY_AND_ASSIGN(Buffer);
 };
 
+// Some GL drivers do not remember the current pipeline state. If you rebind a
+// texture that has already bound to the same target, they will happily stall
+// the pipeline. We therefore cache the state of the GL driver in this class
+// and skip unneeded GL calls.
+class State {
+public:
+	static State& instance();
+
+	void bind_framebuffer(GLuint framebuffer, GLuint texture);
+
+	// Wrapper around glActiveTexture() and glBindTexture(). We never unbind a
+	// texture, i.e. calls with texture == 0 are ignored. It costs only time and
+	// is only needed when the bounded texture is rendered on - see
+	// 'unbind_texture_if_bound'.
+	void bind(GLenum target, GLuint texture);
+
+	// Checks if the texture is bound to any target. If so, unbinds it. This is
+	// needed before the texture is used as target for rendering.
+	void unbind_texture_if_bound(GLuint texture);
+
+	// Calls glEnableVertexAttribArray on all 'entries' and disables all others
+	// that are activated. 'entries' is taken by value on purpose.
+	void enable_vertex_attrib_array(std::unordered_set<GLint> entries);
+
+private:
+	std::unordered_map<GLenum, GLuint> target_to_texture_;
+	std::unordered_map<GLuint, GLenum> texture_to_target_;
+	std::unordered_set<GLint> enabled_attrib_arrays_;
+	GLenum last_active_texture_;
+	GLuint current_framebuffer_;
+	GLuint current_framebuffer_texture_;
+
+	State();
+
+	void do_bind(GLenum target, GLuint texture);
+
+	DISALLOW_COPY_AND_ASSIGN(State);
+};
+
 // Calls glVertexAttribPointer.
 void vertex_attrib_pointer(int vertex_index, int num_items, int stride, int offset);
 

=== modified file 'src/graphic/graphic.cc'
--- src/graphic/graphic.cc	2015-01-28 07:32:57 +0000
+++ src/graphic/graphic.cc	2016-01-09 20:23:46 +0000
@@ -21,17 +21,23 @@
 
 #include <memory>
 
+#include "base/i18n.h"
 #include "base/log.h"
 #include "base/wexception.h"
 #include "build_info.h"
+#include "graphic/align.h"
 #include "graphic/animation.h"
+#include "graphic/build_texture_atlas.h"
+#include "graphic/font.h"
 #include "graphic/font_handler.h"
+#include "graphic/font_handler1.h"
 #include "graphic/gl/system_headers.h"
 #include "graphic/image.h"
 #include "graphic/image_io.h"
 #include "graphic/render_queue.h"
 #include "graphic/rendertarget.h"
 #include "graphic/screen.h"
+#include "graphic/text_layout.h"
 #include "graphic/texture.h"
 #include "io/filesystem/layered_filesystem.h"
 #include "io/streamwrite.h"
@@ -55,18 +61,31 @@
 	SDL_FreeSurface(s);
 }
 
+void show_preload_text(const std::string& text) {
+		if (UI::g_fh && UI::g_fh1) {
+			auto *target = g_gr->get_render_target();
+			UI::g_fh->draw_text(
+					*target, UI::TextStyle::makebold(UI::Font::get(UI::g_fh1->fontset().serif(), 15),
+						RGBColor(204, 204, 0)),
+					Point(target->width() / 2, target->height() / 2),
+					text, UI::Align_Center);
+		   g_gr->refresh();
+	   }
+}
+
 }  // namespace
 
+Graphic::Graphic() : image_cache_(new ImageCache()), animation_manager_(new AnimationManager()) {
+}
+
 /**
  * Initialize the SDL video mode.
-*/
-Graphic::Graphic(int window_mode_w, int window_mode_h, bool init_fullscreen)
-   : m_window_mode_width(window_mode_w),
-     m_window_mode_height(window_mode_h),
-     m_update(true),
-     image_cache_(new ImageCache()),
-     animation_manager_(new AnimationManager())
-{
+ */
+void Graphic::initialize(int window_mode_w, int window_mode_h, bool init_fullscreen) {
+	m_window_mode_width = window_mode_w;
+	m_window_mode_height = window_mode_h;
+	m_requires_update = true;
+
 	// Request an OpenGL 2 context with double buffering.
 	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
 	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
@@ -74,12 +93,10 @@
 	SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
 
 	log("Graphics: Try to set Videomode %ux%u\n", m_window_mode_width, m_window_mode_height);
-	m_sdl_window = SDL_CreateWindow("Widelands Window",
-	                                SDL_WINDOWPOS_UNDEFINED,
-	                                SDL_WINDOWPOS_UNDEFINED,
-	                                m_window_mode_width,
-	                                m_window_mode_height,
-	                                SDL_WINDOW_OPENGL);
+	m_sdl_window =
+	   SDL_CreateWindow("Widelands Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
+	                    m_window_mode_width, m_window_mode_height, SDL_WINDOW_OPENGL);
+
 	resolution_changed();
 	set_fullscreen(init_fullscreen);
 
@@ -96,22 +113,22 @@
 	glewExperimental = GL_TRUE;
 	GLenum err = glewInit();
 	if (err != GLEW_OK) {
-		log("glewInit returns %i\nYour OpenGL installation must be __very__ broken. %s\n",
-			 err, glewGetErrorString(err));
+		log("glewInit returns %i\nYour OpenGL installation must be __very__ broken. %s\n", err,
+		    glewGetErrorString(err));
 		throw wexception("glewInit returns %i: Broken OpenGL installation.", err);
 	}
 #endif
 
-	log("Graphics: OpenGL: Version \"%s\"\n",
-		 reinterpret_cast<const char*>(glGetString(GL_VERSION)));
+	log(
+	   "Graphics: OpenGL: Version \"%s\"\n", reinterpret_cast<const char*>(glGetString(GL_VERSION)));
 
 	GLboolean glBool;
 	glGetBooleanv(GL_DOUBLEBUFFER, &glBool);
 	log("Graphics: OpenGL: Double buffering %s\n", (glBool == GL_TRUE) ? "enabled" : "disabled");
 
-	GLint glInt;
-	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glInt);
-	log("Graphics: OpenGL: Max texture size: %u\n", glInt);
+	GLint max_texture_size;
+	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
+	log("Graphics: OpenGL: Max texture size: %u\n", max_texture_size);
 
 	glDrawBuffer(GL_BACK);
 
@@ -134,12 +151,19 @@
 		    " pixel fmt %u\n"
 		    " size %d %d\n"
 		    "**** END GRAPHICS REPORT ****\n",
-		    SDL_GetCurrentVideoDriver(),
-		    disp_mode.format,
-		    disp_mode.w,
-		    disp_mode.h);
+		    SDL_GetCurrentVideoDriver(), disp_mode.format, disp_mode.w, disp_mode.h);
 		assert(SDL_BYTESPERPIXEL(disp_mode.format) == 4);
 	}
+
+	if (!is_texture_atlas_current()) {
+		if (UI::g_fh && UI::g_fh1) {
+			show_preload_text(
+			   _("Building texture atlas...\nThis one time operation can take up to 5 minutes."));
+		}
+		make_texture_atlas(max_texture_size);
+	}
+	show_preload_text(_("Loading images..."));
+	image_cache_->fill_with_texture_atlas();
 }
 
 Graphic::~Graphic()
@@ -239,14 +263,14 @@
 
 
 void Graphic::update() {
-	m_update = true;
+	m_requires_update = true;
 }
 
 /**
  * Returns true if parts of the screen have been marked for refreshing.
 */
 bool Graphic::need_update() const {
-	return m_update;
+	return m_requires_update;
 }
 
 /**
@@ -270,7 +294,7 @@
 	}
 
 	SDL_GL_SwapWindow(m_sdl_window);
-	m_update = false;
+	m_requires_update = false;
 }
 
 

=== modified file 'src/graphic/graphic.h'
--- src/graphic/graphic.h	2015-06-10 06:46:40 +0000
+++ src/graphic/graphic.h	2016-01-09 20:23:46 +0000
@@ -50,11 +50,14 @@
  */
 class Graphic {
 public:
-	// Creates a new graphic mode with the given resolution if fullscreen is
-	// false, otherwise a window that fills the screen.
-	Graphic(int window_mode_w, int window_mode_height, bool fullscreen);
+	// Creates a new Graphic object. Must call initialize before first use.
+	Graphic();
 	~Graphic();
 
+	// Initializes with the given resolution if fullscreen is false, otherwise a
+	// window that fills the screen.
+	void initialize(int window_mode_w, int window_mode_height, bool fullscreen);
+
 	// Gets and sets the resolution.
 	void change_resolution(int w, int h);
 	int get_xres();
@@ -96,7 +99,7 @@
 	/// A RenderTarget for screen_. This is initialized during init()
 	std::unique_ptr<RenderTarget> m_rendertarget;
 	/// This marks the complete screen for updating.
-	bool m_update;
+	bool m_requires_update;
 
 	/// Non-volatile cache of independent images.
 	std::unique_ptr<ImageCache> image_cache_;

=== modified file 'src/graphic/image_cache.cc'
--- src/graphic/image_cache.cc	2016-01-09 20:23:45 +0000
+++ src/graphic/image_cache.cc	2016-01-09 20:23:46 +0000
@@ -30,29 +30,11 @@
 #include "graphic/image.h"
 #include "graphic/image_io.h"
 #include "graphic/texture.h"
-
-ImageCache::ProxyImage::ProxyImage(std::unique_ptr<const Image> image) : image_(std::move(image)) {
-}
-
-const Image& ImageCache::ProxyImage::image() {
-	return *image_;
-}
-
-void ImageCache::ProxyImage::set_image(std::unique_ptr<const Image> image) {
-	image_ = std::move(image);
-}
-
-int ImageCache::ProxyImage::width() const {
-	return image_->width();
-}
-
-int ImageCache::ProxyImage::height() const {
-	return image_->height();
-}
-
-const BlitData& ImageCache::ProxyImage::blit_data() const {
-	return image_->blit_data();
-}
+#include "io/fileread.h"
+#include "io/filesystem/filesystem.h"
+#include "io/filesystem/layered_filesystem.h"
+#include "scripting/lua_interface.h"
+#include "scripting/lua_table.h"
 
 ImageCache::ImageCache() {
 }
@@ -67,16 +49,47 @@
 const Image* ImageCache::insert(const std::string& hash, std::unique_ptr<const Image> image) {
 	assert(!has(hash));
 	const Image* return_value = image.get();
-	images_.insert(make_pair(hash, std::unique_ptr<ProxyImage>(new ProxyImage(std::move(image)))));
+	images_.emplace(hash, std::move(image));
 	return return_value;
 }
 
+void ImageCache::fill_with_texture_atlas() {
+	LuaInterface lua;
+
+	for (int i = 0; i < 100; ++i) {
+		const auto filename = (boost::format("cache/texture_atlas_%02d.png") % i).str();
+		if (!g_fs->file_exists(filename)) {
+			break;
+		}
+		texture_atlases_.emplace_back(load_image(filename));
+	}
+
+	auto config = lua.run_script("cache/texture_atlas.lua");
+	for (const auto& hash : config->keys<std::string>()) {
+		if (hash == "build_id") {
+			// do not warn about unused keys.
+			config->get_string("build_id");
+			continue;
+		}
+		auto image_config = config->get_table(hash);
+		if (image_config->get_string("type") == "unpacked") {
+			images_.emplace(hash, load_image(hash));
+		} else {
+			int texture_atlas_index = image_config->get_int("texture_atlas");
+			const auto& parent = texture_atlases_[texture_atlas_index]->blit_data();
+			auto rect_config = image_config->get_table("rect");
+			const Rect subrect(rect_config->get_int(1), rect_config->get_int(2),
+			                   rect_config->get_int(3), rect_config->get_int(4));
+			images_.emplace(hash, std::unique_ptr<Texture>(new Texture(
+			                         parent.texture_id, subrect, parent.rect.w, parent.rect.h)));
+		}
+	}
+}
+
 const Image* ImageCache::get(const std::string& hash) {
-	ImageMap::const_iterator it = images_.find(hash);
+	auto it = images_.find(hash);
 	if (it == images_.end()) {
-		images_.insert(
-		   make_pair(hash, std::unique_ptr<ProxyImage>(new ProxyImage(load_image(hash)))));
-		return get(hash);
+		throw wexception("Image with hash %s not found.", hash.c_str());
 	}
 	return it->second.get();
 }

=== modified file 'src/graphic/image_cache.h'
--- src/graphic/image_cache.h	2016-01-09 20:23:45 +0000
+++ src/graphic/image_cache.h	2016-01-09 20:23:46 +0000
@@ -33,11 +33,10 @@
 
 // For historic reasons, most part of the Widelands code base expect that an
 // Image stays valid for the whole duration of the program run. This class is
-// the one that keeps ownership of all Images to ensure that this is true. Also
-// for historic reasons, this class will try to load in Image from disk when
-// its hash is not found. Other parts of Widelands will create images when they
-// do not exist in the cache yet and then put it into the cache and therefore
-// releasing their ownership.
+// the one that keeps ownership of all Images to ensure that this is true.
+// Other parts of Widelands will create images when they do not exist in the
+// cache yet and then put it into the cache and therefore releasing their
+// ownership.
 class ImageCache {
 public:
 	ImageCache();
@@ -55,29 +54,12 @@
 	// Returns true if the given hash is stored in the cache.
 	bool has(const std::string& hash) const;
 
+	// Loads the cache from texture atlases on disk.
+	void fill_with_texture_atlas();
+
 private:
-	// We return a wrapped Image so that we can swap out the pointer to the
-	// image under our user. This can happen when we move an Image from a stand
-	// alone texture into being a subrect of a texture atlas.
-	class ProxyImage : public Image {
-	public:
-		ProxyImage(std::unique_ptr<const Image> image);
-
-		const Image& image();
-		void set_image(std::unique_ptr<const Image> image);
-
-		int width() const override;
-		int height() const override;
-		const BlitData& blit_data() const override;
-
-	private:
-		std::unique_ptr<const Image> image_;
-	};
-
-	using ImageMap = std::map<std::string, std::unique_ptr<ProxyImage>>;
-
 	std::vector<std::unique_ptr<Texture>> texture_atlases_;
-	ImageMap images_;  /// hash of cached filename/image pairs
+	std::map<std::string, std::unique_ptr<const Image>> images_;
 
 	DISALLOW_COPY_AND_ASSIGN(ImageCache);
 };

=== modified file 'src/graphic/render_queue.cc'
--- src/graphic/render_queue.cc	2016-01-05 11:28:54 +0000
+++ src/graphic/render_queue.cc	2016-01-09 20:23:46 +0000
@@ -22,7 +22,6 @@
 #include <algorithm>
 #include <limits>
 
-#include "base/log.h"
 #include "base/rect.h"
 #include "base/wexception.h"
 #include "graphic/gl/blit_program.h"
@@ -76,25 +75,15 @@
 	return (z_value << 40) | (program_id << 36) | extra_value;
 }
 
-// Construct 'args' used by the individual programs out of 'item'.
-inline void from_item(const RenderQueue::Item& item, VanillaBlitProgram::Arguments* args) {
-	args->texture = item.vanilla_blit_arguments.texture;
-	args->opacity = item.vanilla_blit_arguments.opacity;
-}
-
-inline void from_item(const RenderQueue::Item& item, MonochromeBlitProgram::Arguments* args) {
-	args->texture = item.monochrome_blit_arguments.texture;
-	args->blend = item.monochrome_blit_arguments.blend;
-}
-
 inline void from_item(const RenderQueue::Item& item, FillRectProgram::Arguments* args) {
 	args->color = item.rect_arguments.color;
 }
 
-inline void from_item(const RenderQueue::Item& item, BlendedBlitProgram::Arguments* args) {
-	args->texture = item.blended_blit_arguments.texture;
-	args->blend = item.blended_blit_arguments.blend;
-	args->mask = item.blended_blit_arguments.mask;
+inline void from_item(const RenderQueue::Item& item, BlitProgram::Arguments* args) {
+	args->texture = item.blit_arguments.texture;
+	args->blend = item.blit_arguments.blend;
+	args->mask = item.blit_arguments.mask;
+	args->blit_mode = item.blit_arguments.mode;
 }
 
 inline void from_item(const RenderQueue::Item& item, DrawLineProgram::Arguments* args) {
@@ -167,15 +156,7 @@
 
 	switch (given_item.program_id) {
 		case Program::kBlit:
-		   extra_value = given_item.vanilla_blit_arguments.texture.texture_id;
-		 break;
-
-		case Program::kBlitMonochrome:
-		   extra_value = given_item.monochrome_blit_arguments.texture.texture_id;
-			break;
-
-		case Program::kBlitBlended:
-		   extra_value = given_item.blended_blit_arguments.texture.texture_id;
+		   extra_value = given_item.blit_arguments.texture.texture_id;
 			break;
 
 		case Program::kLine:
@@ -212,7 +193,7 @@
 		throw wexception("Too many drawn layers. Ran out of z-values.");
 	}
 
-	glBindFramebuffer(GL_FRAMEBUFFER, 0);
+	Gl::State::instance().bind_framebuffer(0, 0);
 	glViewport(0, 0, screen_width, screen_height);
 
 	glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
@@ -231,7 +212,6 @@
 	blended_items_.clear();
 
 	glDepthMask(GL_TRUE);
-
 	next_z_ = 1;
 }
 
@@ -241,18 +221,8 @@
 		const Item& item = items[i];
 		switch (item.program_id) {
 		case Program::kBlit:
-			VanillaBlitProgram::instance().draw(
-			   batch_up<VanillaBlitProgram::Arguments>(Program::kBlit, items, &i));
-		 break;
-
-		case Program::kBlitMonochrome:
-			MonochromeBlitProgram::instance().draw(
-			   batch_up<MonochromeBlitProgram::Arguments>(Program::kBlitMonochrome, items, &i));
-			break;
-
-		case Program::kBlitBlended:
-			BlendedBlitProgram::instance().draw(
-			   batch_up<BlendedBlitProgram::Arguments>(Program::kBlitBlended, items, &i));
+			BlitProgram::instance().draw(
+			   batch_up<BlitProgram::Arguments>(Program::kBlit, items, &i));
 			break;
 
 		case Program::kLine:

=== modified file 'src/graphic/render_queue.h'
--- src/graphic/render_queue.h	2016-01-08 21:00:39 +0000
+++ src/graphic/render_queue.h	2016-01-09 20:23:46 +0000
@@ -28,6 +28,7 @@
 #include "base/macros.h"
 #include "base/rect.h"
 #include "graphic/blend_mode.h"
+#include "graphic/blit_mode.h"
 #include "graphic/color.h"
 #include "graphic/gl/fields_to_draw.h"
 #include "logic/description_maintainer.h"
@@ -82,24 +83,18 @@
 		kTerrainDither,
 		kTerrainRoad,
 		kBlit,
-		kBlitMonochrome,
-		kBlitBlended,
 		kRect,
 		kLine,
 		kHighestProgramId,
 	};
 
-	struct VanillaBlitArguments {
-		BlitData texture;
-		float opacity;
-	};
-
 	struct MonochromeBlitArguments {
 		BlitData texture;
 		RGBAColor blend;
 	};
 
-	struct BlendedBlitArguments {
+	struct BlitArguments {
+		BlitMode mode;
 		BlitData texture;
 		BlitData mask;
 		RGBAColor blend;
@@ -154,9 +149,7 @@
 		BlendMode blend_mode;
 
 		union {
-			VanillaBlitArguments vanilla_blit_arguments;
-			MonochromeBlitArguments monochrome_blit_arguments;
-			BlendedBlitArguments blended_blit_arguments;
+			BlitArguments blit_arguments;
 			TerrainArguments terrain_arguments;
 			RectArguments rect_arguments;
 			LineArguments line_arguments;

=== modified file 'src/graphic/screen.cc'
--- src/graphic/screen.cc	2016-01-05 11:28:54 +0000
+++ src/graphic/screen.cc	2016-01-09 20:23:46 +0000
@@ -65,11 +65,13 @@
                      float opacity,
                      BlendMode blend_mode) {
 	RenderQueue::Item i;
+	i.destination_rect = dst_rect;
 	i.program_id = RenderQueue::Program::kBlit;
 	i.blend_mode = blend_mode;
-	i.destination_rect = dst_rect;
-	i.vanilla_blit_arguments.texture = texture;
-	i.vanilla_blit_arguments.opacity = opacity;
+	i.blit_arguments.texture = texture;
+	i.blit_arguments.mask.texture_id = 0;
+	i.blit_arguments.blend = RGBAColor(0, 0, 0, 255 * opacity);
+	i.blit_arguments.mode = BlitMode::kDirect;
 	RenderQueue::instance().enqueue(i);
 }
 
@@ -79,11 +81,12 @@
                              const RGBColor& blend) {
 	RenderQueue::Item i;
 	i.destination_rect = dst_rect;
-	i.program_id = RenderQueue::Program::kBlitBlended;
+	i.program_id = RenderQueue::Program::kBlit;
 	i.blend_mode = BlendMode::UseAlpha;
-	i.blended_blit_arguments.texture = texture;
-	i.blended_blit_arguments.mask = mask;
-	i.blended_blit_arguments.blend = blend;
+	i.blit_arguments.texture = texture;
+	i.blit_arguments.mask = mask;
+	i.blit_arguments.blend = blend;
+	i.blit_arguments.mode = BlitMode::kBlendedWithMask;
 	RenderQueue::instance().enqueue(i);
 }
 
@@ -91,11 +94,13 @@
                                 const BlitData& texture,
                                 const RGBAColor& blend) {
 	RenderQueue::Item i;
-	i.program_id = RenderQueue::Program::kBlitMonochrome;
+	i.destination_rect = dst_rect;
+	i.program_id = RenderQueue::Program::kBlit;
 	i.blend_mode = BlendMode::UseAlpha;
-	i.destination_rect = dst_rect;
-	i.monochrome_blit_arguments.texture = texture;
-	i.monochrome_blit_arguments.blend = blend;
+	i.blit_arguments.texture = texture;
+	i.blit_arguments.mask.texture_id = 0;
+	i.blit_arguments.blend = blend;
+	i.blit_arguments.mode = BlitMode::kMonochrome;
 	RenderQueue::instance().enqueue(i);
 }
 

=== modified file 'src/graphic/surface.h'
--- src/graphic/surface.h	2016-01-04 20:54:08 +0000
+++ src/graphic/surface.h	2016-01-09 20:23:46 +0000
@@ -48,14 +48,14 @@
 	          const float opacity,
 	          BlendMode blend_mode);
 
-	/// This draws a playercolor blended image. See BlendedBlitProgram.
+	/// This draws a playercolor blended image.
 	void blit_blended(const Rect& dst,
 	                  const Image& image,
 	                  const Image& texture_mask,
 	                  const Rect& srcrc,
 	                  const RGBColor& blend);
 
-	/// This draws a grayed out version. See MonochromeBlitProgram.
+	/// This draws a grayed out version.
 	void
 	blit_monochrome(const Rect& dst, const Image&, const Rect& srcrc, const RGBAColor& multiplier);
 

=== modified file 'src/graphic/text/test/render_richtext.cc'
--- src/graphic/text/test/render_richtext.cc	2015-03-01 09:21:20 +0000
+++ src/graphic/text/test/render_richtext.cc	2016-01-09 20:23:46 +0000
@@ -98,7 +98,8 @@
 	g_fs = new LayeredFileSystem();
 	g_fs->add_file_system(&FileSystem::create(INSTALL_DATADIR));
 
-	g_gr = new Graphic(1, 1, false);
+	g_gr = new Graphic();
+	g_gr->initialize(1, 1, false);
 }
 
 }  // namespace

=== modified file 'src/graphic/texture.cc'
--- src/graphic/texture.cc	2016-01-04 20:54:08 +0000
+++ src/graphic/texture.cc	2016-01-09 20:23:46 +0000
@@ -167,6 +167,7 @@
 Texture::~Texture()
 {
 	if (m_owns_texture) {
+		Gl::State::instance().unbind_texture_if_bound(m_blit_data.texture_id);
 		glDeleteTextures(1, &m_blit_data.texture_id);
 	}
 }
@@ -192,7 +193,7 @@
 
 	m_owns_texture = true;
 	glGenTextures(1, &m_blit_data.texture_id);
-	glBindTexture(GL_TEXTURE_2D, m_blit_data.texture_id);
+	Gl::State::instance().bind(GL_TEXTURE0, m_blit_data.texture_id);
 
 	// set texture filter to use linear filtering. This looks nicer for resized
 	// texture. Most textures and images are not resized so the filtering
@@ -215,9 +216,8 @@
 
 	m_pixels.reset(new uint8_t[width() * height() * 4]);
 
-	glBindTexture(GL_TEXTURE_2D, m_blit_data.texture_id);
+	Gl::State::instance().bind(GL_TEXTURE0, m_blit_data.texture_id);
 	glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_pixels.get());
-	glBindTexture(GL_TEXTURE_2D, 0);
 }
 
 void Texture::unlock(UnlockMode mode) {
@@ -227,11 +227,9 @@
 	assert(m_pixels);
 
 	if (mode == Unlock_Update) {
-		glBindTexture(GL_TEXTURE_2D, m_blit_data.texture_id);
-		glTexImage2D
-            (GL_TEXTURE_2D, 0, static_cast<GLint>(GL_RGBA), width(), height(), 0, GL_RGBA,
-			 GL_UNSIGNED_BYTE,  m_pixels.get());
-		glBindTexture(GL_TEXTURE_2D, 0);
+		Gl::State::instance().bind(GL_TEXTURE0, m_blit_data.texture_id);
+		glTexImage2D(GL_TEXTURE_2D, 0, static_cast<GLint>(GL_RGBA), width(), height(), 0, GL_RGBA,
+		             GL_UNSIGNED_BYTE, m_pixels.get());
 	}
 
 	m_pixels.reset(nullptr);
@@ -265,8 +263,8 @@
 
 
 void Texture::setup_gl() {
-	glBindFramebuffer(GL_FRAMEBUFFER, GlFramebuffer::instance().id());
-	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_blit_data.texture_id, 0);
+	Gl::State::instance().bind_framebuffer(
+	   GlFramebuffer::instance().id(), m_blit_data.texture_id);
 	glViewport(0, 0, width(), height());
 }
 
@@ -275,7 +273,8 @@
                      float opacity,
                      BlendMode blend_mode) {
 	setup_gl();
-	VanillaBlitProgram::instance().draw(dst_rect, 0.f, texture, opacity, blend_mode);
+	BlitProgram::instance().draw(dst_rect, 0.f, texture, BlitData{0, 0, 0, Rect()},
+	                             RGBAColor(0, 0, 0, 255 * opacity), blend_mode);
 }
 
 void Texture::do_blit_blended(const FloatRect& dst_rect,
@@ -284,14 +283,14 @@
                               const RGBColor& blend) {
 
 	setup_gl();
-	BlendedBlitProgram::instance().draw(dst_rect, 0.f, texture, mask, blend);
+	BlitProgram::instance().draw(dst_rect, 0.f, texture, mask, blend, BlendMode::UseAlpha);
 }
 
 void Texture::do_blit_monochrome(const FloatRect& dst_rect,
                                  const BlitData& texture,
                                  const RGBAColor& blend) {
 	setup_gl();
-	MonochromeBlitProgram::instance().draw(dst_rect, 0.f, texture, blend);
+	BlitProgram::instance().draw_monochrome(dst_rect, 0.f, texture, blend);
 }
 
 void

=== modified file 'src/graphic/texture_atlas.cc'
--- src/graphic/texture_atlas.cc	2016-01-09 20:23:45 +0000
+++ src/graphic/texture_atlas.cc	2016-01-09 20:23:46 +0000
@@ -137,7 +137,7 @@
 		   *block.texture,
 		   Rect(0, 0, block.texture->width(), block.texture->height()),
 		   1.,
-		   BlendMode::UseAlpha);
+		   BlendMode::Copy);
 
 		pack_info->emplace_back(PackedTexture(
 		   texture_atlas_index, block.index,

=== modified file 'src/logic/CMakeLists.txt'
--- src/logic/CMakeLists.txt	2016-01-08 21:00:39 +0000
+++ src/logic/CMakeLists.txt	2016-01-09 20:23:46 +0000
@@ -237,7 +237,6 @@
     graphic_surface
     graphic_text
     graphic_text_layout
-    graphic_texture_atlas
     helper
     io_fileread
     io_filesystem

=== modified file 'src/logic/map_info.cc'
--- src/logic/map_info.cc	2015-03-01 09:21:20 +0000
+++ src/logic/map_info.cc	2016-01-09 20:23:46 +0000
@@ -48,7 +48,8 @@
 	g_fs = new LayeredFileSystem();
 	g_fs->add_file_system(&FileSystem::create(INSTALL_DATADIR));
 
-	g_gr = new Graphic(1, 1, false);
+	g_gr = new Graphic();
+	g_gr->initialize(1, 1, false);
 }
 
 }  // namespace

=== modified file 'src/logic/map_objects/tribes/road_textures.cc'
--- src/logic/map_objects/tribes/road_textures.cc	2015-11-28 22:29:26 +0000
+++ src/logic/map_objects/tribes/road_textures.cc	2016-01-09 20:23:46 +0000
@@ -21,18 +21,18 @@
 
 #include <memory>
 
-const Texture& RoadTextures::get_normal_texture(int x, int y, int direction) const {
+const Image& RoadTextures::get_normal_texture(int x, int y, int direction) const {
 	return *normal_textures_.at((x + y + direction) % normal_textures_.size());
 }
 
-const Texture& RoadTextures::get_busy_texture(int x, int y, int direction) const {
+const Image& RoadTextures::get_busy_texture(int x, int y, int direction) const {
 	return *busy_textures_.at((x + y + direction) % busy_textures_.size());
 }
 
-void RoadTextures::add_normal_road_texture(std::unique_ptr<Texture> texture) {
-	normal_textures_.emplace_back(std::move(texture));
+void RoadTextures::add_normal_road_texture(const Image* image) {
+	normal_textures_.emplace_back(image);
 }
 
-void RoadTextures::add_busy_road_texture(std::unique_ptr<Texture> texture) {
-	busy_textures_.emplace_back(std::move(texture));
+void RoadTextures::add_busy_road_texture(const Image* image) {
+	busy_textures_.emplace_back(image);
 }

=== modified file 'src/logic/map_objects/tribes/road_textures.h'
--- src/logic/map_objects/tribes/road_textures.h	2015-11-28 22:29:26 +0000
+++ src/logic/map_objects/tribes/road_textures.h	2016-01-09 20:23:46 +0000
@@ -23,23 +23,23 @@
 #include <memory>
 #include <vector>
 
-#include "graphic/texture.h"
+#include "graphic/image.h"
 
 // Simple container to give access of the road textures of a tribe.
 class RoadTextures {
 public:
 	// Returns the road texture that should be used for the Cooordinate x, y and
 	// the road going into direction 'direction' (which can be any number).
-	const Texture& get_normal_texture(int x, int y, int direction) const;
-	const Texture& get_busy_texture(int x, int y, int direction) const;
+	const Image& get_normal_texture(int x, int y, int direction) const;
+	const Image& get_busy_texture(int x, int y, int direction) const;
 
 	// Adds a new road texture.
-	void add_normal_road_texture(std::unique_ptr<Texture> texture);
-	void add_busy_road_texture(std::unique_ptr<Texture> texture);
+	void add_normal_road_texture(const Image* texture);
+	void add_busy_road_texture(const Image* texture);
 
 private:
-	std::vector<std::unique_ptr<Texture>> normal_textures_;
-	std::vector<std::unique_ptr<Texture>> busy_textures_;
+	std::vector<const Image*> normal_textures_;
+	std::vector<const Image*> busy_textures_;
 };
 
 #endif  // end of include guard: WL_LOGIC_MAP_OBJECTS_TRIBES_ROAD_TEXTURES_H

=== modified file 'src/logic/map_objects/tribes/tribe_descr.cc'
--- src/logic/map_objects/tribes/tribe_descr.cc	2016-01-08 21:00:39 +0000
+++ src/logic/map_objects/tribes/tribe_descr.cc	2016-01-09 20:23:46 +0000
@@ -319,12 +319,12 @@
 	return busy_road_paths_;
 }
 
-void TribeDescr::add_normal_road_texture(std::unique_ptr<Texture> texture) {
-	road_textures_.add_normal_road_texture(std::move(texture));
+void TribeDescr::add_normal_road_texture(const Image* texture) {
+	road_textures_.add_normal_road_texture(texture);
 }
 
-void TribeDescr::add_busy_road_texture(std::unique_ptr<Texture> texture) {
-	road_textures_.add_busy_road_texture(std::move(texture));
+void TribeDescr::add_busy_road_texture(const Image* texture) {
+	road_textures_.add_busy_road_texture(texture);
 }
 
 const RoadTextures& TribeDescr::road_textures() const {

=== modified file 'src/logic/map_objects/tribes/tribe_descr.h'
--- src/logic/map_objects/tribes/tribe_descr.h	2015-11-28 22:29:26 +0000
+++ src/logic/map_objects/tribes/tribe_descr.h	2016-01-09 20:23:46 +0000
@@ -114,10 +114,9 @@
 	const std::vector<std::string>& normal_road_paths() const;
 	const std::vector<std::string>& busy_road_paths() const;
 
-	// Add the corresponding texture (which probably resides in a
-	// texture atlas) for roads.
-	void add_normal_road_texture(std::unique_ptr<Texture> texture);
-	void add_busy_road_texture(std::unique_ptr<Texture> texture);
+	// Add the corresponding texture for roads.
+	void add_normal_road_texture(const Image* texture);
+	void add_busy_road_texture(const Image* texture);
 
 	// The road textures used for drawing roads.
 	const RoadTextures& road_textures() const;

=== modified file 'src/logic/map_objects/tribes/tribes.cc'
--- src/logic/map_objects/tribes/tribes.cc	2016-01-09 20:23:45 +0000
+++ src/logic/map_objects/tribes/tribes.cc	2016-01-09 20:23:46 +0000
@@ -22,9 +22,6 @@
 #include <memory>
 
 #include "graphic/graphic.h"
-#include "graphic/image_io.h"
-#include "graphic/texture_atlas.h"
-#include "io/filesystem/layered_filesystem.h"
 #include "logic/game_data_error.h"
 
 namespace Widelands {
@@ -343,38 +340,15 @@
 
 void Tribes::load_graphics()
 {
-	// Construct and hold on to the texture atlas that contains all road images.
-	TextureAtlas ta;
-
 	// These will be deleted at the end of the method.
 	std::vector<std::unique_ptr<Texture>> individual_textures_;
 	for (size_t tribeindex = 0; tribeindex < nrtribes(); ++tribeindex) {
 		TribeDescr* tribe = tribes_->get_mutable(tribeindex);
 		for (const std::string& texture_path : tribe->normal_road_paths()) {
-			individual_textures_.emplace_back(load_image(texture_path, g_fs));
-			ta.add(*individual_textures_.back());
+			tribe->add_normal_road_texture(g_gr->images().get(texture_path));
 		}
 		for (const std::string& texture_path : tribe->busy_road_paths()) {
-			individual_textures_.emplace_back(load_image(texture_path, g_fs));
-			ta.add(*individual_textures_.back());
-		}
-	}
-
-	std::vector<TextureAtlas::PackedTexture> packed_texture;
-	std::vector<std::unique_ptr<Texture>> texture_atlases;
-	ta.pack(1024, &texture_atlases, &packed_texture);
-
-	assert(texture_atlases.size() == 1);
-	road_texture_ = std::move(texture_atlases[0]);
-
-	size_t next_texture_to_move = 0;
-	for (size_t tribeindex = 0; tribeindex < nrtribes(); ++tribeindex) {
-		TribeDescr* tribe = tribes_->get_mutable(tribeindex);
-		for (size_t i = 0; i < tribe->normal_road_paths().size(); ++i) {
-			tribe->add_normal_road_texture(std::move(packed_texture.at(next_texture_to_move++).texture));
-		}
-		for (size_t i = 0; i < tribe->busy_road_paths().size(); ++i) {
-			tribe->add_busy_road_texture(std::move(packed_texture.at(next_texture_to_move++).texture));
+			tribe->add_busy_road_texture(g_gr->images().get(texture_path));
 		}
 	}
 }

=== modified file 'src/logic/map_objects/tribes/tribes.h'
--- src/logic/map_objects/tribes/tribes.h	2015-11-28 22:29:26 +0000
+++ src/logic/map_objects/tribes/tribes.h	2016-01-09 20:23:46 +0000
@@ -154,8 +154,6 @@
 	std::unique_ptr<DescriptionMaintainer<WorkerDescr>> workers_;
 	std::unique_ptr<DescriptionMaintainer<TribeDescr>> tribes_;
 
-	std::unique_ptr<Texture> road_texture_; // Used in loading the road texture graphics
-
 	DISALLOW_COPY_AND_ASSIGN(Tribes);
 };
 

=== modified file 'src/logic/map_objects/world/terrain_description.cc'
--- src/logic/map_objects/world/terrain_description.cc	2015-12-11 19:06:50 +0000
+++ src/logic/map_objects/world/terrain_description.cc	2016-01-09 20:23:46 +0000
@@ -160,15 +160,15 @@
 TerrainDescription::~TerrainDescription() {
 }
 
-const Texture& TerrainDescription::get_texture(uint32_t gametime) const {
+const Image& TerrainDescription::get_texture(uint32_t gametime) const {
 	return *textures_.at((gametime / frame_length_) % textures_.size());
 }
 
-void TerrainDescription::add_texture(std::unique_ptr<Texture> texture) {
+void TerrainDescription::add_texture(const Image* texture) {
 	if (texture->width() != kTextureSideLength || texture->height() != kTextureSideLength) {
 		throw wexception("Tried to add a texture with wrong size.");
 	}
-	textures_.emplace_back(std::move(texture));
+	textures_.emplace_back(texture);
 }
 
 const std::vector<std::string>& TerrainDescription::texture_paths() const {

=== modified file 'src/logic/map_objects/world/terrain_description.h'
--- src/logic/map_objects/world/terrain_description.h	2015-12-11 19:06:50 +0000
+++ src/logic/map_objects/world/terrain_description.h	2016-01-09 20:23:46 +0000
@@ -73,8 +73,8 @@
 	const std::vector<std::string>& texture_paths() const;
 
 	/// Returns the texture for the given gametime.
-	const Texture& get_texture(uint32_t gametime) const;
-	void add_texture(std::unique_ptr<Texture> texture);
+	const Image& get_texture(uint32_t gametime) const;
+	void add_texture(const Image* texture);
 
 	// Sets the base minimap color.
 	void set_minimap_color(const RGBColor& color);
@@ -140,7 +140,7 @@
 	double fertility_;
 	double humidity_;
 	std::vector<std::string> texture_paths_;
-	std::vector<std::unique_ptr<Texture>> textures_;
+	std::vector<const Image*> textures_;
 	RGBColor    minimap_colors_[256];
 
 	DISALLOW_COPY_AND_ASSIGN(TerrainDescription);

=== modified file 'src/logic/map_objects/world/world.cc'
--- src/logic/map_objects/world/world.cc	2016-01-09 20:23:45 +0000
+++ src/logic/map_objects/world/world.cc	2016-01-09 20:23:46 +0000
@@ -23,8 +23,13 @@
 
 #include "base/i18n.h"
 #include "graphic/image_io.h"
+<<<<<<< TREE
 #include "graphic/texture.h"
 #include "graphic/texture_atlas.h"
+=======
+#include "logic/bob.h"
+#include "logic/critter.h"
+>>>>>>> MERGE-SOURCE
 #include "logic/game_data_error.h"
 #include "logic/map_objects/bob.h"
 #include "logic/map_objects/immovable.h"
@@ -49,11 +54,6 @@
 }
 
 void World::load_graphics() {
-	TextureAtlas ta;
-
-	// These will be deleted at the end of the method.
-	std::vector<std::unique_ptr<Texture>> individual_textures_;
-
 	for (size_t i = 0; i < terrains_->size(); ++i) {
 		TerrainDescription* terrain = terrains_->get_mutable(i);
 		for (size_t j = 0; j < terrain->texture_paths().size(); ++j) {
@@ -67,23 +67,7 @@
 				terrain->set_minimap_color(
 				   RGBColor(top_left_pixel_color.r, top_left_pixel_color.g, top_left_pixel_color.b));
 			}
-			individual_textures_.emplace_back(new Texture(sdl_surface));
-			ta.add(*individual_textures_.back());
-		}
-	}
-
-	std::vector<TextureAtlas::PackedTexture> packed_texture;
-	std::vector<std::unique_ptr<Texture>> texture_atlases;
-	ta.pack(1024, &texture_atlases, &packed_texture);
-
-	assert(texture_atlases.size() == 1);
-	terrain_texture_ = std::move(texture_atlases[0]);
-
-	int next_texture_to_move = 0;
-	for (size_t i = 0; i < terrains_->size(); ++i) {
-		TerrainDescription* terrain = terrains_->get_mutable(i);
-		for (size_t j = 0; j < terrain->texture_paths().size(); ++j) {
-			terrain->add_texture(std::move(packed_texture.at(next_texture_to_move++).texture));
+			terrain->add_texture(g_gr->images().get(terrain->texture_paths()[j]));
 		}
 	}
 }

=== modified file 'src/logic/map_objects/world/world.h'
--- src/logic/map_objects/world/world.h	2015-11-28 22:29:26 +0000
+++ src/logic/map_objects/world/world.h	2016-01-09 20:23:46 +0000
@@ -28,7 +28,6 @@
 
 class LuaInterface;
 class LuaTable;
-class Texture;
 
 namespace Widelands {
 
@@ -99,7 +98,6 @@
 	std::unique_ptr<DescriptionMaintainer<ResourceDescription>> resources_;
 	std::unique_ptr<DescriptionMaintainer<EditorCategory>> editor_terrain_categories_;
 	std::unique_ptr<DescriptionMaintainer<EditorCategory>> editor_immovable_categories_;
-	std::unique_ptr<Texture> terrain_texture_;
 
 	DISALLOW_COPY_AND_ASSIGN(World);
 };

=== modified file 'src/wlapplication.cc'
--- src/wlapplication.cc	2015-11-28 22:29:26 +0000
+++ src/wlapplication.cc	2016-01-09 20:23:46 +0000
@@ -271,19 +271,32 @@
 	changedir_on_mac();
 	cleanup_replays();
 
-	// handling of graphics
-	init_hardware();
-
-	// This might grab the input.
-	refresh_graphics();
+	Section & config = g_options.pull_section("global");
+
+	//Start the SDL core
+	if (SDL_Init(SDL_INIT_VIDEO) == -1)
+		throw wexception("Failed to initialize SDL, no valid video driver: %s", SDL_GetError());
+
+	SDL_ShowCursor(SDL_DISABLE);
+	g_gr = new Graphic();
 
 	if (TTF_Init() == -1)
 		throw wexception
 			("True Type library did not initialize: %s\n", TTF_GetError());
 
-	UI::g_fh1 = UI::create_fonthandler(g_gr); // This will create the fontset, so loading it first.
+	UI::g_fh1 =
+	   UI::create_fonthandler(&g_gr->images());  // This will create the fontset, so loading it first.
 	UI::g_fh = new UI::FontHandler();
 
+	g_gr->initialize(config.get_int("xres", DEFAULT_RESOLUTION_W),
+	                 config.get_int("yres", DEFAULT_RESOLUTION_H),
+	                 config.get_bool("fullscreen", false));
+	g_sound_handler.init(); //  TODO(unknown): memory leak!
+
+
+	// This might grab the input.
+	refresh_graphics();
+
 	if (SDLNet_Init() == -1)
 		throw wexception("SDLNet_Init failed: %s\n", SDLNet_GetError());
 
@@ -779,33 +792,6 @@
 	}
 }
 
-/**
- * Start the hardware: switch to graphics mode, start sound handler
- *
- * \pre The locale must be known before calling this
- *
- * \return true if there were no fatal errors that prevent the game from running
- */
-bool WLApplication::init_hardware() {
-	Section & s = g_options.pull_section("global");
-
-	//Start the SDL core
-	if (SDL_Init(SDL_INIT_VIDEO) == -1)
-		throw wexception
-			("Failed to initialize SDL, no valid video driver: %s",
-			 SDL_GetError());
-
-	SDL_ShowCursor(SDL_DISABLE);
-
-	g_gr = new Graphic(s.get_int("xres", DEFAULT_RESOLUTION_W),
-	                   s.get_int("yres", DEFAULT_RESOLUTION_H),
-	                   s.get_bool("fullscreen", false));
-
-	g_sound_handler.init(); //  TODO(unknown): memory leak!
-
-	return true;
-}
-
 void WLApplication::shutdown_hardware()
 {
 	delete g_gr;

=== modified file 'src/wlapplication.h'
--- src/wlapplication.h	2015-11-08 17:31:06 +0000
+++ src/wlapplication.h	2016-01-09 20:23:46 +0000
@@ -191,7 +191,6 @@
 	void init_language();
 	void shutdown_settings();
 
-	bool init_hardware();
 	void shutdown_hardware();
 
 	void parse_commandline(int argc, char const * const * argv);


Follow ups