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

Proposed by SirVer
Status: Merged
Merged at revision: 7709
Proposed branch: lp:~widelands-dev/widelands/use_image_cache
Merge into: lp:widelands
Prerequisite: lp:~widelands-dev/widelands/full_texture_atlas
Diff against target: 2683 lines (+657/-944)
40 files modified
src/editor/ui_menus/editor_tool_set_terrain_options_menu.cc (+1/-1)
src/graphic/CMakeLists.txt (+7/-9)
src/graphic/blit_mode.h (+36/-0)
src/graphic/build_texture_atlas.cc (+42/-177)
src/graphic/build_texture_atlas.h (+38/-0)
src/graphic/font_handler1.cc (+2/-2)
src/graphic/font_handler1.h (+3/-3)
src/graphic/gl/blit_program.cc (+131/-298)
src/graphic/gl/blit_program.h (+78/-83)
src/graphic/gl/dither_program.cc (+10/-25)
src/graphic/gl/draw_line_program.cc (+5/-9)
src/graphic/gl/fill_rect_program.cc (+4/-6)
src/graphic/gl/road_program.cc (+7/-15)
src/graphic/gl/terrain_program.cc (+5/-15)
src/graphic/gl/utils.cc (+72/-0)
src/graphic/gl/utils.h (+49/-11)
src/graphic/graphic.cc (+36/-28)
src/graphic/graphic.h (+7/-4)
src/graphic/image_cache.cc (+12/-30)
src/graphic/image_cache.h (+17/-32)
src/graphic/render_queue.cc (+9/-39)
src/graphic/render_queue.h (+4/-11)
src/graphic/screen.cc (+16/-11)
src/graphic/surface.h (+2/-2)
src/graphic/text/test/render_richtext.cc (+2/-1)
src/graphic/texture.cc (+12/-13)
src/logic/CMakeLists.txt (+0/-1)
src/logic/map_info.cc (+2/-1)
src/logic/map_objects/tribes/road_textures.cc (+6/-6)
src/logic/map_objects/tribes/road_textures.h (+7/-7)
src/logic/map_objects/tribes/tribe_descr.cc (+4/-4)
src/logic/map_objects/tribes/tribe_descr.h (+3/-4)
src/logic/map_objects/tribes/tribes.cc (+2/-28)
src/logic/map_objects/tribes/tribes.h (+0/-2)
src/logic/map_objects/world/terrain_description.cc (+3/-3)
src/logic/map_objects/world/terrain_description.h (+3/-3)
src/logic/map_objects/world/world.cc (+1/-24)
src/logic/map_objects/world/world.h (+0/-2)
src/wlapplication.cc (+19/-33)
src/wlapplication.h (+0/-1)
To merge this branch: bzr merge lp:~widelands-dev/widelands/use_image_cache
Reviewer Review Type Date Requested Status
TiborB Approve
SirVer Needs Resubmitting
Nicolai Hähnle Needs Fixing
Tino Needs Fixing
kaputtnik (community) Approve
Review via email: mp+282106@code.launchpad.net

Commit message

- Build a texture atlas with the most commonly used images on startup.
- 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.
- Apply suggestion by nha: Recreate buffers on the GPU on each frame instead of trying to reuse the memory.

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. Census & statistics were disabled since they do not benefit from the texture atlas at all.

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: 28% CPU load, 3000 OpenGL calls per frame.

An experimental version that loaded every image in Widelands into one 16kx16k texture atlas (1 GB of GPU RAMs was needed): 18% CPU load, 209 OpenGL calls. This proved unsustainable for most GPUs so it was dropped again.

Worst to best rendering now only takes 32% CPU and 8% GL calls of what it used to.

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. This is described in bug 1121982. I am not motivated to work on this now though and I only consider it a nice to have.

Description of the change

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

To post a comment you must log in.
Revision history for this message
bunnybot (widelandsofficial) wrote :

Hi, I am bunnybot (https://github.com/widelands/bunnybot).

I am keeping the source branch lp:~widelands-dev/widelands/use_image_cache mirrored to https://github.com/widelands/widelands/tree/_widelands_dev_widelands_use_image_cache

You can give me commands by starting a line with @bunnybot <command>. I understand:
 merge: Merges the source branch into the target branch, closing the merge proposal. I will use the proposed commit message if it is set.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Travis build 233 has changed state to: failed. Details: https://travis-ci.org/widelands/widelands/builds/101329419.

Revision history for this message
Nicolai Hähnle (nha) wrote :
Download full text (3.8 KiB)

This is an impressive improvement! Out of curiosity, how large does the texture atlas end up being?

I have mostly looked at the actual drawing code (I did notice a merge conflict marker in world.cc though, and bunnybot probably did, too ;)). As I already said today on IRC, for desktop OpenGL you should really use instancing. You can copy the Arguments structure almost 1:1 into a buffer with per-instance data and use a simple vertex shader to reduce the CPU load. I'd understand if you don't want separate paths for OpenGL ES 2.0 though.

The next-best thing I noticed is using glMapBuffer to avoid an unnecessary copy. Right now you fill the vertices_ vector (first "copy"). Then glBufferData copies the vertex data into the buffer object (second copy). And finally the vertex data gets uploaded to/pulled by the GPU.

Since you know the number of vertices up-front, it would be better to use glBufferData(..., NULL, ...) to reserve the required amount of space, then use glMapBuffer and write the vertex data directly into the buffer object, thus avoiding the second copy. (This would also apply to

(And now that I've written this, I notice that GLES 2.0 apparently doesn't have glMapBuffer... *sigh*)

Third, it would almost certainly be beneficial to use an element array. That would reduce the amount of vertex data you need to write by one third, and would also reduce the vertex shader load on the GPU by up to one third (Widelands almost certainly isn't shader bound, but hey... perhaps it saves some power). The element array can even be completely static - it just needs to grow as needed during application warm-up.

For the buffer handling, the following is actually best for the kind of streaming draw that Widelands is doing:
1. Determine a reasonably large buffer size N.
2. Use a single buffer object, initialized with glBufferData(..., N, NULL, GL_STREAM_DRAW);
3. As you go along drawing a frame, fill the buffer object from front to back. For all vertex data that you write, use glMapBufferRange to map _exactly_ the range that you want to write for the next draw call, then unmap, then do the draw call.
4. When the vertex data for the next draw call wouldn't fit in the remaining space of the buffer, start from the front, but (and this is VERY important, I've seen plenty of applications screw this up) add the GL_MAP_INVALIDATE_BUFFER_BIT.

What happens when you do this is that the driver magically maintains a pool of underlying buffers, all of size N. Whenever you start from 0 with the MAP_INVALIDATE_BUFFER_BIT, the driver switches the underlying buffer to one from the pool that is no longer in-flight. As far as I know, all desktop GL drivers do this.

Now you see that the patch I sent isn't actually optimal. It would be even better to do something like this:

GLsizeiptr bytes = items.size() * sizeof(T);

if (bytes > size_) {
  glBufferData(GL_ARRAY_BUFFER, bytes, items.data(), GL_STREAM_DRAW);
  size_ = bytes;
  filled_ = bytes;
} else {
  GLbitfield access = GL_MAP_WRITE_BIT;

  if (filled_ + bytes > size_) {
    filled_ = 0;
    access |= GL_MAP_INVALIDATE_BUFFER_BIT;
  }

  void *map = glMapBufferRange(GL_ARRAY_BUFFER, filled_, bytes, access);
  memc...

Read more...

Revision history for this message
SirVer (sirver) wrote :

> This is an impressive improvement! Out of curiosity, how large does the texture atlas end up being?

All images fit into 16kx16k, but since most GPUs seem to support 8k we have 2 textures. This could be improved by doing what we started once before, but never finished: compactifying the animations by finding common rectangles. This could be done when the atlas is constructed and would reduce memory foodprint a lot.

[Element Array]
I did experiment with an element array for a bit and it seems to buy some CPU too, but I had trouble keeping this indirection in my head during coding, so I cut it eventually. I agree that it would be better design.

[Buffer magic]
That sounds interesting, but would require some redesign in the current draw approach. I am very weary working on OpenGL related stuff, so I'll call it good enough for now. If you are interested in experimenting further, that would of course be greatly appreciated!

Revision history for this message
kaputtnik (franku) wrote :

Thanks for adding the file :-) I just compile and want to test.

Revision history for this message
Tino (tino79) wrote :

Compiles and runs fine on windows. I would really like to see this land in trunk as soon as possible.

review: Approve
Revision history for this message
kaputtnik (franku) wrote :

Yes, great enhancement. No flickering anymore (which came up with the new font renderer):
- Fixes bug 1509791
- may this could help also fixing the bug related to some wrong colored shadows? See bug 1519361

A suggestion, if possible. After the image atlas is created a black "Loading images" screen appears. It would be nice if the splash screen (the one where one has to click) could be used as the background for "Loading images". And yes, of course, a progress bar when creating the texture atlas ... :-D

review: Approve
Revision history for this message
Tino (tino79) wrote :

Currently the images in campaigns are not cached.
Starting any campaign mission fails.

review: Needs Fixing
Revision history for this message
bunnybot (widelandsofficial) wrote :

Travis build 235 has changed state to: passed. Details: https://travis-ci.org/widelands/widelands/builds/101396647.

Revision history for this message
SirVer (sirver) wrote :

Images in campaigns are fixed now.

review: Needs Resubmitting
Revision history for this message
Nicolai Hähnle (nha) wrote :

I wanted to test this branch on my laptop right now, and I have to say it's a bit of a nightmare, all of which can be roughly summarized as "this is probably not a good idea when VRAM is small". Let me list a number of problems in no particular order:

1) Our driver correctly announces support for textures up to 16384 in size, but when you actually allocate such a texture, it is 1GB large, which is going to fail when VRAM is not that big. So that possibility is something that the code should account for.

2) It seems there are plenty of fun bugs in our driver when textures become gigantic, so on the plus side, this is a great test case for making our driver more robust...

3) When I force max_size to 4096, I no longer hit critical driver bugs (only ones leading to rendering errors, oh well). There is still a problem though because the atlas textures add up to something like 110 million texels, which means that the textures are going to take more than 400MB (okay, maybe slightly under since there are also mask textures), which means that on integrated GPUs, you get texture thrashing. (Mine has a 512MB VRAM reservation, and whether it starts thrashing or not seems to depend on what compiz is currently doing - if I only had a single monitor running Widelands fullscreen it would probably okay, but as is performance is occasionally killed completely.)

Even without texture atlases, an integrated GPU is going to swap textures occasionally, but it's going to happen much less because the working set is smaller. With texture atlases, your working set is going to be everything almost all of the time.

This sucks. Summary:

(1) Trying to reduce the atlas size is more important than you thought.

(2) Probably the best quick fix option you have is to check VRAM size (e.g. using GLX_MESA_query_renderer) and add some heuristic to disable texture atlases below a certain VRAM size. It should be possible to override the heuristic via the command line. It's kind of sad that you then have a performance optimization that doesn't help the hardware that needs it most...

(3) A longer term fix could be some kind of texture streaming. I actually don't know the best way to do this.

review: Needs Fixing
Revision history for this message
SirVer (sirver) wrote :

Nicolai, could you outline what VRAM means in this case? I did not understand a lot of your last post, it is full of things that I have no understanding of.

So far the only feedback I got is that this change improves the performance - I tested it on old macs too which has only integrated graphics.

Revision history for this message
TiborB (tiborb95) wrote :

If there is such problem it must be discussed thoroughly.

I just tested it on my linux box, with integrated intel HD 4000 and 8 GB RAM (this probably matters much) and no visible problems and it seems to be running nicely.

I googled a bit how to find out VRAM or whatever it is and found two commands (for linux):

$ dmesg | grep Memory
......
[ 6.992863] [drm] Memory usable by graphics device = 2048M

$ LC_ALL=C lspci -v | grep -EA10 "3D|VGA" | grep 'prefetchable'
 Memory at f7800000 (64-bit, non-prefetchable) [size=4M]
 Memory at e0000000 (64-bit, prefetchable) [size=256M]

It seems the first one shows maximum (2GB) and second one actual usage (256MB) - but I am not sure of interpretation...

But as I said - it runs OK here. Perhaps if more peoples tested it...

Revision history for this message
Nicolai Hähnle (nha) wrote :

VRAM is Video RAM, which in the case of integrated graphics is a part of the physical RAM which is reserved for the GPU. Run `free` to get an idea of how large it is approximately: the total memory shown will be less than the physical amount of RAM, reflecting how much is available to the CPU.

In addition, there's the GTT or GART, which is a virtual memory aperture through which the GPU can access system RAM. If not all data fits into VRAM, the GPU can still render from the GTT, but it tends to be slower. The 2048M shown in the dmesg log are probably VRAM + GTT.

It's possible that since Widelands doesn't use that much bandwidth, rendering from GTT (typically 1-2GB) would be fine.

Please do provide an option to disable the texture atlas, and I'll see what can be done about our driver.

Revision history for this message
TiborB (tiborb95) wrote :

@nha
Thanks for clarification.
I noticed when experimenting with this that it disabled (crashed ?) compton - this is a lightweight desktop 3D acceleration - but no big deal for me. So perhaps if you disabled compiz...
Also I wonder if the game will be capable to run in VirtualBox, but I dont have any virtual system at hand to test it...

Revision history for this message
GunChleoc (gunchleoc) wrote :

I have a VMWare VM, so I could eventually test it. I want to review the prerequisite branch first though (the VM makes my whole machine slow when linking...)

Revision history for this message
TiborB (tiborb95) wrote :

AFAIK VirtualBox is bit worse in regard to 3D acceleration, but testing in VMWare is also very usefull

Revision history for this message
SirVer (sirver) wrote :

I will look into running with a minimal texture atlas through a commandline option and profile how much performance that cost on my system.

Probably not before the weekend though.

In the meantime, more testing with virtual machines would be great!

Revision history for this message
TiborB (tiborb95) wrote :

I can not start it in VirtualBox (Mint in Arch linux) - I am getting segmentation fault while building the texture. But virtualbox simply is not good enough virtualization tool for running such game inside a guest I think

Revision history for this message
SirVer (sirver) wrote :

I need another round of testing, please.

I modified the code to only pack terrains, roads and tribe borders and the pics/ subdirectory into a texture atlas and load all other graphics as needed from disk. This is what Nicolai requested - the code requires terrains and roads to be in the same texture, so we need this minimal atlas always.

In my tests, the performance for this is comparable to using one atlas with 2048x2048 textures. It needs ~29% CPU and 3300 GL calls per frame. This is still less than half of what trunk currently needs (60% CU, 18300 GL calls), but not as good as one huge atlas (18% CPU, 210 GL calls).

Advantages of this are: building the atlas is very quick and there is no need to cache. Could I get some testing feedback?

Revision history for this message
kaputtnik (franku) wrote :

Runs fine, the time to create the atlas and loading images is very short on my old AMD CPU. Tested editor and a save game. File sizes:

ls -lh
insgesamt 2,4M
-rw-r--r-- 1 kaputtnik users 79K 14. Jan 16:36 texture_atlas.lua
-rw-r--r-- 1 kaputtnik users 2,3M 14. Jan 16:36 texture_atlas_00.png

CPU usage after loading a 5hrs save game:
This branch: ~32%
Current trunk: ~54%

Revision history for this message
TiborB (tiborb95) wrote :

Well, it compiled, building of that cache was fast, BUT I see a lot of output like this in console:

Image with hash tribes/workers/barbarians/lumberjack/walk_ne_08_pc.png not found. Loading from disk.
Image with hash tribes/workers/barbarians/lumberjack/walk_ne_09_pc.png not found. Loading from disk.

???

Revision history for this message
SirVer (sirver) wrote :

I changed the code back to load an image from disk if it was not already loaded. That is the current behavior in trunk too. I just added logging to get a feeling how often that actually happens. I'll remove it before merging.

nicolai, if you undo the last commit you have the originial behavior that creates a huge atlas if it can. I'll rework this branch to no longer cache the texture atlas and instead just regenerate the one with the most common images every time the game launches. We might revisit packing everything into an atlas once we go back to bug 1121982.

Revision history for this message
TiborB (tiborb95) wrote :

So it will not built it at once on start, but instead it will expand it on the fly as new images are needed during the game? Correct?

Revision history for this message
SirVer (sirver) wrote :

no. sorry, that explanation was confusing.

It will immediately on start build a small one (~1024x1200 pixels) that contains frequently used images like the UI elements, buildhelp & the roads and world textures. Every other image will be loaded as needed from disk and put into a separate texture each, i.e. no texture atlas.

Revision history for this message
TiborB (tiborb95) wrote :

OK, I like it.
And I think there will be no more testers, unless GunChleoc shows up.
I am willing to approve it if you think my approval is sufficient.

Of course there is still some NOCOM in diff and you have to mute a logging about images as you mentioned.

BTW, windows builds were tested?

Revision history for this message
SirVer (sirver) wrote :

Okay, I simplified the code as there is no need to cache the images anymore on disk - building the atlas is fast. I updated the commit message, merged trunk and removed codecheck warnings. Tibor, if you approve of the code, could you merge it in?

review: Needs Resubmitting
Revision history for this message
GunChleoc (gunchleoc) wrote :

I'm online today, so I can do some testing.

Revision history for this message
kaputtnik (franku) wrote :

Is there a great difference between fullscreen and window mode? I used windowed mode to have the task manager open to see the CPU usage.

I just tested a little game on my laptop and this branch as current trunk uses about 12% CPU... So no difference?

Revision history for this message
SirVer (sirver) wrote :

fullscreen and window will likely not make a huge difference. But the size of the widelands drawing area (i.e. resolution in game) and the amount of objects on it. The more different objects (terrains, roads, buildings, workers, critters and windows) the higher the difference in load.

Also make sure to pause the game, otherwise the game logic will interfere with your benchmark.

If your GPU is super quick at swapping textures, the difference to the renderqueue branch might be small. However, this branch should in no case be slower than trunk.

Revision history for this message
GunChleoc (gunchleoc) wrote :

I gave this a quick spin in my VM and it runs fine. There is a problem with drawing rectangles though - got probably introduced in one of the previous graphics branches.

https://launchpadlibrarian.net/234364565/rectangles.png

Revision history for this message
SirVer (sirver) wrote :

I think this is https://bugs.launchpad.net/widelands/+bug/1531114. I will look into that soonish. Can this branch go in?

Revision history for this message
TiborB (tiborb95) wrote :

I have the same problem as GunChleoc - though I did not notice it.

Otherwise, it works nicely

Revision history for this message
TiborB (tiborb95) wrote :

I am all for merging it.

review: Approve
Revision history for this message
GunChleoc (gunchleoc) wrote :

Looks like my bug is https://bugs.launchpad.net/widelands/+bug/1531114 indeed - seems more pronounced with small screen resolutions maybe?

I won't have time to look at the code until end of next week, but if everything has been checked, it can go in.

Revision history for this message
SirVer (sirver) wrote :

Thanks for testing everybody! Thanks for reviewing, Tibor.

@bunnybot merge

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== 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-16 20:47:43 +0000
@@ -51,7 +51,7 @@
51 std::vector<std::string> tooltips;51 std::vector<std::string> tooltips;
5252
53 // Blit the main terrain image53 // Blit the main terrain image
54 const Texture& terrain_texture = terrain_descr.get_texture(0);54 const Image& terrain_texture = terrain_descr.get_texture(0);
55 Texture* texture = new Texture(terrain_texture.width(), terrain_texture.height());55 Texture* texture = new Texture(terrain_texture.width(), terrain_texture.height());
56 texture->blit(Rect(0, 0, terrain_texture.width(), terrain_texture.height()),56 texture->blit(Rect(0, 0, terrain_texture.width(), terrain_texture.height()),
57 terrain_texture,57 terrain_texture,
5858
=== modified file 'src/graphic/CMakeLists.txt'
--- src/graphic/CMakeLists.txt 2016-01-07 12:47:17 +0000
+++ src/graphic/CMakeLists.txt 2016-01-16 20:47:43 +0000
@@ -3,19 +3,16 @@
3# TODO(sirver): Separate this directory into a base directory and one3# TODO(sirver): Separate this directory into a base directory and one
4# that is Widelands aware (can include logic stuff).4# that is Widelands aware (can include logic stuff).
55
6# A binary that creates and writes out a texture atlas of all images in6wl_library(graphic_build_texture_atlas
7# a directory.
8wl_binary(wl_make_texture_atlas
9 SRCS7 SRCS
10 make_texture_atlas_main.cc8 build_texture_atlas.h
9 build_texture_atlas.cc
11 DEPENDS10 DEPENDS
12 base_log
13 graphic11 graphic
14 graphic_image_io12 graphic_image_io
13 graphic_surface
15 graphic_texture_atlas14 graphic_texture_atlas
16 helper
17 io_filesystem15 io_filesystem
18 io_stream
19)16)
2017
21wl_library(graphic_color18wl_library(graphic_color
@@ -32,7 +29,6 @@
32 DEPENDS29 DEPENDS
33 base_exceptions30 base_exceptions
34 base_geometry31 base_geometry
35 base_log
36 base_macros32 base_macros
37 graphic_color33 graphic_color
38 graphic_terrain_programs34 graphic_terrain_programs
@@ -128,6 +124,7 @@
128wl_library(graphic_draw_programs124wl_library(graphic_draw_programs
129 SRCS125 SRCS
130 blend_mode.h126 blend_mode.h
127 blit_mode.h
131 gl/blit_program.cc128 gl/blit_program.cc
132 gl/blit_program.h129 gl/blit_program.h
133 gl/draw_line_program.cc130 gl/draw_line_program.cc
@@ -244,10 +241,11 @@
244 base_geometry241 base_geometry
245 base_i18n242 base_i18n
246 base_log243 base_log
247 graphic_draw_programs
248 base_macros244 base_macros
249 build_info245 build_info
246 graphic_build_texture_atlas
250 graphic_color247 graphic_color
248 graphic_draw_programs
251 graphic_gl_utils249 graphic_gl_utils
252 graphic_image_cache250 graphic_image_cache
253 graphic_image_io251 graphic_image_io
254252
=== 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-16 20:47:43 +0000
@@ -0,0 +1,36 @@
1/*
2 * Copyright (C) 2006-2016 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 *
18 */
19
20#ifndef WL_GRAPHIC_BLIT_MODE_H
21#define WL_GRAPHIC_BLIT_MODE_H
22
23// The type of blit performed.
24enum class BlitMode {
25 // Blit texture unchanged.
26 kDirect,
27
28 // Blit texture desaturated and maybe tinted with a color.
29 kMonochrome,
30
31 // Blit texture tinted with a color everywhere where a mask is not
32 // transparent,
33 kBlendedWithMask,
34};
35
36#endif // end of include guard: WL_GRAPHIC_BLIT_MODE_H
037
=== renamed file 'src/graphic/make_texture_atlas_main.cc' => 'src/graphic/build_texture_atlas.cc'
--- src/graphic/make_texture_atlas_main.cc 2016-01-12 21:26:15 +0000
+++ src/graphic/build_texture_atlas.cc 2016-01-16 20:47:43 +0000
@@ -17,29 +17,21 @@
17 *17 *
18 */18 */
1919
20#include <fstream>20#include "graphic/build_texture_atlas.h"
21#include <iostream>21
22#include <map>
22#include <memory>23#include <memory>
23#include <set>
24#include <string>24#include <string>
25#include <unordered_set>25#include <unordered_set>
26#include <vector>26#include <vector>
2727
28#include <SDL.h>
29#include <boost/algorithm/string/predicate.hpp>28#include <boost/algorithm/string/predicate.hpp>
30#include <boost/format.hpp>29
31
32#undef main // No, we do not want SDL_main
33
34#include "base/log.h"
35#include "config.h"
36#include "graphic/graphic.h"30#include "graphic/graphic.h"
37#include "graphic/image_io.h"31#include "graphic/image_io.h"
38#include "graphic/texture_atlas.h"32#include "graphic/texture_atlas.h"
39#include "helper.h"
40#include "io/filesystem/filesystem.h"33#include "io/filesystem/filesystem.h"
41#include "io/filesystem/layered_filesystem.h"34#include "io/filesystem/layered_filesystem.h"
42#include "io/streamwrite.h"
4335
44namespace {36namespace {
4537
@@ -47,47 +39,10 @@
47// threshold, but not background pictures.39// threshold, but not background pictures.
48constexpr int kMaxAreaForTextureAtlas = 240 * 240;40constexpr int kMaxAreaForTextureAtlas = 240 * 240;
4941
42// A graphics card must at least support this size for texture for Widelands to
43// run.
50constexpr int kMinimumSizeForTextures = 2048;44constexpr int kMinimumSizeForTextures = 2048;
5145
52// An image can either be Type::kPacked inside a texture atlas, in which case
53// we need to keep track which one and where inside of that one. It can also be
54// Type::kUnpacked if it is to be loaded from disk.
55struct PackInfo {
56 enum class Type {
57 kUnpacked,
58 kPacked,
59 };
60
61 Type type;
62 int texture_atlas;
63 Rect rect;
64};
65
66int parse_arguments(
67 int argc, char** argv, int* max_size)
68{
69 if (argc < 2) {
70 std::cout << "Usage: wl_make_texture_atlas [max_size]" << std::endl << std::endl
71 << "Will write output.png in the current directory." << std::endl;
72 return 1;
73 }
74 *max_size = atoi(argv[1]);
75 if (*max_size < kMinimumSizeForTextures) {
76 std::cout << "Widelands requires at least 2048 for the smallest texture size." << std::endl;
77 return 1;
78 }
79 return 0;
80}
81
82// Setup the static objects Widelands needs to operate and initializes systems.
83void initialize() {
84 SDL_Init(SDL_INIT_VIDEO);
85
86 g_fs = new LayeredFileSystem();
87 g_fs->add_file_system(&FileSystem::create(INSTALL_DATADIR));
88 g_gr = new Graphic(1, 1, false);
89}
90
91// Returns true if 'filename' ends with an image extension.46// Returns true if 'filename' ends with an image extension.
92bool is_image(const std::string& filename) {47bool is_image(const std::string& filename) {
93 return boost::ends_with(filename, ".png") || boost::ends_with(filename, ".jpg");48 return boost::ends_with(filename, ".png") || boost::ends_with(filename, ".jpg");
@@ -113,68 +68,34 @@
113 }68 }
114}69}
11570
116void dump_result(const std::map<std::string, PackInfo>& pack_info,71// If 'filename' should end up in the texture atlas, will load it into 'image'
117 std::vector<std::unique_ptr<Texture>>* texture_atlases,72// and return true.
118 FileSystem* fs) {73bool should_be_packed(const std::string& filename, std::unique_ptr<Texture>* image) {
11974 if (boost::ends_with(filename, ".jpg")) {
120 for (size_t i = 0; i < texture_atlases->size(); ++i) {75 return false;
121 std::unique_ptr<StreamWrite> sw(76 }
122 fs->open_stream_write((boost::format("output_%02i.png") % i).str()));77 *image = load_image(filename, g_fs);
123 save_to_png(texture_atlases->at(i).get(), sw.get(), ColorType::RGBA);78 const auto area = (*image)->width() * (*image)->height();
124 }79 if (area > kMaxAreaForTextureAtlas) {
12580 return false;
126 {81 }
127 std::unique_ptr<StreamWrite> sw(fs->open_stream_write("output.lua"));82 return true;
128 sw->text("return {\n");
129 for (const auto& pair : pack_info) {
130 sw->text(" [\"");
131 sw->text(pair.first);
132 sw->text("\"] = {\n");
133
134 switch (pair.second.type) {
135 case PackInfo::Type::kPacked:
136 sw->text(" type = \"packed\",\n");
137 sw->text(
138 (boost::format(" texture_atlas = %d,\n") % pair.second.texture_atlas).str());
139 sw->text((boost::format(" rect = { %d, %d, %d, %d },\n") % pair.second.rect.x %
140 pair.second.rect.y % pair.second.rect.w % pair.second.rect.h).str());
141 break;
142
143 case PackInfo::Type::kUnpacked:
144 sw->text(" type = \"unpacked\",\n");
145 break;
146 }
147 sw->text(" },\n");
148 }
149 sw->text("}\n");
150 }
151}83}
15284
153// Pack the images in 'filenames' into texture atlases.85// Pack the images in 'filenames' into texture atlases.
154std::vector<std::unique_ptr<Texture>> pack_images(const std::vector<std::string>& filenames,86std::vector<std::unique_ptr<Texture>>
155 const int max_size,87pack_images(const std::vector<std::string>& filenames,
156 std::map<std::string, PackInfo>* pack_info,88 const int max_size,
157 Texture* first_texture,89 std::map<std::string, std::unique_ptr<Texture>>* textures_in_atlas) {
158 TextureAtlas::PackedTexture* first_atlas_packed_texture) {
159 std::vector<std::pair<std::string, std::unique_ptr<Texture>>> to_be_packed;90 std::vector<std::pair<std::string, std::unique_ptr<Texture>>> to_be_packed;
160 for (const auto& filename : filenames) {91 for (const auto& filename : filenames) {
161 std::unique_ptr<Texture> image = load_image(filename, g_fs);92 std::unique_ptr<Texture> image;
162 const auto area = image->width() * image->height();93 if (should_be_packed(filename, &image)) {
163 if (area < kMaxAreaForTextureAtlas) {
164 to_be_packed.push_back(std::make_pair(filename, std::move(image)));94 to_be_packed.push_back(std::make_pair(filename, std::move(image)));
165 } else {
166 pack_info->insert(std::make_pair(filename, PackInfo{
167 PackInfo::Type::kUnpacked, 0, Rect(),
168 }));
169 }95 }
170 }96 }
17197
172 TextureAtlas atlas;98 TextureAtlas atlas;
173 int packed_texture_index = 0;
174 if (first_texture != nullptr) {
175 atlas.add(*first_texture);
176 packed_texture_index = 1;
177 }
178 for (auto& pair : to_be_packed) {99 for (auto& pair : to_be_packed) {
179 atlas.add(*pair.second);100 atlas.add(*pair.second);
180 }101 }
@@ -183,92 +104,36 @@
183 std::vector<TextureAtlas::PackedTexture> packed_textures;104 std::vector<TextureAtlas::PackedTexture> packed_textures;
184 atlas.pack(max_size, &texture_atlases, &packed_textures);105 atlas.pack(max_size, &texture_atlases, &packed_textures);
185106
186 if (first_texture != nullptr) {
187 assert(first_atlas_packed_texture != nullptr);
188 *first_atlas_packed_texture = std::move(packed_textures[0]);
189 }
190
191 for (size_t i = 0; i < to_be_packed.size(); ++i) {107 for (size_t i = 0; i < to_be_packed.size(); ++i) {
192 const auto& packed_texture = packed_textures.at(packed_texture_index++);108 textures_in_atlas->insert(
193 pack_info->insert(109 std::make_pair(to_be_packed[i].first, std::move(packed_textures[i].texture)));
194 std::make_pair(to_be_packed[i].first, PackInfo{PackInfo::Type::kPacked,
195 packed_texture.texture_atlas,
196 packed_texture.texture->blit_data().rect}));
197 }110 }
198 return texture_atlases;111 return texture_atlases;
199}112}
200113
201} // namespace114} // namespace
202115
203int main(int argc, char** argv) {116std::vector<std::unique_ptr<Texture>>
204 int max_size;117build_texture_atlas(const int max_size,
205 if (parse_arguments(argc, argv, &max_size))118 std::map<std::string, std::unique_ptr<Texture>>* textures_in_atlas) {
206 return 1;119 if (max_size < kMinimumSizeForTextures) {
207120 throw wexception("The texture atlas must use at least %d as size (%d was given)",
208 if (SDL_Init(SDL_INIT_VIDEO) < 0) {121 kMinimumSizeForTextures, max_size);
209 std::cerr << "SDLInit did not succeed: " << SDL_GetError() << std::endl;
210 return 1;
211 }122 }
212 initialize();123 std::vector<std::string> first_atlas_images;
213
214
215 // For performance reasons, we need to have some images in the first texture
216 // atlas, so that OpenGL texture switches do not happen during (for example)
217 // terrain or road rendering. To ensure this, we separate all images into
218 // two disjunct sets. We than pack all images that should go into the first
219 // texture atlas into a texture atlas. Then, we pack all remaining textures
220 // into a texture atlas, but including the first texture atlas as a singular
221 // image (which will probably be the biggest we allow).
222 //
223 // We have to adjust the sub rectangle rendering for the images in the first
224 // texture atlas in 'pack_info' later, before dumping the results.
225 std::vector<std::string> other_images, images_that_must_be_in_first_atlas;
226 std::unordered_set<std::string> all_images;124 std::unordered_set<std::string> all_images;
227125
228 // For terrain textures.126 // For terrain textures.
229 find_images("world/terrains", &all_images, &images_that_must_be_in_first_atlas);127 find_images("world/terrains", &all_images, &first_atlas_images);
230 // For flags and roads.128 // For flags and roads.
231 find_images("tribes/images", &all_images, &images_that_must_be_in_first_atlas);129 find_images("tribes/images", &all_images, &first_atlas_images);
232 // For UI elements mostly, but we get more than we need really.130 // For UI elements mostly, but we get more than we need really.
233 find_images("pics", &all_images, &images_that_must_be_in_first_atlas);131 find_images("pics", &all_images, &first_atlas_images);
234132
235 // Add all other images, we do not really cares about the order for these.133 auto first_texture_atlas = pack_images(first_atlas_images, max_size, textures_in_atlas);
236 find_images("world", &all_images, &other_images);
237 find_images("tribes", &all_images, &other_images);
238 assert(images_that_must_be_in_first_atlas.size() + other_images.size() == all_images.size());
239
240 std::map<std::string, PackInfo> first_texture_atlas_pack_info;
241 auto first_texture_atlas = pack_images(images_that_must_be_in_first_atlas, max_size,
242 &first_texture_atlas_pack_info, nullptr, nullptr);
243 if (first_texture_atlas.size() != 1) {134 if (first_texture_atlas.size() != 1) {
244 std::cout << "Not all images that should fit in the first texture atlas did actually fit."135 throw wexception("Not all images that should fit in the first texture atlas did actually "
245 << std::endl;136 "fit. Widelands has now more images than before.");
246 return 1;137 }
247 }138 return {std::move(first_texture_atlas)};
248
249 std::map<std::string, PackInfo> pack_info;
250 TextureAtlas::PackedTexture first_atlas_packed_texture;
251 auto texture_atlases = pack_images(other_images, max_size, &pack_info,
252 first_texture_atlas[0].get(), &first_atlas_packed_texture);
253
254 const auto& blit_data = first_atlas_packed_texture.texture->blit_data();
255 for (const auto& pair : first_texture_atlas_pack_info) {
256 assert(pack_info.count(pair.first) == 0);
257 pack_info.insert(std::make_pair(pair.first, PackInfo{
258 pair.second.type,
259 first_atlas_packed_texture.texture_atlas,
260 Rect(blit_data.rect.x + pair.second.rect.x,
261 blit_data.rect.y + pair.second.rect.y,
262 pair.second.rect.w, pair.second.rect.h),
263 }));
264 }
265
266 // Make sure we have all images.
267 assert(all_images.size() == pack_info.size());
268
269 std::unique_ptr<FileSystem> output_fs(&FileSystem::create("."));
270 dump_result(pack_info, &texture_atlases, output_fs.get());
271
272 SDL_Quit();
273 return 0;
274}139}
275140
=== added file 'src/graphic/build_texture_atlas.h'
--- src/graphic/build_texture_atlas.h 1970-01-01 00:00:00 +0000
+++ src/graphic/build_texture_atlas.h 2016-01-16 20:47:43 +0000
@@ -0,0 +1,38 @@
1/*
2 * Copyright (C) 2006-2016 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 *
18 */
19
20#ifndef WL_GRAPHIC_BUILD_TEXTURE_ATLAS_H
21#define WL_GRAPHIC_BUILD_TEXTURE_ATLAS_H
22
23#include <map>
24#include <memory>
25#include <vector>
26
27#include "graphic/texture.h"
28
29// Builds a texture atlas where no texture is bigger than 'max_size' x
30// 'max_size' using the most commonly used images like UI elements, roads and
31// textures. Returns the texture_atlases which must be kept around in memory
32// and fills in 'textures_in_atlas' which is a map from filename to Texture in
33// the atlas.
34std::vector<std::unique_ptr<Texture>>
35build_texture_atlas(
36 const int max_size, std::map<std::string, std::unique_ptr<Texture>>* textures_in_atlas);
37
38#endif // end of include guard: WL_GRAPHIC_BUILD_TEXTURE_ATLAS_H
039
=== 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-16 20:47:43 +0000
@@ -139,8 +139,8 @@
139 ImageCache* const image_cache_; // not owned139 ImageCache* const image_cache_; // not owned
140};140};
141141
142IFontHandler1 * create_fonthandler(Graphic* gr) {142IFontHandler1 * create_fonthandler(ImageCache* image_cache) {
143 return new FontHandler1(&gr->images());143 return new FontHandler1(image_cache);
144}144}
145145
146IFontHandler1 * g_fh1 = nullptr;146IFontHandler1 * g_fh1 = nullptr;
147147
=== 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-16 20:47:43 +0000
@@ -32,7 +32,7 @@
3232
33class FileSystem;33class FileSystem;
34class Image;34class Image;
35class Graphic;35class ImageCache;
3636
37namespace UI {37namespace UI {
3838
@@ -61,8 +61,8 @@
61 DISALLOW_COPY_AND_ASSIGN(IFontHandler1);61 DISALLOW_COPY_AND_ASSIGN(IFontHandler1);
62};62};
6363
64// Create a new FontHandler1. Ownership for the objects is not taken.64// Create a new FontHandler1.
65IFontHandler1 * create_fonthandler(Graphic* gr);65IFontHandler1 * create_fonthandler(ImageCache* image_cache);
6666
67extern IFontHandler1 * g_fh1;67extern IFontHandler1 * g_fh1;
6868
6969
=== 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-16 20:47:43 +0000
@@ -22,6 +22,7 @@
22#include <vector>22#include <vector>
2323
24#include "base/log.h"24#include "base/log.h"
25#include "graphic/blit_mode.h"
25#include "graphic/gl/blit_data.h"26#include "graphic/gl/blit_data.h"
26#include "graphic/gl/coordinate_conversion.h"27#include "graphic/gl/coordinate_conversion.h"
27#include "graphic/gl/utils.h"28#include "graphic/gl/utils.h"
@@ -36,52 +37,23 @@
36attribute vec2 attr_texture_position;37attribute vec2 attr_texture_position;
37attribute vec3 attr_position;38attribute vec3 attr_position;
38attribute vec4 attr_blend;39attribute vec4 attr_blend;
40attribute float attr_program_flavor;
3941
40varying vec2 out_mask_texture_coordinate;42varying vec2 out_mask_texture_coordinate;
41varying vec2 out_texture_coordinate;43varying vec2 out_texture_coordinate;
42varying vec4 out_blend;44varying vec4 out_blend;
45varying float out_program_flavor;
4346
44void main() {47void main() {
45 out_mask_texture_coordinate = attr_mask_texture_position;48 out_mask_texture_coordinate = attr_mask_texture_position;
46 out_texture_coordinate = attr_texture_position;49 out_texture_coordinate = attr_texture_position;
47 out_blend = attr_blend;50 out_blend = attr_blend;
51 out_program_flavor = attr_program_flavor;
48 gl_Position = vec4(attr_position, 1.);52 gl_Position = vec4(attr_position, 1.);
49}53}
50)";54)";
5155
52const char kVanillaBlitFragmentShader[] = R"(56const char kBlitFragmentShader[] = R"(
53#version 120
54
55uniform sampler2D u_texture;
56
57varying vec2 out_texture_coordinate;
58varying vec4 out_blend;
59
60void main() {
61 vec4 color = texture2D(u_texture, out_texture_coordinate);
62 gl_FragColor = color * out_blend;
63}
64)";
65
66const char kMonochromeBlitFragmentShader[] = R"(
67#version 120
68
69uniform sampler2D u_texture;
70
71varying vec2 out_texture_coordinate;
72varying vec4 out_blend;
73
74void main() {
75 vec4 texture_color = texture2D(u_texture, out_texture_coordinate);
76
77 // See http://en.wikipedia.org/wiki/YUV.
78 float luminance = dot(vec3(0.299, 0.587, 0.114), texture_color.rgb);
79
80 gl_FragColor = vec4(vec3(luminance) * out_blend.rgb, out_blend.a * texture_color.a);
81}
82)";
83
84const char kBlendedBlitFragmentShader[] = R"(
85#version 12057#version 120
8658
87uniform sampler2D u_texture;59uniform sampler2D u_texture;
@@ -90,16 +62,25 @@
90varying vec2 out_mask_texture_coordinate;62varying vec2 out_mask_texture_coordinate;
91varying vec2 out_texture_coordinate;63varying vec2 out_texture_coordinate;
92varying vec4 out_blend;64varying vec4 out_blend;
65varying float out_program_flavor;
9366
94void main() {67void main() {
95 vec4 texture_color = texture2D(u_texture, out_texture_coordinate);68 vec4 texture_color = texture2D(u_texture, out_texture_coordinate);
96 vec4 mask_color = texture2D(u_mask, out_mask_texture_coordinate);
9769
98 // See http://en.wikipedia.org/wiki/YUV.70 // See http://en.wikipedia.org/wiki/YUV.
99 float luminance = dot(vec3(0.299, 0.587, 0.114), texture_color.rgb);71 float luminance = dot(vec3(0.299, 0.587, 0.114), texture_color.rgb);
100 float blend_influence = mask_color.r * mask_color.a;72
101 gl_FragColor = vec4(73 if (out_program_flavor == 0.) {
102 mix(texture_color.rgb, out_blend.rgb * luminance, blend_influence), out_blend.a * texture_color.a);74 gl_FragColor = vec4(texture_color.rgb, out_blend.a * texture_color.a);
75 } else if (out_program_flavor == 1.) {
76 gl_FragColor = vec4(vec3(luminance) * out_blend.rgb, out_blend.a * texture_color.a);
77 } else {
78 vec4 mask_color = texture2D(u_mask, out_mask_texture_coordinate);
79 float blend_influence = mask_color.r * mask_color.a;
80 gl_FragColor = vec4(
81 mix(texture_color.rgb, out_blend.rgb * luminance, blend_influence),
82 out_blend.a * texture_color.a);
83 }
103}84}
104)";85)";
10586
@@ -117,104 +98,31 @@
11798
118} // namespace99} // namespace
119100
120class BlitProgram {101BlitProgram::BlitProgram() {
121public:102 gl_program_.build(kBlitVertexShader, kBlitFragmentShader);
122 struct Arguments {
123 FloatRect destination_rect;
124 float z_value;
125 BlitData texture;
126 BlitData mask;
127 RGBAColor blend;
128 BlendMode blend_mode;
129 };
130 BlitProgram(const std::string& fragment_shader);
131
132 void activate();
133
134 void draw_and_deactivate(const std::vector<Arguments>& arguments);
135
136 int program_object() const {
137 return gl_program_.object();
138 }
139
140private:
141 struct PerVertexData {
142 PerVertexData(float init_gl_x,
143 float init_gl_y,
144 float init_gl_z,
145 float init_texture_x,
146 float init_texture_y,
147 float init_mask_texture_x,
148 float init_mask_texture_y,
149 float init_blend_r,
150 float init_blend_g,
151 float init_blend_b,
152 float init_blend_a)
153 : gl_x(init_gl_x),
154 gl_y(init_gl_y),
155 gl_z(init_gl_z),
156 texture_x(init_texture_x),
157 texture_y(init_texture_y),
158 mask_texture_x(init_mask_texture_x),
159 mask_texture_y(init_mask_texture_y),
160 blend_r(init_blend_r),
161 blend_g(init_blend_g),
162 blend_b(init_blend_b),
163 blend_a(init_blend_a) {
164 }
165
166 float gl_x, gl_y, gl_z;
167 float texture_x, texture_y;
168 float mask_texture_x, mask_texture_y;
169 float blend_r, blend_g, blend_b, blend_a;
170 };
171 static_assert(sizeof(PerVertexData) == 44, "Wrong padding.");
172
173 // The buffer that will contain the quad for rendering.
174 Gl::Buffer<PerVertexData> gl_array_buffer_;
175
176 // The program.
177 Gl::Program gl_program_;
178
179 // Attributes.
180 GLint attr_blend_;
181 GLint attr_mask_texture_position_;
182 GLint attr_position_;
183 GLint attr_texture_position_;
184
185 // Uniforms.
186 GLint u_texture_;
187 GLint u_mask_;
188
189 // Cached for efficiency.
190 std::vector<PerVertexData> vertices_;
191
192 DISALLOW_COPY_AND_ASSIGN(BlitProgram);
193};
194
195BlitProgram::BlitProgram(const std::string& fragment_shader) {
196 gl_program_.build(kBlitVertexShader, fragment_shader.c_str());
197103
198 attr_blend_ = glGetAttribLocation(gl_program_.object(), "attr_blend");104 attr_blend_ = glGetAttribLocation(gl_program_.object(), "attr_blend");
199 attr_mask_texture_position_ = glGetAttribLocation(gl_program_.object(), "attr_mask_texture_position");105 attr_mask_texture_position_ = glGetAttribLocation(gl_program_.object(), "attr_mask_texture_position");
200 attr_position_ = glGetAttribLocation(gl_program_.object(), "attr_position");106 attr_position_ = glGetAttribLocation(gl_program_.object(), "attr_position");
201 attr_texture_position_ = glGetAttribLocation(gl_program_.object(), "attr_texture_position");107 attr_texture_position_ = glGetAttribLocation(gl_program_.object(), "attr_texture_position");
108 attr_program_flavor_ = glGetAttribLocation(gl_program_.object(), "attr_program_flavor");
202109
203 u_texture_ = glGetUniformLocation(gl_program_.object(), "u_texture");110 u_texture_ = glGetUniformLocation(gl_program_.object(), "u_texture");
204 u_mask_ = glGetUniformLocation(gl_program_.object(), "u_mask");111 u_mask_ = glGetUniformLocation(gl_program_.object(), "u_mask");
205}112}
206113
207void BlitProgram::activate() {114BlitProgram::~BlitProgram() {}
115
116void BlitProgram::draw(const std::vector<Arguments>& arguments) {
208 glUseProgram(gl_program_.object());117 glUseProgram(gl_program_.object());
209118
210 glEnableVertexAttribArray(attr_blend_);119 auto& gl_state = Gl::State::instance();
211 glEnableVertexAttribArray(attr_mask_texture_position_);
212 glEnableVertexAttribArray(attr_position_);
213 glEnableVertexAttribArray(attr_texture_position_);
214}
215120
216void BlitProgram::draw_and_deactivate(const std::vector<Arguments>& arguments) {121 gl_state.enable_vertex_attrib_array({attr_blend_,
217 size_t i = 0;122 attr_mask_texture_position_,
123 attr_position_,
124 attr_texture_position_,
125 attr_program_flavor_});
218126
219 gl_array_buffer_.bind();127 gl_array_buffer_.bind();
220128
@@ -226,6 +134,8 @@
226 Gl::vertex_attrib_pointer(attr_position_, 3, sizeof(PerVertexData), offsetof(PerVertexData, gl_x));134 Gl::vertex_attrib_pointer(attr_position_, 3, sizeof(PerVertexData), offsetof(PerVertexData, gl_x));
227 Gl::vertex_attrib_pointer(135 Gl::vertex_attrib_pointer(
228 attr_texture_position_, 2, sizeof(PerVertexData), offsetof(PerVertexData, texture_x));136 attr_texture_position_, 2, sizeof(PerVertexData), offsetof(PerVertexData, texture_x));
137 Gl::vertex_attrib_pointer(
138 attr_program_flavor_, 1, sizeof(PerVertexData), offsetof(PerVertexData, program_flavor));
229139
230 glUniform1i(u_texture_, 0);140 glUniform1i(u_texture_, 0);
231 glUniform1i(u_mask_, 1);141 glUniform1i(u_mask_, 1);
@@ -234,15 +144,18 @@
234 std::vector<DrawBatch> draw_batches;144 std::vector<DrawBatch> draw_batches;
235 int offset = 0;145 int offset = 0;
236 vertices_.clear();146 vertices_.clear();
147
148 size_t i = 0;
237 while (i < arguments.size()) {149 while (i < arguments.size()) {
238 const Arguments& template_args = arguments[i];150 const auto& template_args = arguments[i];
239151
240 // Batch common blit operations up.152 // Batch common blit operations up.
241 while (i < arguments.size()) {153 while (i < arguments.size()) {
242 const Arguments& current_args = arguments[i];154 const auto& current_args = arguments[i];
243 if (current_args.blend_mode != template_args.blend_mode ||155 if (current_args.blend_mode != template_args.blend_mode ||
244 current_args.texture.texture_id != template_args.texture.texture_id ||156 current_args.texture.texture_id != template_args.texture.texture_id ||
245 current_args.mask.texture_id != template_args.mask.texture_id) {157 (current_args.mask.texture_id != 0 &&
158 current_args.mask.texture_id != template_args.mask.texture_id)) {
246 break;159 break;
247 }160 }
248161
@@ -253,56 +166,71 @@
253166
254 const FloatRect texture_rect = to_gl_texture(current_args.texture);167 const FloatRect texture_rect = to_gl_texture(current_args.texture);
255 const FloatRect mask_rect = to_gl_texture(current_args.mask);168 const FloatRect mask_rect = to_gl_texture(current_args.mask);
256 vertices_.emplace_back(current_args.destination_rect.x,169 float program_flavor = 0;
257 current_args.destination_rect.y,170 switch (current_args.blit_mode) {
258 current_args.z_value,171 case BlitMode::kDirect:
259 texture_rect.x,172 program_flavor = 0.;
260 texture_rect.y,173 break;
261 mask_rect.x,174
262 mask_rect.y,175 case BlitMode::kMonochrome:
263 blend_r,176 program_flavor = 1.;
264 blend_g,177 break;
265 blend_b,178
266 blend_a);179 case BlitMode::kBlendedWithMask:
267180 program_flavor = 2.;
268 vertices_.emplace_back(current_args.destination_rect.x + current_args.destination_rect.w,181 break;
269 current_args.destination_rect.y,182 }
270 current_args.z_value,183
271 texture_rect.x + texture_rect.w,184 vertices_.emplace_back(current_args.destination_rect.x,
272 texture_rect.y,185 current_args.destination_rect.y,
273 mask_rect.x + mask_rect.w,186 current_args.z_value,
274 mask_rect.y,187 texture_rect.x,
275 blend_r,188 texture_rect.y,
276 blend_g,189 mask_rect.x,
277 blend_b,190 mask_rect.y,
278 blend_a);191 blend_r,
279192 blend_g,
280 vertices_.emplace_back(current_args.destination_rect.x,193 blend_b,
281 current_args.destination_rect.y + current_args.destination_rect.h,194 blend_a, program_flavor);
282 current_args.z_value,195
283 texture_rect.x,196 vertices_.emplace_back(current_args.destination_rect.x + current_args.destination_rect.w,
284 texture_rect.y + texture_rect.h,197 current_args.destination_rect.y,
285 mask_rect.x,198 current_args.z_value,
286 mask_rect.y + mask_rect.h,199 texture_rect.x + texture_rect.w,
287 blend_r,200 texture_rect.y,
288 blend_g,201 mask_rect.x + mask_rect.w,
289 blend_b,202 mask_rect.y,
290 blend_a);203 blend_r,
291204 blend_g,
292 vertices_.emplace_back(vertices_.at(vertices_.size() - 2));205 blend_b,
293 vertices_.emplace_back(vertices_.at(vertices_.size() - 2));206 blend_a, program_flavor);
294207
295 vertices_.emplace_back(current_args.destination_rect.x + current_args.destination_rect.w,208 vertices_.emplace_back(current_args.destination_rect.x,
296 current_args.destination_rect.y + current_args.destination_rect.h,209 current_args.destination_rect.y + current_args.destination_rect.h,
297 current_args.z_value,210 current_args.z_value,
298 texture_rect.x + texture_rect.w,211 texture_rect.x,
299 texture_rect.y + texture_rect.h,212 texture_rect.y + texture_rect.h,
300 mask_rect.x + mask_rect.w,213 mask_rect.x,
301 mask_rect.y + mask_rect.h,214 mask_rect.y + mask_rect.h,
302 blend_r,215 blend_r,
303 blend_g,216 blend_g,
304 blend_b,217 blend_b,
305 blend_a);218 blend_a, program_flavor);
219
220 vertices_.emplace_back(vertices_.at(vertices_.size() - 2));
221 vertices_.emplace_back(vertices_.at(vertices_.size() - 2));
222
223 vertices_.emplace_back(current_args.destination_rect.x + current_args.destination_rect.w,
224 current_args.destination_rect.y + current_args.destination_rect.h,
225 current_args.z_value,
226 texture_rect.x + texture_rect.w,
227 texture_rect.y + texture_rect.h,
228 mask_rect.x + mask_rect.w,
229 mask_rect.y + mask_rect.h,
230 blend_r,
231 blend_g,
232 blend_b,
233 blend_a, program_flavor);
306 ++i;234 ++i;
307 }235 }
308236
@@ -317,11 +245,8 @@
317245
318 // Now do the draw calls.246 // Now do the draw calls.
319 for (const auto& draw_arg : draw_batches) {247 for (const auto& draw_arg : draw_batches) {
320 glActiveTexture(GL_TEXTURE0);248 gl_state.bind(GL_TEXTURE0, draw_arg.texture);
321 glBindTexture(GL_TEXTURE_2D, draw_arg.texture);249 gl_state.bind(GL_TEXTURE1, draw_arg.mask);
322
323 glActiveTexture(GL_TEXTURE1);
324 glBindTexture(GL_TEXTURE_2D, draw_arg.mask);
325250
326 if (draw_arg.blend_mode == BlendMode::Copy) {251 if (draw_arg.blend_mode == BlendMode::Copy) {
327 glBlendFunc(GL_ONE, GL_ZERO);252 glBlendFunc(GL_ONE, GL_ZERO);
@@ -332,130 +257,38 @@
332 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);257 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
333 }258 }
334 }259 }
335260}
336 glDisableVertexAttribArray(attr_blend_);261
337 glDisableVertexAttribArray(attr_mask_texture_position_);262void BlitProgram::draw(const FloatRect& gl_dest_rect,
338 glDisableVertexAttribArray(attr_position_);
339 glDisableVertexAttribArray(attr_texture_position_);
340
341 glBindTexture(GL_TEXTURE_2D, 0);
342
343 glActiveTexture(GL_TEXTURE0);
344 glBindTexture(GL_TEXTURE_2D, 0);
345
346 glBindBuffer(GL_ARRAY_BUFFER, 0);
347}
348
349// static
350VanillaBlitProgram& VanillaBlitProgram::instance() {
351 static VanillaBlitProgram blit_program;
352 return blit_program;
353}
354
355VanillaBlitProgram::~VanillaBlitProgram() {
356}
357
358VanillaBlitProgram::VanillaBlitProgram() {
359 blit_program_.reset(new BlitProgram(kVanillaBlitFragmentShader));
360}
361
362void VanillaBlitProgram::draw(const FloatRect& gl_dest_rect,
363 const float z_value,263 const float z_value,
364 const BlitData& texture,264 const BlitData& texture,
365 const float opacity,265 const BlitData& mask,
366 const BlendMode blend_mode) {266 const RGBAColor& blend,
367 draw({Arguments{gl_dest_rect, z_value, texture, opacity, blend_mode}});267 const BlendMode& blend_mode) {
368}268 draw({Arguments{gl_dest_rect,
369269 z_value,
370void VanillaBlitProgram::draw(const std::vector<Arguments>& arguments) {270 texture,
371 std::vector<BlitProgram::Arguments> blit_arguments;271 mask,
372 for (const Arguments arg : arguments) {272 blend,
373 blit_arguments.emplace_back(BlitProgram::Arguments{273 blend_mode,
374 arg.destination_rect,274 mask.texture_id != 0 ? BlitMode::kBlendedWithMask : BlitMode::kDirect}});
375 arg.z_value,275}
376 arg.texture,276
377 BlitData{0, 0, 0, Rect()},277void BlitProgram::draw_monochrome(const FloatRect& dest_rect,
378 RGBAColor(255, 255, 255, arg.opacity * 255),
379 arg.blend_mode,
380 });
381 }
382
383 blit_program_->activate();
384 blit_program_->draw_and_deactivate(blit_arguments);
385}
386
387
388// static
389MonochromeBlitProgram& MonochromeBlitProgram::instance() {
390 static MonochromeBlitProgram blit_program;
391 return blit_program;
392}
393
394MonochromeBlitProgram::~MonochromeBlitProgram() {
395}
396
397MonochromeBlitProgram::MonochromeBlitProgram() {
398 blit_program_.reset(new BlitProgram(kMonochromeBlitFragmentShader));
399}
400
401void MonochromeBlitProgram::draw(const FloatRect& dest_rect,
402 const float z_value,278 const float z_value,
403 const BlitData& texture,279 const BlitData& texture,
404 const RGBAColor& blend) {280 const RGBAColor& blend) {
405 draw({Arguments{dest_rect, z_value, texture, blend, BlendMode::UseAlpha}});281 draw({Arguments{dest_rect,
406}282 z_value,
407283 texture,
408void MonochromeBlitProgram::draw(const std::vector<Arguments>& arguments) {284 BlitData{0, 0, 0, Rect()},
409 std::vector<BlitProgram::Arguments> blit_arguments;285 blend,
410 for (const Arguments arg : arguments) {286 BlendMode::UseAlpha,
411 blit_arguments.emplace_back(BlitProgram::Arguments{287 BlitMode::kMonochrome}});
412 arg.destination_rect,
413 arg.z_value,
414 arg.texture,
415 BlitData{0, 0, 0, Rect()},
416 arg.blend,
417 arg.blend_mode,
418 });
419 }
420
421 blit_program_->activate();
422 blit_program_->draw_and_deactivate(blit_arguments);
423}288}
424289
425// static290// static
426BlendedBlitProgram& BlendedBlitProgram::instance() {291BlitProgram& BlitProgram::instance() {
427 static BlendedBlitProgram blit_program;292 static BlitProgram blit_program;
428 return blit_program;293 return blit_program;
429}294}
430
431BlendedBlitProgram::~BlendedBlitProgram() {
432}
433
434BlendedBlitProgram::BlendedBlitProgram() {
435 blit_program_.reset(new BlitProgram(kBlendedBlitFragmentShader));
436}
437
438void BlendedBlitProgram::draw(const FloatRect& gl_dest_rect,
439 const float z_value,
440 const BlitData& texture,
441 const BlitData& mask,
442 const RGBAColor& blend) {
443 draw({Arguments{gl_dest_rect, z_value, texture, mask, blend, BlendMode::UseAlpha}});
444}
445
446void BlendedBlitProgram::draw(const std::vector<Arguments>& arguments) {
447 std::vector<BlitProgram::Arguments> blit_arguments;
448 for (const Arguments arg : arguments) {
449 blit_arguments.emplace_back(BlitProgram::Arguments{
450 arg.destination_rect,
451 arg.z_value,
452 arg.texture,
453 arg.mask,
454 arg.blend,
455 arg.blend_mode,
456 });
457 }
458
459 blit_program_->activate();
460 blit_program_->draw_and_deactivate(blit_arguments);
461}
462295
=== 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-16 20:47:43 +0000
@@ -26,84 +26,14 @@
26#include "base/macros.h"26#include "base/macros.h"
27#include "base/rect.h"27#include "base/rect.h"
28#include "graphic/blend_mode.h"28#include "graphic/blend_mode.h"
29#include "graphic/blit_mode.h"
29#include "graphic/color.h"30#include "graphic/color.h"
30#include "graphic/gl/blit_data.h"31#include "graphic/gl/blit_data.h"
31#include "graphic/gl/system_headers.h"32#include "graphic/gl/system_headers.h"
3233#include "graphic/gl/utils.h"
33class BlitProgram;34
3435// Blits images. Can blend them with player color or make them monochrome.
3536class BlitProgram {
36class VanillaBlitProgram {
37public:
38 struct Arguments {
39 FloatRect destination_rect;
40 float z_value;
41 BlitData texture;
42 float opacity;
43 BlendMode blend_mode;
44 };
45
46 // Returns the (singleton) instance of this class.
47 static VanillaBlitProgram& instance();
48 ~VanillaBlitProgram();
49
50 // Draws the rectangle 'gl_src_rect' from the texture with the name
51 // 'texture' to 'gl_dest_rect' in the currently bound framebuffer. All alpha
52 // values are multiplied by 'opacity' during the blit.
53 // All coordinates are in the OpenGL frame. The 'blend_mode' defines if the
54 // values are copied or if alpha values are used.
55 void draw(const FloatRect& gl_dest_rect,
56 const float z_value,
57 const BlitData& texture,
58 float opacity,
59 const BlendMode blend_mode);
60
61 // Draws a bunch of items at once.
62 void draw(const std::vector<Arguments>& arguments);
63
64private:
65 VanillaBlitProgram();
66
67 std::unique_ptr<BlitProgram> blit_program_;
68
69 DISALLOW_COPY_AND_ASSIGN(VanillaBlitProgram);
70};
71
72class MonochromeBlitProgram {
73public:
74 struct Arguments {
75 FloatRect destination_rect;
76 float z_value;
77 BlitData texture;
78 RGBAColor blend;
79 BlendMode blend_mode;
80 };
81
82 // Returns the (singleton) instance of this class.
83 static MonochromeBlitProgram& instance();
84 ~MonochromeBlitProgram();
85
86 // Draws the rectangle 'gl_src_rect' from the texture with the name
87 // 'texture' to 'gl_dest_rect' in the currently bound framebuffer. All
88 // coordinates are in the OpenGL frame. The image is first converted to
89 // luminance, then all values are multiplied with blend.
90 void draw(const FloatRect& gl_dest_rect,
91 const float z_value,
92 const BlitData& blit_source,
93 const RGBAColor& blend);
94
95 // Draws a bunch of items at once.
96 void draw(const std::vector<Arguments>& arguments);
97
98private:
99 MonochromeBlitProgram();
100
101 std::unique_ptr<BlitProgram> blit_program_;
102
103 DISALLOW_COPY_AND_ASSIGN(MonochromeBlitProgram);
104};
105
106class BlendedBlitProgram {
107public:37public:
108 struct Arguments {38 struct Arguments {
109 FloatRect destination_rect;39 FloatRect destination_rect;
@@ -112,11 +42,12 @@
112 BlitData mask;42 BlitData mask;
113 RGBAColor blend;43 RGBAColor blend;
114 BlendMode blend_mode;44 BlendMode blend_mode;
45 BlitMode blit_mode;
115 };46 };
11647
117 // Returns the (singleton) instance of this class.48 // Returns the (singleton) instance of this class.
118 static BlendedBlitProgram& instance();49 static BlitProgram& instance();
119 ~BlendedBlitProgram();50 ~BlitProgram();
12051
121 // Draws the rectangle 'gl_src_rect' from the texture with the name52 // Draws the rectangle 'gl_src_rect' from the texture with the name
122 // 'gl_texture_image' to 'gl_dest_rect' in the currently bound framebuffer. All53 // 'gl_texture_image' to 'gl_dest_rect' in the currently bound framebuffer. All
@@ -126,17 +57,81 @@
126 const float z_value,57 const float z_value,
127 const BlitData& texture,58 const BlitData& texture,
128 const BlitData& mask,59 const BlitData& mask,
129 const RGBAColor& blend);60 const RGBAColor& blend,
61 const BlendMode& blend_mode);
62
63 // Draws the rectangle 'gl_src_rect' from the texture with the name
64 // 'texture' to 'gl_dest_rect' in the currently bound framebuffer. All
65 // coordinates are in the OpenGL frame. The image is first converted to
66 // luminance, then all values are multiplied with blend.
67 void draw_monochrome(const FloatRect& gl_dest_rect,
68 const float z_value,
69 const BlitData& blit_source,
70 const RGBAColor& blend);
71
13072
131 // Draws a bunch of items at once.73 // Draws a bunch of items at once.
132 void draw(const std::vector<Arguments>& arguments);74 void draw(const std::vector<Arguments>& arguments);
13375
134private:76private:
135 BlendedBlitProgram();77 BlitProgram();
13678
137 std::unique_ptr<BlitProgram> blit_program_;79 struct PerVertexData {
13880 PerVertexData(float init_gl_x,
139 DISALLOW_COPY_AND_ASSIGN(BlendedBlitProgram);81 float init_gl_y,
82 float init_gl_z,
83 float init_texture_x,
84 float init_texture_y,
85 float init_mask_texture_x,
86 float init_mask_texture_y,
87 float init_blend_r,
88 float init_blend_g,
89 float init_blend_b,
90 float init_blend_a,
91 float init_program_flavor)
92 : gl_x(init_gl_x),
93 gl_y(init_gl_y),
94 gl_z(init_gl_z),
95 texture_x(init_texture_x),
96 texture_y(init_texture_y),
97 mask_texture_x(init_mask_texture_x),
98 mask_texture_y(init_mask_texture_y),
99 blend_r(init_blend_r),
100 blend_g(init_blend_g),
101 blend_b(init_blend_b),
102 blend_a(init_blend_a),
103 program_flavor(init_program_flavor) {
104 }
105
106 float gl_x, gl_y, gl_z;
107 float texture_x, texture_y;
108 float mask_texture_x, mask_texture_y;
109 float blend_r, blend_g, blend_b, blend_a;
110 float program_flavor;
111 };
112 static_assert(sizeof(PerVertexData) == 48, "Wrong padding.");
113
114 // The buffer that will contain the quad for rendering.
115 Gl::Buffer<PerVertexData> gl_array_buffer_;
116
117 // The program.
118 Gl::Program gl_program_;
119
120 // Attributes.
121 GLint attr_blend_;
122 GLint attr_mask_texture_position_;
123 GLint attr_position_;
124 GLint attr_texture_position_;
125 GLint attr_program_flavor_;
126
127 // Uniforms.
128 GLint u_texture_;
129 GLint u_mask_;
130
131 // Cached for efficiency.
132 std::vector<PerVertexData> vertices_;
133
134 DISALLOW_COPY_AND_ASSIGN(BlitProgram);
140};135};
141136
142#endif // end of include guard: WL_GRAPHIC_GL_BLIT_PROGRAM_H137#endif // end of include guard: WL_GRAPHIC_GL_BLIT_PROGRAM_H
143138
=== 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-16 20:47:43 +0000
@@ -22,6 +22,7 @@
22#include "base/wexception.h"22#include "base/wexception.h"
23#include "graphic/gl/coordinate_conversion.h"23#include "graphic/gl/coordinate_conversion.h"
24#include "graphic/gl/fields_to_draw.h"24#include "graphic/gl/fields_to_draw.h"
25#include "graphic/gl/utils.h"
25#include "graphic/image_io.h"26#include "graphic/image_io.h"
26#include "graphic/texture.h"27#include "graphic/texture.h"
27#include "io/filesystem/layered_filesystem.h"28#include "io/filesystem/layered_filesystem.h"
@@ -100,12 +101,11 @@
100101
101 dither_mask_.reset(new Texture(load_image_as_sdl_surface("world/pics/edge.png", g_fs), true));102 dither_mask_.reset(new Texture(load_image_as_sdl_surface("world/pics/edge.png", g_fs), true));
102103
103 glBindTexture(GL_TEXTURE_2D, dither_mask_->blit_data().texture_id);104 Gl::State::instance().bind(GL_TEXTURE0, dither_mask_->blit_data().texture_id);
104 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, static_cast<GLint>(GL_CLAMP_TO_EDGE));105 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, static_cast<GLint>(GL_CLAMP_TO_EDGE));
105 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, static_cast<GLint>(GL_CLAMP_TO_EDGE));106 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, static_cast<GLint>(GL_CLAMP_TO_EDGE));
106 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, static_cast<GLint>(GL_LINEAR));107 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, static_cast<GLint>(GL_LINEAR));
107 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, static_cast<GLint>(GL_NEAREST));108 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, static_cast<GLint>(GL_NEAREST));
108 glBindTexture(GL_TEXTURE_2D, 0);
109}109}
110110
111DitherProgram::~DitherProgram() {}111DitherProgram::~DitherProgram() {}
@@ -169,11 +169,12 @@
169void DitherProgram::gl_draw(int gl_texture, float texture_w, float texture_h, const float z_value) {169void DitherProgram::gl_draw(int gl_texture, float texture_w, float texture_h, const float z_value) {
170 glUseProgram(gl_program_.object());170 glUseProgram(gl_program_.object());
171171
172 glEnableVertexAttribArray(attr_brightness_);172 auto& gl_state = Gl::State::instance();
173 glEnableVertexAttribArray(attr_dither_texture_position_);173 gl_state.enable_vertex_attrib_array({attr_brightness_,
174 glEnableVertexAttribArray(attr_position_);174 attr_dither_texture_position_,
175 glEnableVertexAttribArray(attr_texture_offset_);175 attr_position_,
176 glEnableVertexAttribArray(attr_texture_position_);176 attr_texture_offset_,
177 attr_texture_position_});
177178
178 gl_array_buffer_.bind();179 gl_array_buffer_.bind();
179 gl_array_buffer_.update(vertices_);180 gl_array_buffer_.update(vertices_);
@@ -190,14 +191,8 @@
190 Gl::vertex_attrib_pointer(191 Gl::vertex_attrib_pointer(
191 attr_texture_position_, 2, sizeof(PerVertexData), offsetof(PerVertexData, texture_x));192 attr_texture_position_, 2, sizeof(PerVertexData), offsetof(PerVertexData, texture_x));
192193
193 glBindBuffer(GL_ARRAY_BUFFER, 0);194 gl_state.bind(GL_TEXTURE0, dither_mask_->blit_data().texture_id);
194195 gl_state.bind(GL_TEXTURE1, gl_texture);
195 // Set the sampler texture unit to 0
196 glActiveTexture(GL_TEXTURE0);
197 glBindTexture(GL_TEXTURE_2D, dither_mask_->blit_data().texture_id);
198
199 glActiveTexture(GL_TEXTURE1);
200 glBindTexture(GL_TEXTURE_2D, gl_texture);
201196
202 glUniform1f(u_z_value_, z_value);197 glUniform1f(u_z_value_, z_value);
203 glUniform1i(u_dither_texture_, 0);198 glUniform1i(u_dither_texture_, 0);
@@ -205,16 +200,6 @@
205 glUniform2f(u_texture_dimensions_, texture_w, texture_h);200 glUniform2f(u_texture_dimensions_, texture_w, texture_h);
206201
207 glDrawArrays(GL_TRIANGLES, 0, vertices_.size());202 glDrawArrays(GL_TRIANGLES, 0, vertices_.size());
208
209 glBindTexture(GL_TEXTURE_2D, 0);
210 glActiveTexture(GL_TEXTURE0);
211 glBindTexture(GL_TEXTURE_2D, 0);
212
213 glDisableVertexAttribArray(attr_brightness_);
214 glDisableVertexAttribArray(attr_dither_texture_position_);
215 glDisableVertexAttribArray(attr_position_);
216 glDisableVertexAttribArray(attr_texture_offset_);
217 glDisableVertexAttribArray(attr_texture_position_);
218}203}
219204
220void DitherProgram::draw(const uint32_t gametime,205void DitherProgram::draw(const uint32_t gametime,
221206
=== 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-16 20:47:43 +0000
@@ -89,8 +89,11 @@
89 size_t i = 0;89 size_t i = 0;
9090
91 glUseProgram(gl_program_.object());91 glUseProgram(gl_program_.object());
92 glEnableVertexAttribArray(attr_position_);92
93 glEnableVertexAttribArray(attr_color_);93 auto& gl_state = Gl::State::instance();
94 gl_state.enable_vertex_attrib_array({
95 attr_position_, attr_color_,
96 });
9497
95 gl_array_buffer_.bind();98 gl_array_buffer_.bind();
9699
@@ -140,11 +143,4 @@
140 glLineWidth(draw_arg.line_width);143 glLineWidth(draw_arg.line_width);
141 glDrawArrays(GL_LINES, draw_arg.offset, draw_arg.count);144 glDrawArrays(GL_LINES, draw_arg.offset, draw_arg.count);
142 }145 }
143
144 glBindBuffer(GL_ARRAY_BUFFER, 0);
145
146 glDisableVertexAttribArray(attr_position_);
147 glDisableVertexAttribArray(attr_color_);
148
149 glUseProgram(0);
150}146}
151147
=== 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-16 20:47:43 +0000
@@ -112,8 +112,10 @@
112112
113 gl_array_buffer_.bind();113 gl_array_buffer_.bind();
114114
115 glEnableVertexAttribArray(attr_position_);115 auto& gl_state = Gl::State::instance();
116 glEnableVertexAttribArray(attr_color_);116 gl_state.enable_vertex_attrib_array({
117 attr_position_, attr_color_,
118 });
117119
118 // Batch common rectangles up.120 // Batch common rectangles up.
119 while (i < arguments.size()) {121 while (i < arguments.size()) {
@@ -199,8 +201,4 @@
199 break;201 break;
200 }202 }
201 }203 }
202
203 glDisableVertexAttribArray(attr_position_);
204 glDisableVertexAttribArray(attr_color_);
205 glBindBuffer(GL_ARRAY_BUFFER, 0);
206}204}
207205
=== 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-16 20:47:43 +0000
@@ -25,6 +25,7 @@
25#include "base/log.h"25#include "base/log.h"
26#include "graphic/gl/coordinate_conversion.h"26#include "graphic/gl/coordinate_conversion.h"
27#include "graphic/gl/fields_to_draw.h"27#include "graphic/gl/fields_to_draw.h"
28#include "graphic/gl/utils.h"
28#include "graphic/graphic.h"29#include "graphic/graphic.h"
29#include "graphic/image_io.h"30#include "graphic/image_io.h"
30#include "graphic/texture.h"31#include "graphic/texture.h"
@@ -111,7 +112,7 @@
111 const float road_thickness_x = (-delta_y / vector_length) * kRoadThicknessInPixels;112 const float road_thickness_x = (-delta_y / vector_length) * kRoadThicknessInPixels;
112 const float road_thickness_y = (delta_x / vector_length) * kRoadThicknessInPixels;113 const float road_thickness_y = (delta_x / vector_length) * kRoadThicknessInPixels;
113114
114 const Texture& texture =115 const Image& texture =
115 road_type == Widelands::RoadType::kNormal ?116 road_type == Widelands::RoadType::kNormal ?
116 start.road_textures->get_normal_texture(start.fx, start.fy, direction) :117 start.road_textures->get_normal_texture(start.fx, start.fy, direction) :
117 start.road_textures->get_busy_texture(start.fx, start.fy, direction);118 start.road_textures->get_busy_texture(start.fx, start.fy, direction);
@@ -234,9 +235,10 @@
234235
235 glUseProgram(gl_program_.object());236 glUseProgram(gl_program_.object());
236237
237 glEnableVertexAttribArray(attr_position_);238 auto& gl_state = Gl::State::instance();
238 glEnableVertexAttribArray(attr_texture_position_);239 gl_state.enable_vertex_attrib_array({
239 glEnableVertexAttribArray(attr_brightness_);240 attr_position_, attr_texture_position_, attr_brightness_
241 });
240242
241 gl_array_buffer_.bind();243 gl_array_buffer_.bind();
242 gl_array_buffer_.update(vertices_);244 gl_array_buffer_.update(vertices_);
@@ -248,19 +250,9 @@
248 Gl::vertex_attrib_pointer(250 Gl::vertex_attrib_pointer(
249 attr_brightness_, 1, sizeof(PerVertexData), offsetof(PerVertexData, brightness));251 attr_brightness_, 1, sizeof(PerVertexData), offsetof(PerVertexData, brightness));
250252
251 glBindBuffer(GL_ARRAY_BUFFER, 0);253 gl_state.bind(GL_TEXTURE0, gl_texture);
252
253 // Bind the textures.
254 glActiveTexture(GL_TEXTURE0);
255 glBindTexture(GL_TEXTURE_2D, gl_texture);
256
257 glUniform1i(u_texture_, 0);254 glUniform1i(u_texture_, 0);
258
259 glUniform1f(u_z_value_, z_value);255 glUniform1f(u_z_value_, z_value);
260256
261 glDrawArrays(GL_TRIANGLES, 0, vertices_.size());257 glDrawArrays(GL_TRIANGLES, 0, vertices_.size());
262
263 glDisableVertexAttribArray(attr_position_);
264 glDisableVertexAttribArray(attr_texture_position_);
265 glDisableVertexAttribArray(attr_brightness_);
266}258}
267259
=== 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-16 20:47:43 +0000
@@ -21,6 +21,7 @@
2121
22#include "graphic/gl/coordinate_conversion.h"22#include "graphic/gl/coordinate_conversion.h"
23#include "graphic/gl/fields_to_draw.h"23#include "graphic/gl/fields_to_draw.h"
24#include "graphic/gl/utils.h"
24#include "graphic/texture.h"25#include "graphic/texture.h"
2526
26namespace {27namespace {
@@ -103,10 +104,9 @@
103void TerrainProgram::gl_draw(int gl_texture, float texture_w, float texture_h, float z_value) {104void TerrainProgram::gl_draw(int gl_texture, float texture_w, float texture_h, float z_value) {
104 glUseProgram(gl_program_.object());105 glUseProgram(gl_program_.object());
105106
106 glEnableVertexAttribArray(attr_brightness_);107 auto& gl_state = Gl::State::instance();
107 glEnableVertexAttribArray(attr_position_);108 gl_state.enable_vertex_attrib_array(
108 glEnableVertexAttribArray(attr_texture_offset_);109 {attr_brightness_, attr_position_, attr_texture_offset_, attr_texture_position_});
109 glEnableVertexAttribArray(attr_texture_position_);
110110
111 gl_array_buffer_.bind();111 gl_array_buffer_.bind();
112 gl_array_buffer_.update(vertices_);112 gl_array_buffer_.update(vertices_);
@@ -119,23 +119,13 @@
119 Gl::vertex_attrib_pointer(119 Gl::vertex_attrib_pointer(
120 attr_texture_position_, 2, sizeof(PerVertexData), offsetof(PerVertexData, texture_x));120 attr_texture_position_, 2, sizeof(PerVertexData), offsetof(PerVertexData, texture_x));
121121
122 glBindBuffer(GL_ARRAY_BUFFER, 0);122 gl_state.bind(GL_TEXTURE0, gl_texture);
123
124 glActiveTexture(GL_TEXTURE0);
125 glBindTexture(GL_TEXTURE_2D, gl_texture);
126123
127 glUniform1f(u_z_value_, z_value);124 glUniform1f(u_z_value_, z_value);
128 glUniform1i(u_terrain_texture_, 0);125 glUniform1i(u_terrain_texture_, 0);
129 glUniform2f(u_texture_dimensions_, texture_w, texture_h);126 glUniform2f(u_texture_dimensions_, texture_w, texture_h);
130127
131 glDrawArrays(GL_TRIANGLES, 0, vertices_.size());128 glDrawArrays(GL_TRIANGLES, 0, vertices_.size());
132
133 glBindTexture(GL_TEXTURE_2D, 0);
134
135 glDisableVertexAttribArray(attr_brightness_);
136 glDisableVertexAttribArray(attr_position_);
137 glDisableVertexAttribArray(attr_texture_offset_);
138 glDisableVertexAttribArray(attr_texture_position_);
139}129}
140130
141void TerrainProgram::add_vertex(const FieldsToDraw::Field& field,131void TerrainProgram::add_vertex(const FieldsToDraw::Field& field,
142132
=== 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-16 20:47:43 +0000
@@ -166,6 +166,78 @@
166 }166 }
167}167}
168168
169State::State()
170 : last_active_texture_(0), current_framebuffer_(0), current_framebuffer_texture_(0) {
171}
172
173void State::bind(const GLenum target, const GLuint texture) {
174 if (texture == 0) {
175 return;
176 }
177 do_bind(target, texture);
178}
179
180void State::do_bind(const GLenum target, const GLuint texture) {
181 const auto currently_bound_texture = target_to_texture_[target];
182 if (currently_bound_texture == texture) {
183 return;
184 }
185 if (last_active_texture_ != target) {
186 glActiveTexture(target);
187 last_active_texture_ = target;
188 }
189 glBindTexture(GL_TEXTURE_2D, texture);
190
191 target_to_texture_[target] = texture;
192 texture_to_target_[currently_bound_texture] = 0;
193 texture_to_target_[texture] = target;
194}
195
196void State::unbind_texture_if_bound(const GLuint texture) {
197 if (texture == 0) {
198 return;
199 }
200 const auto target = texture_to_target_[texture];
201 if (target != 0) {
202 do_bind(target, 0);
203 }
204}
205
206void State::bind_framebuffer(const GLuint framebuffer, const GLuint texture) {
207 if (current_framebuffer_ == framebuffer && current_framebuffer_texture_ == texture) {
208 return;
209 }
210
211 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
212 if (framebuffer != 0) {
213 unbind_texture_if_bound(texture);
214 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
215 }
216 current_framebuffer_ = framebuffer;
217 current_framebuffer_texture_ = texture;
218}
219
220void State::enable_vertex_attrib_array(std::unordered_set<GLint> entries) {
221 for (const auto e : entries) {
222 if (!enabled_attrib_arrays_.count(e)) {
223 glEnableVertexAttribArray(e);
224 }
225 }
226 for (const auto e : enabled_attrib_arrays_) {
227 if (!entries.count(e)) {
228 glDisableVertexAttribArray(e);
229 }
230 }
231 enabled_attrib_arrays_ = entries;
232}
233
234// static
235State& State::instance() {
236 static State binder;
237 return binder;
238}
239
240
169void vertex_attrib_pointer(int vertex_index, int num_items, int stride, int offset) {241void vertex_attrib_pointer(int vertex_index, int num_items, int stride, int offset) {
170 glVertexAttribPointer(242 glVertexAttribPointer(
171 vertex_index, num_items, GL_FLOAT, GL_FALSE, stride, reinterpret_cast<void*>(offset));243 vertex_index, num_items, GL_FLOAT, GL_FALSE, stride, reinterpret_cast<void*>(offset));
172244
=== 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-16 20:47:43 +0000
@@ -20,6 +20,8 @@
20#define WL_GRAPHIC_GL_UTILS_H20#define WL_GRAPHIC_GL_UTILS_H
2121
22#include <memory>22#include <memory>
23#include <unordered_map>
24#include <unordered_set>
23#include <vector>25#include <vector>
2426
25#include <stdint.h>27#include <stdint.h>
@@ -63,10 +65,10 @@
63template<typename T>65template<typename T>
64class Buffer {66class Buffer {
65public:67public:
66 Buffer() : buffer_size_(0) {68 Buffer() {
67 glGenBuffers(1, &object_);69 glGenBuffers(1, &object_);
68 if (!object_) {70 if (!object_) {
69 throw wexception("Could not create GL program.");71 throw wexception("Could not create GL buffer.");
70 }72 }
71 }73 }
7274
@@ -82,24 +84,60 @@
82 }84 }
8385
8486
85 // Copies 'elements' into the buffer. If the buffer is too small to hold the87 // Copies 'elements' into the buffer, overwriting what was there before.
86 // data, it is reallocated. Does not check if the buffer is already bound.88 // Does not check if the buffer is already bound.
87 void update(const std::vector<T>& items) {89 void update(const std::vector<T>& items) {
88 if (buffer_size_ < items.size()) {90 // Always re-allocate the buffer. This ends up being much more
89 glBufferData(GL_ARRAY_BUFFER, items.size() * sizeof(T), items.data(), GL_DYNAMIC_DRAW);91 // efficient than trying to do a partial update, because partial
90 buffer_size_ = items.size();92 // updates tend to force the driver to do command buffer flushes.
91 } else {93 glBufferData(GL_ARRAY_BUFFER, items.size() * sizeof(T), items.data(), GL_DYNAMIC_DRAW);
92 glBufferSubData(GL_ARRAY_BUFFER, 0, items.size() * sizeof(T), items.data());
93 }
94 }94 }
9595
96private:96private:
97 GLuint object_;97 GLuint object_;
98 size_t buffer_size_; // In number of elements.
9998
100 DISALLOW_COPY_AND_ASSIGN(Buffer);99 DISALLOW_COPY_AND_ASSIGN(Buffer);
101};100};
102101
102// Some GL drivers do not remember the current pipeline state. If you rebind a
103// texture that has already bound to the same target, they will happily stall
104// the pipeline. We therefore cache the state of the GL driver in this class
105// and skip unneeded GL calls.
106class State {
107public:
108 static State& instance();
109
110 void bind_framebuffer(GLuint framebuffer, GLuint texture);
111
112 // Wrapper around glActiveTexture() and glBindTexture(). We never unbind a
113 // texture, i.e. calls with texture == 0 are ignored. It costs only time and
114 // is only needed when the bounded texture is rendered on - see
115 // 'unbind_texture_if_bound'.
116 void bind(GLenum target, GLuint texture);
117
118 // Checks if the texture is bound to any target. If so, unbinds it. This is
119 // needed before the texture is used as target for rendering.
120 void unbind_texture_if_bound(GLuint texture);
121
122 // Calls glEnableVertexAttribArray on all 'entries' and disables all others
123 // that are activated. 'entries' is taken by value on purpose.
124 void enable_vertex_attrib_array(std::unordered_set<GLint> entries);
125
126private:
127 std::unordered_map<GLenum, GLuint> target_to_texture_;
128 std::unordered_map<GLuint, GLenum> texture_to_target_;
129 std::unordered_set<GLint> enabled_attrib_arrays_;
130 GLenum last_active_texture_;
131 GLuint current_framebuffer_;
132 GLuint current_framebuffer_texture_;
133
134 State();
135
136 void do_bind(GLenum target, GLuint texture);
137
138 DISALLOW_COPY_AND_ASSIGN(State);
139};
140
103// Calls glVertexAttribPointer.141// Calls glVertexAttribPointer.
104void vertex_attrib_pointer(int vertex_index, int num_items, int stride, int offset);142void vertex_attrib_pointer(int vertex_index, int num_items, int stride, int offset);
105143
106144
=== modified file 'src/graphic/graphic.cc'
--- src/graphic/graphic.cc 2015-01-28 07:32:57 +0000
+++ src/graphic/graphic.cc 2016-01-16 20:47:43 +0000
@@ -21,17 +21,23 @@
2121
22#include <memory>22#include <memory>
2323
24#include "base/i18n.h"
24#include "base/log.h"25#include "base/log.h"
25#include "base/wexception.h"26#include "base/wexception.h"
26#include "build_info.h"27#include "build_info.h"
28#include "graphic/align.h"
27#include "graphic/animation.h"29#include "graphic/animation.h"
30#include "graphic/build_texture_atlas.h"
31#include "graphic/font.h"
28#include "graphic/font_handler.h"32#include "graphic/font_handler.h"
33#include "graphic/font_handler1.h"
29#include "graphic/gl/system_headers.h"34#include "graphic/gl/system_headers.h"
30#include "graphic/image.h"35#include "graphic/image.h"
31#include "graphic/image_io.h"36#include "graphic/image_io.h"
32#include "graphic/render_queue.h"37#include "graphic/render_queue.h"
33#include "graphic/rendertarget.h"38#include "graphic/rendertarget.h"
34#include "graphic/screen.h"39#include "graphic/screen.h"
40#include "graphic/text_layout.h"
35#include "graphic/texture.h"41#include "graphic/texture.h"
36#include "io/filesystem/layered_filesystem.h"42#include "io/filesystem/layered_filesystem.h"
37#include "io/streamwrite.h"43#include "io/streamwrite.h"
@@ -57,16 +63,17 @@
5763
58} // namespace64} // namespace
5965
66Graphic::Graphic() : image_cache_(new ImageCache()), animation_manager_(new AnimationManager()) {
67}
68
60/**69/**
61 * Initialize the SDL video mode.70 * Initialize the SDL video mode.
62*/71 */
63Graphic::Graphic(int window_mode_w, int window_mode_h, bool init_fullscreen)72void Graphic::initialize(int window_mode_w, int window_mode_h, bool init_fullscreen) {
64 : m_window_mode_width(window_mode_w),73 m_window_mode_width = window_mode_w;
65 m_window_mode_height(window_mode_h),74 m_window_mode_height = window_mode_h;
66 m_update(true),75 m_requires_update = true;
67 image_cache_(new ImageCache()),76
68 animation_manager_(new AnimationManager())
69{
70 // Request an OpenGL 2 context with double buffering.77 // Request an OpenGL 2 context with double buffering.
71 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);78 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
72 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);79 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
@@ -74,12 +81,10 @@
74 SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);81 SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
7582
76 log("Graphics: Try to set Videomode %ux%u\n", m_window_mode_width, m_window_mode_height);83 log("Graphics: Try to set Videomode %ux%u\n", m_window_mode_width, m_window_mode_height);
77 m_sdl_window = SDL_CreateWindow("Widelands Window",84 m_sdl_window =
78 SDL_WINDOWPOS_UNDEFINED,85 SDL_CreateWindow("Widelands Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
79 SDL_WINDOWPOS_UNDEFINED,86 m_window_mode_width, m_window_mode_height, SDL_WINDOW_OPENGL);
80 m_window_mode_width,87
81 m_window_mode_height,
82 SDL_WINDOW_OPENGL);
83 resolution_changed();88 resolution_changed();
84 set_fullscreen(init_fullscreen);89 set_fullscreen(init_fullscreen);
8590
@@ -96,22 +101,22 @@
96 glewExperimental = GL_TRUE;101 glewExperimental = GL_TRUE;
97 GLenum err = glewInit();102 GLenum err = glewInit();
98 if (err != GLEW_OK) {103 if (err != GLEW_OK) {
99 log("glewInit returns %i\nYour OpenGL installation must be __very__ broken. %s\n",104 log("glewInit returns %i\nYour OpenGL installation must be __very__ broken. %s\n", err,
100 err, glewGetErrorString(err));105 glewGetErrorString(err));
101 throw wexception("glewInit returns %i: Broken OpenGL installation.", err);106 throw wexception("glewInit returns %i: Broken OpenGL installation.", err);
102 }107 }
103#endif108#endif
104109
105 log("Graphics: OpenGL: Version \"%s\"\n",110 log(
106 reinterpret_cast<const char*>(glGetString(GL_VERSION)));111 "Graphics: OpenGL: Version \"%s\"\n", reinterpret_cast<const char*>(glGetString(GL_VERSION)));
107112
108 GLboolean glBool;113 GLboolean glBool;
109 glGetBooleanv(GL_DOUBLEBUFFER, &glBool);114 glGetBooleanv(GL_DOUBLEBUFFER, &glBool);
110 log("Graphics: OpenGL: Double buffering %s\n", (glBool == GL_TRUE) ? "enabled" : "disabled");115 log("Graphics: OpenGL: Double buffering %s\n", (glBool == GL_TRUE) ? "enabled" : "disabled");
111116
112 GLint glInt;117 GLint max_texture_size;
113 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glInt);118 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
114 log("Graphics: OpenGL: Max texture size: %u\n", glInt);119 log("Graphics: OpenGL: Max texture size: %u\n", max_texture_size);
115120
116 glDrawBuffer(GL_BACK);121 glDrawBuffer(GL_BACK);
117122
@@ -134,12 +139,15 @@
134 " pixel fmt %u\n"139 " pixel fmt %u\n"
135 " size %d %d\n"140 " size %d %d\n"
136 "**** END GRAPHICS REPORT ****\n",141 "**** END GRAPHICS REPORT ****\n",
137 SDL_GetCurrentVideoDriver(),142 SDL_GetCurrentVideoDriver(), disp_mode.format, disp_mode.w, disp_mode.h);
138 disp_mode.format,
139 disp_mode.w,
140 disp_mode.h);
141 assert(SDL_BYTESPERPIXEL(disp_mode.format) == 4);143 assert(SDL_BYTESPERPIXEL(disp_mode.format) == 4);
142 }144 }
145
146
147 std::map<std::string, std::unique_ptr<Texture>> textures_in_atlas;
148 auto texture_atlases = build_texture_atlas(max_texture_size, &textures_in_atlas);
149 image_cache_->fill_with_texture_atlases(
150 std::move(texture_atlases), std::move(textures_in_atlas));
143}151}
144152
145Graphic::~Graphic()153Graphic::~Graphic()
@@ -239,14 +247,14 @@
239247
240248
241void Graphic::update() {249void Graphic::update() {
242 m_update = true;250 m_requires_update = true;
243}251}
244252
245/**253/**
246 * Returns true if parts of the screen have been marked for refreshing.254 * Returns true if parts of the screen have been marked for refreshing.
247*/255*/
248bool Graphic::need_update() const {256bool Graphic::need_update() const {
249 return m_update;257 return m_requires_update;
250}258}
251259
252/**260/**
@@ -270,7 +278,7 @@
270 }278 }
271279
272 SDL_GL_SwapWindow(m_sdl_window);280 SDL_GL_SwapWindow(m_sdl_window);
273 m_update = false;281 m_requires_update = false;
274}282}
275283
276284
277285
=== modified file 'src/graphic/graphic.h'
--- src/graphic/graphic.h 2015-06-10 06:46:40 +0000
+++ src/graphic/graphic.h 2016-01-16 20:47:43 +0000
@@ -50,11 +50,14 @@
50 */50 */
51class Graphic {51class Graphic {
52public:52public:
53 // Creates a new graphic mode with the given resolution if fullscreen is53 // Creates a new Graphic object. Must call initialize before first use.
54 // false, otherwise a window that fills the screen.54 Graphic();
55 Graphic(int window_mode_w, int window_mode_height, bool fullscreen);
56 ~Graphic();55 ~Graphic();
5756
57 // Initializes with the given resolution if fullscreen is false, otherwise a
58 // window that fills the screen.
59 void initialize(int window_mode_w, int window_mode_height, bool fullscreen);
60
58 // Gets and sets the resolution.61 // Gets and sets the resolution.
59 void change_resolution(int w, int h);62 void change_resolution(int w, int h);
60 int get_xres();63 int get_xres();
@@ -96,7 +99,7 @@
96 /// A RenderTarget for screen_. This is initialized during init()99 /// A RenderTarget for screen_. This is initialized during init()
97 std::unique_ptr<RenderTarget> m_rendertarget;100 std::unique_ptr<RenderTarget> m_rendertarget;
98 /// This marks the complete screen for updating.101 /// This marks the complete screen for updating.
99 bool m_update;102 bool m_requires_update;
100103
101 /// Non-volatile cache of independent images.104 /// Non-volatile cache of independent images.
102 std::unique_ptr<ImageCache> image_cache_;105 std::unique_ptr<ImageCache> image_cache_;
103106
=== modified file 'src/graphic/image_cache.cc'
--- src/graphic/image_cache.cc 2016-01-12 21:26:15 +0000
+++ src/graphic/image_cache.cc 2016-01-16 20:47:43 +0000
@@ -25,36 +25,11 @@
25#include <string>25#include <string>
2626
27#include <SDL.h>27#include <SDL.h>
28#include <boost/format.hpp>
2928
30#include "graphic/image.h"29#include "graphic/image.h"
31#include "graphic/image_io.h"30#include "graphic/image_io.h"
32#include "graphic/texture.h"31#include "graphic/texture.h"
3332
34ImageCache::ProxyImage::ProxyImage(std::unique_ptr<const Image> original_image)
35 : image_(std::move(original_image)) {
36}
37
38const Image& ImageCache::ProxyImage::image() {
39 return *image_;
40}
41
42void ImageCache::ProxyImage::set_image(std::unique_ptr<const Image> original_image) {
43 image_ = std::move(original_image);
44}
45
46int ImageCache::ProxyImage::width() const {
47 return image_->width();
48}
49
50int ImageCache::ProxyImage::height() const {
51 return image_->height();
52}
53
54const BlitData& ImageCache::ProxyImage::blit_data() const {
55 return image_->blit_data();
56}
57
58ImageCache::ImageCache() {33ImageCache::ImageCache() {
59}34}
6035
@@ -68,16 +43,23 @@
68const Image* ImageCache::insert(const std::string& hash, std::unique_ptr<const Image> image) {43const Image* ImageCache::insert(const std::string& hash, std::unique_ptr<const Image> image) {
69 assert(!has(hash));44 assert(!has(hash));
70 const Image* return_value = image.get();45 const Image* return_value = image.get();
71 images_.insert(make_pair(hash, std::unique_ptr<ProxyImage>(new ProxyImage(std::move(image)))));46 images_.insert(std::make_pair(hash, std::move(image)));
72 return return_value;47 return return_value;
73}48}
7449
50void ImageCache::fill_with_texture_atlases(
51 std::vector<std::unique_ptr<Texture>> texture_atlases,
52 std::map<std::string, std::unique_ptr<Texture>> textures_in_atlas) {
53 texture_atlases_ = std::move(texture_atlases);
54 for (auto& pair : textures_in_atlas) {
55 images_.insert(std::move(pair));
56 }
57}
58
75const Image* ImageCache::get(const std::string& hash) {59const Image* ImageCache::get(const std::string& hash) {
76 ImageMap::const_iterator it = images_.find(hash);60 auto it = images_.find(hash);
77 if (it == images_.end()) {61 if (it == images_.end()) {
78 images_.insert(62 return images_.insert(std::make_pair(hash, std::move(load_image(hash)))).first->second.get();
79 make_pair(hash, std::unique_ptr<ProxyImage>(new ProxyImage(load_image(hash)))));
80 return get(hash);
81 }63 }
82 return it->second.get();64 return it->second.get();
83}65}
8466
=== modified file 'src/graphic/image_cache.h'
--- src/graphic/image_cache.h 2016-01-07 16:21:36 +0000
+++ src/graphic/image_cache.h 2016-01-16 20:47:43 +0000
@@ -33,51 +33,36 @@
3333
34// For historic reasons, most part of the Widelands code base expect that an34// For historic reasons, most part of the Widelands code base expect that an
35// Image stays valid for the whole duration of the program run. This class is35// Image stays valid for the whole duration of the program run. This class is
36// the one that keeps ownership of all Images to ensure that this is true. Also36// the one that keeps ownership of all Images to ensure that this is true.
37// for historic reasons, this class will try to load in Image from disk when37// Other parts of Widelands will create images when they do not exist in the
38// its hash is not found. Other parts of Widelands will create images when they38// cache yet and then put it into the cache and therefore releasing their
39// do not exist in the cache yet and then put it into the cache and therefore39// ownership.
40// releasing their ownership.
41class ImageCache {40class ImageCache {
42public:41public:
43 ImageCache();42 ImageCache();
44 ~ImageCache();43 ~ImageCache();
4544
46 // Insert the given Image into the cache.45 // Insert the 'image' into the cache and returns a pointer to the inserted
47 // Will return a pointer to the freshly inserted image for convenience.46 // image for convenience.
48 const Image* insert(const std::string& hash, std::unique_ptr<const Image> image);47 const Image* insert(const std::string& hash, std::unique_ptr<const Image> image);
4948
50 // Returns the image associated with the given hash. If no image by this49 // Returns the image associated with the 'hash'. If no image by this hash is
51 // hash is known, it will try to load one from disk with the filename =50 // known, it will try to load one from disk with the filename = hash. If
52 // hash. If this fails, it will throw an error.51 // this fails, it will throw an error.
53 const Image* get(const std::string& hash);52 const Image* get(const std::string& hash);
5453
55 // Returns true if the given hash is stored in the cache.54 // Returns true if the 'hash' is stored in the cache.
56 bool has(const std::string& hash) const;55 bool has(const std::string& hash) const;
5756
57 // Fills the image cache with the hash -> Texture map 'textures_in_atlas'
58 // and take ownership of 'texture_atlases' so that the textures stay valid.
59 void
60 fill_with_texture_atlases(std::vector<std::unique_ptr<Texture>> texture_atlases,
61 std::map<std::string, std::unique_ptr<Texture>> textures_in_atlas);
62
58private:63private:
59 // We return a wrapped Image so that we can swap out the pointer to the
60 // image under our user. This can happen when we move an Image from a stand
61 // alone texture into being a subrect of a texture atlas.
62 class ProxyImage : public Image {
63 public:
64 ProxyImage(std::unique_ptr<const Image> image);
65
66 const Image& image();
67 void set_image(std::unique_ptr<const Image> image);
68
69 int width() const override;
70 int height() const override;
71 const BlitData& blit_data() const override;
72
73 private:
74 std::unique_ptr<const Image> image_;
75 };
76
77 using ImageMap = std::map<std::string, std::unique_ptr<ProxyImage>>;
78
79 std::vector<std::unique_ptr<Texture>> texture_atlases_;64 std::vector<std::unique_ptr<Texture>> texture_atlases_;
80 ImageMap images_; /// hash of cached filename/image pairs65 std::map<std::string, std::unique_ptr<const Image>> images_;
8166
82 DISALLOW_COPY_AND_ASSIGN(ImageCache);67 DISALLOW_COPY_AND_ASSIGN(ImageCache);
83};68};
8469
=== modified file 'src/graphic/render_queue.cc'
--- src/graphic/render_queue.cc 2016-01-10 11:56:27 +0000
+++ src/graphic/render_queue.cc 2016-01-16 20:47:43 +0000
@@ -22,7 +22,6 @@
22#include <algorithm>22#include <algorithm>
23#include <limits>23#include <limits>
2424
25#include "base/log.h"
26#include "base/rect.h"25#include "base/rect.h"
27#include "base/wexception.h"26#include "base/wexception.h"
28#include "graphic/gl/blit_program.h"27#include "graphic/gl/blit_program.h"
@@ -76,25 +75,15 @@
76 return (z_value << 40) | (program_id << 36) | extra_value;75 return (z_value << 40) | (program_id << 36) | extra_value;
77}76}
7877
79// Construct 'args' used by the individual programs out of 'item'.
80inline void from_item(const RenderQueue::Item& item, VanillaBlitProgram::Arguments* args) {
81 args->texture = item.vanilla_blit_arguments.texture;
82 args->opacity = item.vanilla_blit_arguments.opacity;
83}
84
85inline void from_item(const RenderQueue::Item& item, MonochromeBlitProgram::Arguments* args) {
86 args->texture = item.monochrome_blit_arguments.texture;
87 args->blend = item.monochrome_blit_arguments.blend;
88}
89
90inline void from_item(const RenderQueue::Item& item, FillRectProgram::Arguments* args) {78inline void from_item(const RenderQueue::Item& item, FillRectProgram::Arguments* args) {
91 args->color = item.rect_arguments.color;79 args->color = item.rect_arguments.color;
92}80}
9381
94inline void from_item(const RenderQueue::Item& item, BlendedBlitProgram::Arguments* args) {82inline void from_item(const RenderQueue::Item& item, BlitProgram::Arguments* args) {
95 args->texture = item.blended_blit_arguments.texture;83 args->texture = item.blit_arguments.texture;
96 args->blend = item.blended_blit_arguments.blend;84 args->blend = item.blit_arguments.blend;
97 args->mask = item.blended_blit_arguments.mask;85 args->mask = item.blit_arguments.mask;
86 args->blit_mode = item.blit_arguments.mode;
98}87}
9988
100inline void from_item(const RenderQueue::Item& item, DrawLineProgram::Arguments* args) {89inline void from_item(const RenderQueue::Item& item, DrawLineProgram::Arguments* args) {
@@ -167,15 +156,7 @@
167156
168 switch (given_item.program_id) {157 switch (given_item.program_id) {
169 case Program::kBlit:158 case Program::kBlit:
170 extra_value = given_item.vanilla_blit_arguments.texture.texture_id;159 extra_value = given_item.blit_arguments.texture.texture_id;
171 break;
172
173 case Program::kBlitMonochrome:
174 extra_value = given_item.monochrome_blit_arguments.texture.texture_id;
175 break;
176
177 case Program::kBlitBlended:
178 extra_value = given_item.blended_blit_arguments.texture.texture_id;
179 break;160 break;
180161
181 case Program::kLine:162 case Program::kLine:
@@ -212,7 +193,7 @@
212 throw wexception("Too many drawn layers. Ran out of z-values.");193 throw wexception("Too many drawn layers. Ran out of z-values.");
213 }194 }
214195
215 glBindFramebuffer(GL_FRAMEBUFFER, 0);196 Gl::State::instance().bind_framebuffer(0, 0);
216 glViewport(0, 0, screen_width, screen_height);197 glViewport(0, 0, screen_width, screen_height);
217198
218 glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);199 glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
@@ -231,7 +212,6 @@
231 blended_items_.clear();212 blended_items_.clear();
232213
233 glDepthMask(GL_TRUE);214 glDepthMask(GL_TRUE);
234
235 next_z_ = 1;215 next_z_ = 1;
236}216}
237217
@@ -241,18 +221,8 @@
241 const Item& item = items[i];221 const Item& item = items[i];
242 switch (item.program_id) {222 switch (item.program_id) {
243 case Program::kBlit:223 case Program::kBlit:
244 VanillaBlitProgram::instance().draw(224 BlitProgram::instance().draw(
245 batch_up<VanillaBlitProgram::Arguments>(Program::kBlit, items, &i));225 batch_up<BlitProgram::Arguments>(Program::kBlit, items, &i));
246 break;
247
248 case Program::kBlitMonochrome:
249 MonochromeBlitProgram::instance().draw(
250 batch_up<MonochromeBlitProgram::Arguments>(Program::kBlitMonochrome, items, &i));
251 break;
252
253 case Program::kBlitBlended:
254 BlendedBlitProgram::instance().draw(
255 batch_up<BlendedBlitProgram::Arguments>(Program::kBlitBlended, items, &i));
256 break;226 break;
257227
258 case Program::kLine:228 case Program::kLine:
259229
=== 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-16 20:47:43 +0000
@@ -28,6 +28,7 @@
28#include "base/macros.h"28#include "base/macros.h"
29#include "base/rect.h"29#include "base/rect.h"
30#include "graphic/blend_mode.h"30#include "graphic/blend_mode.h"
31#include "graphic/blit_mode.h"
31#include "graphic/color.h"32#include "graphic/color.h"
32#include "graphic/gl/fields_to_draw.h"33#include "graphic/gl/fields_to_draw.h"
33#include "logic/description_maintainer.h"34#include "logic/description_maintainer.h"
@@ -82,24 +83,18 @@
82 kTerrainDither,83 kTerrainDither,
83 kTerrainRoad,84 kTerrainRoad,
84 kBlit,85 kBlit,
85 kBlitMonochrome,
86 kBlitBlended,
87 kRect,86 kRect,
88 kLine,87 kLine,
89 kHighestProgramId,88 kHighestProgramId,
90 };89 };
9190
92 struct VanillaBlitArguments {
93 BlitData texture;
94 float opacity;
95 };
96
97 struct MonochromeBlitArguments {91 struct MonochromeBlitArguments {
98 BlitData texture;92 BlitData texture;
99 RGBAColor blend;93 RGBAColor blend;
100 };94 };
10195
102 struct BlendedBlitArguments {96 struct BlitArguments {
97 BlitMode mode;
103 BlitData texture;98 BlitData texture;
104 BlitData mask;99 BlitData mask;
105 RGBAColor blend;100 RGBAColor blend;
@@ -154,9 +149,7 @@
154 BlendMode blend_mode;149 BlendMode blend_mode;
155150
156 union {151 union {
157 VanillaBlitArguments vanilla_blit_arguments;152 BlitArguments blit_arguments;
158 MonochromeBlitArguments monochrome_blit_arguments;
159 BlendedBlitArguments blended_blit_arguments;
160 TerrainArguments terrain_arguments;153 TerrainArguments terrain_arguments;
161 RectArguments rect_arguments;154 RectArguments rect_arguments;
162 LineArguments line_arguments;155 LineArguments line_arguments;
163156
=== modified file 'src/graphic/screen.cc'
--- src/graphic/screen.cc 2016-01-05 11:28:54 +0000
+++ src/graphic/screen.cc 2016-01-16 20:47:43 +0000
@@ -65,11 +65,13 @@
65 float opacity,65 float opacity,
66 BlendMode blend_mode) {66 BlendMode blend_mode) {
67 RenderQueue::Item i;67 RenderQueue::Item i;
68 i.destination_rect = dst_rect;
68 i.program_id = RenderQueue::Program::kBlit;69 i.program_id = RenderQueue::Program::kBlit;
69 i.blend_mode = blend_mode;70 i.blend_mode = blend_mode;
70 i.destination_rect = dst_rect;71 i.blit_arguments.texture = texture;
71 i.vanilla_blit_arguments.texture = texture;72 i.blit_arguments.mask.texture_id = 0;
72 i.vanilla_blit_arguments.opacity = opacity;73 i.blit_arguments.blend = RGBAColor(0, 0, 0, 255 * opacity);
74 i.blit_arguments.mode = BlitMode::kDirect;
73 RenderQueue::instance().enqueue(i);75 RenderQueue::instance().enqueue(i);
74}76}
7577
@@ -79,11 +81,12 @@
79 const RGBColor& blend) {81 const RGBColor& blend) {
80 RenderQueue::Item i;82 RenderQueue::Item i;
81 i.destination_rect = dst_rect;83 i.destination_rect = dst_rect;
82 i.program_id = RenderQueue::Program::kBlitBlended;84 i.program_id = RenderQueue::Program::kBlit;
83 i.blend_mode = BlendMode::UseAlpha;85 i.blend_mode = BlendMode::UseAlpha;
84 i.blended_blit_arguments.texture = texture;86 i.blit_arguments.texture = texture;
85 i.blended_blit_arguments.mask = mask;87 i.blit_arguments.mask = mask;
86 i.blended_blit_arguments.blend = blend;88 i.blit_arguments.blend = blend;
89 i.blit_arguments.mode = BlitMode::kBlendedWithMask;
87 RenderQueue::instance().enqueue(i);90 RenderQueue::instance().enqueue(i);
88}91}
8992
@@ -91,11 +94,13 @@
91 const BlitData& texture,94 const BlitData& texture,
92 const RGBAColor& blend) {95 const RGBAColor& blend) {
93 RenderQueue::Item i;96 RenderQueue::Item i;
94 i.program_id = RenderQueue::Program::kBlitMonochrome;97 i.destination_rect = dst_rect;
98 i.program_id = RenderQueue::Program::kBlit;
95 i.blend_mode = BlendMode::UseAlpha;99 i.blend_mode = BlendMode::UseAlpha;
96 i.destination_rect = dst_rect;100 i.blit_arguments.texture = texture;
97 i.monochrome_blit_arguments.texture = texture;101 i.blit_arguments.mask.texture_id = 0;
98 i.monochrome_blit_arguments.blend = blend;102 i.blit_arguments.blend = blend;
103 i.blit_arguments.mode = BlitMode::kMonochrome;
99 RenderQueue::instance().enqueue(i);104 RenderQueue::instance().enqueue(i);
100}105}
101106
102107
=== modified file 'src/graphic/surface.h'
--- src/graphic/surface.h 2016-01-04 20:54:08 +0000
+++ src/graphic/surface.h 2016-01-16 20:47:43 +0000
@@ -48,14 +48,14 @@
48 const float opacity,48 const float opacity,
49 BlendMode blend_mode);49 BlendMode blend_mode);
5050
51 /// This draws a playercolor blended image. See BlendedBlitProgram.51 /// This draws a playercolor blended image.
52 void blit_blended(const Rect& dst,52 void blit_blended(const Rect& dst,
53 const Image& image,53 const Image& image,
54 const Image& texture_mask,54 const Image& texture_mask,
55 const Rect& srcrc,55 const Rect& srcrc,
56 const RGBColor& blend);56 const RGBColor& blend);
5757
58 /// This draws a grayed out version. See MonochromeBlitProgram.58 /// This draws a grayed out version.
59 void59 void
60 blit_monochrome(const Rect& dst, const Image&, const Rect& srcrc, const RGBAColor& multiplier);60 blit_monochrome(const Rect& dst, const Image&, const Rect& srcrc, const RGBAColor& multiplier);
6161
6262
=== 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-16 20:47:43 +0000
@@ -98,7 +98,8 @@
98 g_fs = new LayeredFileSystem();98 g_fs = new LayeredFileSystem();
99 g_fs->add_file_system(&FileSystem::create(INSTALL_DATADIR));99 g_fs->add_file_system(&FileSystem::create(INSTALL_DATADIR));
100100
101 g_gr = new Graphic(1, 1, false);101 g_gr = new Graphic();
102 g_gr->initialize(1, 1, false);
102}103}
103104
104} // namespace105} // namespace
105106
=== modified file 'src/graphic/texture.cc'
--- src/graphic/texture.cc 2016-01-12 08:17:04 +0000
+++ src/graphic/texture.cc 2016-01-16 20:47:43 +0000
@@ -167,6 +167,7 @@
167Texture::~Texture()167Texture::~Texture()
168{168{
169 if (m_owns_texture) {169 if (m_owns_texture) {
170 Gl::State::instance().unbind_texture_if_bound(m_blit_data.texture_id);
170 glDeleteTextures(1, &m_blit_data.texture_id);171 glDeleteTextures(1, &m_blit_data.texture_id);
171 }172 }
172}173}
@@ -192,7 +193,7 @@
192193
193 m_owns_texture = true;194 m_owns_texture = true;
194 glGenTextures(1, &m_blit_data.texture_id);195 glGenTextures(1, &m_blit_data.texture_id);
195 glBindTexture(GL_TEXTURE_2D, m_blit_data.texture_id);196 Gl::State::instance().bind(GL_TEXTURE0, m_blit_data.texture_id);
196197
197 // set texture filter to use linear filtering. This looks nicer for resized198 // set texture filter to use linear filtering. This looks nicer for resized
198 // texture. Most textures and images are not resized so the filtering199 // texture. Most textures and images are not resized so the filtering
@@ -215,9 +216,8 @@
215216
216 m_pixels.reset(new uint8_t[width() * height() * 4]);217 m_pixels.reset(new uint8_t[width() * height() * 4]);
217218
218 glBindTexture(GL_TEXTURE_2D, m_blit_data.texture_id);219 Gl::State::instance().bind(GL_TEXTURE0, m_blit_data.texture_id);
219 glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_pixels.get());220 glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_pixels.get());
220 glBindTexture(GL_TEXTURE_2D, 0);
221}221}
222222
223void Texture::unlock(UnlockMode mode) {223void Texture::unlock(UnlockMode mode) {
@@ -227,11 +227,9 @@
227 assert(m_pixels);227 assert(m_pixels);
228228
229 if (mode == Unlock_Update) {229 if (mode == Unlock_Update) {
230 glBindTexture(GL_TEXTURE_2D, m_blit_data.texture_id);230 Gl::State::instance().bind(GL_TEXTURE0, m_blit_data.texture_id);
231 glTexImage2D231 glTexImage2D(GL_TEXTURE_2D, 0, static_cast<GLint>(GL_RGBA), width(), height(), 0, GL_RGBA,
232 (GL_TEXTURE_2D, 0, static_cast<GLint>(GL_RGBA), width(), height(), 0, GL_RGBA,232 GL_UNSIGNED_BYTE, m_pixels.get());
233 GL_UNSIGNED_BYTE, m_pixels.get());
234 glBindTexture(GL_TEXTURE_2D, 0);
235 }233 }
236234
237 m_pixels.reset(nullptr);235 m_pixels.reset(nullptr);
@@ -265,8 +263,8 @@
265263
266264
267void Texture::setup_gl() {265void Texture::setup_gl() {
268 glBindFramebuffer(GL_FRAMEBUFFER, GlFramebuffer::instance().id());266 Gl::State::instance().bind_framebuffer(
269 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_blit_data.texture_id, 0);267 GlFramebuffer::instance().id(), m_blit_data.texture_id);
270 glViewport(0, 0, width(), height());268 glViewport(0, 0, width(), height());
271}269}
272270
@@ -275,7 +273,8 @@
275 float opacity,273 float opacity,
276 BlendMode blend_mode) {274 BlendMode blend_mode) {
277 setup_gl();275 setup_gl();
278 VanillaBlitProgram::instance().draw(dst_rect, 0.f, texture, opacity, blend_mode);276 BlitProgram::instance().draw(dst_rect, 0.f, texture, BlitData{0, 0, 0, Rect()},
277 RGBAColor(0, 0, 0, 255 * opacity), blend_mode);
279}278}
280279
281void Texture::do_blit_blended(const FloatRect& dst_rect,280void Texture::do_blit_blended(const FloatRect& dst_rect,
@@ -284,14 +283,14 @@
284 const RGBColor& blend) {283 const RGBColor& blend) {
285284
286 setup_gl();285 setup_gl();
287 BlendedBlitProgram::instance().draw(dst_rect, 0.f, texture, mask, blend);286 BlitProgram::instance().draw(dst_rect, 0.f, texture, mask, blend, BlendMode::UseAlpha);
288}287}
289288
290void Texture::do_blit_monochrome(const FloatRect& dst_rect,289void Texture::do_blit_monochrome(const FloatRect& dst_rect,
291 const BlitData& texture,290 const BlitData& texture,
292 const RGBAColor& blend) {291 const RGBAColor& blend) {
293 setup_gl();292 setup_gl();
294 MonochromeBlitProgram::instance().draw(dst_rect, 0.f, texture, blend);293 BlitProgram::instance().draw_monochrome(dst_rect, 0.f, texture, blend);
295}294}
296295
297void296void
298297
=== modified file 'src/logic/CMakeLists.txt'
--- src/logic/CMakeLists.txt 2016-01-10 11:36:05 +0000
+++ src/logic/CMakeLists.txt 2016-01-16 20:47:43 +0000
@@ -237,7 +237,6 @@
237 graphic_surface237 graphic_surface
238 graphic_text238 graphic_text
239 graphic_text_layout239 graphic_text_layout
240 graphic_texture_atlas
241 helper240 helper
242 io_fileread241 io_fileread
243 io_filesystem242 io_filesystem
244243
=== 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-16 20:47:43 +0000
@@ -48,7 +48,8 @@
48 g_fs = new LayeredFileSystem();48 g_fs = new LayeredFileSystem();
49 g_fs->add_file_system(&FileSystem::create(INSTALL_DATADIR));49 g_fs->add_file_system(&FileSystem::create(INSTALL_DATADIR));
5050
51 g_gr = new Graphic(1, 1, false);51 g_gr = new Graphic();
52 g_gr->initialize(1, 1, false);
52}53}
5354
54} // namespace55} // namespace
5556
=== 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-16 20:47:43 +0000
@@ -21,18 +21,18 @@
2121
22#include <memory>22#include <memory>
2323
24const Texture& RoadTextures::get_normal_texture(int x, int y, int direction) const {24const Image& RoadTextures::get_normal_texture(int x, int y, int direction) const {
25 return *normal_textures_.at((x + y + direction) % normal_textures_.size());25 return *normal_textures_.at((x + y + direction) % normal_textures_.size());
26}26}
2727
28const Texture& RoadTextures::get_busy_texture(int x, int y, int direction) const {28const Image& RoadTextures::get_busy_texture(int x, int y, int direction) const {
29 return *busy_textures_.at((x + y + direction) % busy_textures_.size());29 return *busy_textures_.at((x + y + direction) % busy_textures_.size());
30}30}
3131
32void RoadTextures::add_normal_road_texture(std::unique_ptr<Texture> texture) {32void RoadTextures::add_normal_road_texture(const Image* image) {
33 normal_textures_.emplace_back(std::move(texture));33 normal_textures_.emplace_back(image);
34}34}
3535
36void RoadTextures::add_busy_road_texture(std::unique_ptr<Texture> texture) {36void RoadTextures::add_busy_road_texture(const Image* image) {
37 busy_textures_.emplace_back(std::move(texture));37 busy_textures_.emplace_back(image);
38}38}
3939
=== 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-16 20:47:43 +0000
@@ -23,23 +23,23 @@
23#include <memory>23#include <memory>
24#include <vector>24#include <vector>
2525
26#include "graphic/texture.h"26#include "graphic/image.h"
2727
28// Simple container to give access of the road textures of a tribe.28// Simple container to give access of the road textures of a tribe.
29class RoadTextures {29class RoadTextures {
30public:30public:
31 // Returns the road texture that should be used for the Cooordinate x, y and31 // Returns the road texture that should be used for the Cooordinate x, y and
32 // the road going into direction 'direction' (which can be any number).32 // the road going into direction 'direction' (which can be any number).
33 const Texture& get_normal_texture(int x, int y, int direction) const;33 const Image& get_normal_texture(int x, int y, int direction) const;
34 const Texture& get_busy_texture(int x, int y, int direction) const;34 const Image& get_busy_texture(int x, int y, int direction) const;
3535
36 // Adds a new road texture.36 // Adds a new road texture.
37 void add_normal_road_texture(std::unique_ptr<Texture> texture);37 void add_normal_road_texture(const Image* texture);
38 void add_busy_road_texture(std::unique_ptr<Texture> texture);38 void add_busy_road_texture(const Image* texture);
3939
40private:40private:
41 std::vector<std::unique_ptr<Texture>> normal_textures_;41 std::vector<const Image*> normal_textures_;
42 std::vector<std::unique_ptr<Texture>> busy_textures_;42 std::vector<const Image*> busy_textures_;
43};43};
4444
45#endif // end of include guard: WL_LOGIC_MAP_OBJECTS_TRIBES_ROAD_TEXTURES_H45#endif // end of include guard: WL_LOGIC_MAP_OBJECTS_TRIBES_ROAD_TEXTURES_H
4646
=== 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-16 20:47:43 +0000
@@ -319,12 +319,12 @@
319 return busy_road_paths_;319 return busy_road_paths_;
320}320}
321321
322void TribeDescr::add_normal_road_texture(std::unique_ptr<Texture> texture) {322void TribeDescr::add_normal_road_texture(const Image* texture) {
323 road_textures_.add_normal_road_texture(std::move(texture));323 road_textures_.add_normal_road_texture(texture);
324}324}
325325
326void TribeDescr::add_busy_road_texture(std::unique_ptr<Texture> texture) {326void TribeDescr::add_busy_road_texture(const Image* texture) {
327 road_textures_.add_busy_road_texture(std::move(texture));327 road_textures_.add_busy_road_texture(texture);
328}328}
329329
330const RoadTextures& TribeDescr::road_textures() const {330const RoadTextures& TribeDescr::road_textures() const {
331331
=== 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-16 20:47:43 +0000
@@ -114,10 +114,9 @@
114 const std::vector<std::string>& normal_road_paths() const;114 const std::vector<std::string>& normal_road_paths() const;
115 const std::vector<std::string>& busy_road_paths() const;115 const std::vector<std::string>& busy_road_paths() const;
116116
117 // Add the corresponding texture (which probably resides in a117 // Add the corresponding texture for roads.
118 // texture atlas) for roads.118 void add_normal_road_texture(const Image* texture);
119 void add_normal_road_texture(std::unique_ptr<Texture> texture);119 void add_busy_road_texture(const Image* texture);
120 void add_busy_road_texture(std::unique_ptr<Texture> texture);
121120
122 // The road textures used for drawing roads.121 // The road textures used for drawing roads.
123 const RoadTextures& road_textures() const;122 const RoadTextures& road_textures() const;
124123
=== modified file 'src/logic/map_objects/tribes/tribes.cc'
--- src/logic/map_objects/tribes/tribes.cc 2016-01-12 21:26:15 +0000
+++ src/logic/map_objects/tribes/tribes.cc 2016-01-16 20:47:43 +0000
@@ -22,9 +22,6 @@
22#include <memory>22#include <memory>
2323
24#include "graphic/graphic.h"24#include "graphic/graphic.h"
25#include "graphic/image_io.h"
26#include "graphic/texture_atlas.h"
27#include "io/filesystem/layered_filesystem.h"
28#include "logic/game_data_error.h"25#include "logic/game_data_error.h"
2926
30namespace Widelands {27namespace Widelands {
@@ -343,38 +340,15 @@
343340
344void Tribes::load_graphics()341void Tribes::load_graphics()
345{342{
346 // Construct and hold on to the texture atlas that contains all road images.
347 TextureAtlas ta;
348
349 // These will be deleted at the end of the method.343 // These will be deleted at the end of the method.
350 std::vector<std::unique_ptr<Texture>> individual_textures_;344 std::vector<std::unique_ptr<Texture>> individual_textures_;
351 for (size_t tribeindex = 0; tribeindex < nrtribes(); ++tribeindex) {345 for (size_t tribeindex = 0; tribeindex < nrtribes(); ++tribeindex) {
352 TribeDescr* tribe = tribes_->get_mutable(tribeindex);346 TribeDescr* tribe = tribes_->get_mutable(tribeindex);
353 for (const std::string& texture_path : tribe->normal_road_paths()) {347 for (const std::string& texture_path : tribe->normal_road_paths()) {
354 individual_textures_.emplace_back(load_image(texture_path, g_fs));348 tribe->add_normal_road_texture(g_gr->images().get(texture_path));
355 ta.add(*individual_textures_.back());
356 }349 }
357 for (const std::string& texture_path : tribe->busy_road_paths()) {350 for (const std::string& texture_path : tribe->busy_road_paths()) {
358 individual_textures_.emplace_back(load_image(texture_path, g_fs));351 tribe->add_busy_road_texture(g_gr->images().get(texture_path));
359 ta.add(*individual_textures_.back());
360 }
361 }
362
363 std::vector<TextureAtlas::PackedTexture> packed_texture;
364 std::vector<std::unique_ptr<Texture>> texture_atlases;
365 ta.pack(1024, &texture_atlases, &packed_texture);
366
367 assert(texture_atlases.size() == 1);
368 road_texture_ = std::move(texture_atlases[0]);
369
370 size_t next_texture_to_move = 0;
371 for (size_t tribeindex = 0; tribeindex < nrtribes(); ++tribeindex) {
372 TribeDescr* tribe = tribes_->get_mutable(tribeindex);
373 for (size_t i = 0; i < tribe->normal_road_paths().size(); ++i) {
374 tribe->add_normal_road_texture(std::move(packed_texture.at(next_texture_to_move++).texture));
375 }
376 for (size_t i = 0; i < tribe->busy_road_paths().size(); ++i) {
377 tribe->add_busy_road_texture(std::move(packed_texture.at(next_texture_to_move++).texture));
378 }352 }
379 }353 }
380}354}
381355
=== 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-16 20:47:43 +0000
@@ -154,8 +154,6 @@
154 std::unique_ptr<DescriptionMaintainer<WorkerDescr>> workers_;154 std::unique_ptr<DescriptionMaintainer<WorkerDescr>> workers_;
155 std::unique_ptr<DescriptionMaintainer<TribeDescr>> tribes_;155 std::unique_ptr<DescriptionMaintainer<TribeDescr>> tribes_;
156156
157 std::unique_ptr<Texture> road_texture_; // Used in loading the road texture graphics
158
159 DISALLOW_COPY_AND_ASSIGN(Tribes);157 DISALLOW_COPY_AND_ASSIGN(Tribes);
160};158};
161159
162160
=== 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-16 20:47:43 +0000
@@ -160,15 +160,15 @@
160TerrainDescription::~TerrainDescription() {160TerrainDescription::~TerrainDescription() {
161}161}
162162
163const Texture& TerrainDescription::get_texture(uint32_t gametime) const {163const Image& TerrainDescription::get_texture(uint32_t gametime) const {
164 return *textures_.at((gametime / frame_length_) % textures_.size());164 return *textures_.at((gametime / frame_length_) % textures_.size());
165}165}
166166
167void TerrainDescription::add_texture(std::unique_ptr<Texture> texture) {167void TerrainDescription::add_texture(const Image* texture) {
168 if (texture->width() != kTextureSideLength || texture->height() != kTextureSideLength) {168 if (texture->width() != kTextureSideLength || texture->height() != kTextureSideLength) {
169 throw wexception("Tried to add a texture with wrong size.");169 throw wexception("Tried to add a texture with wrong size.");
170 }170 }
171 textures_.emplace_back(std::move(texture));171 textures_.emplace_back(texture);
172}172}
173173
174const std::vector<std::string>& TerrainDescription::texture_paths() const {174const std::vector<std::string>& TerrainDescription::texture_paths() const {
175175
=== 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-16 20:47:43 +0000
@@ -73,8 +73,8 @@
73 const std::vector<std::string>& texture_paths() const;73 const std::vector<std::string>& texture_paths() const;
7474
75 /// Returns the texture for the given gametime.75 /// Returns the texture for the given gametime.
76 const Texture& get_texture(uint32_t gametime) const;76 const Image& get_texture(uint32_t gametime) const;
77 void add_texture(std::unique_ptr<Texture> texture);77 void add_texture(const Image* texture);
7878
79 // Sets the base minimap color.79 // Sets the base minimap color.
80 void set_minimap_color(const RGBColor& color);80 void set_minimap_color(const RGBColor& color);
@@ -140,7 +140,7 @@
140 double fertility_;140 double fertility_;
141 double humidity_;141 double humidity_;
142 std::vector<std::string> texture_paths_;142 std::vector<std::string> texture_paths_;
143 std::vector<std::unique_ptr<Texture>> textures_;143 std::vector<const Image*> textures_;
144 RGBColor minimap_colors_[256];144 RGBColor minimap_colors_[256];
145145
146 DISALLOW_COPY_AND_ASSIGN(TerrainDescription);146 DISALLOW_COPY_AND_ASSIGN(TerrainDescription);
147147
=== modified file 'src/logic/map_objects/world/world.cc'
--- src/logic/map_objects/world/world.cc 2016-01-12 21:26:15 +0000
+++ src/logic/map_objects/world/world.cc 2016-01-16 20:47:43 +0000
@@ -23,8 +23,6 @@
2323
24#include "base/i18n.h"24#include "base/i18n.h"
25#include "graphic/image_io.h"25#include "graphic/image_io.h"
26#include "graphic/texture.h"
27#include "graphic/texture_atlas.h"
28#include "logic/game_data_error.h"26#include "logic/game_data_error.h"
29#include "logic/map_objects/bob.h"27#include "logic/map_objects/bob.h"
30#include "logic/map_objects/immovable.h"28#include "logic/map_objects/immovable.h"
@@ -49,11 +47,6 @@
49}47}
5048
51void World::load_graphics() {49void World::load_graphics() {
52 TextureAtlas ta;
53
54 // These will be deleted at the end of the method.
55 std::vector<std::unique_ptr<Texture>> individual_textures_;
56
57 for (size_t i = 0; i < terrains_->size(); ++i) {50 for (size_t i = 0; i < terrains_->size(); ++i) {
58 TerrainDescription* terrain = terrains_->get_mutable(i);51 TerrainDescription* terrain = terrains_->get_mutable(i);
59 for (size_t j = 0; j < terrain->texture_paths().size(); ++j) {52 for (size_t j = 0; j < terrain->texture_paths().size(); ++j) {
@@ -67,23 +60,7 @@
67 terrain->set_minimap_color(60 terrain->set_minimap_color(
68 RGBColor(top_left_pixel_color.r, top_left_pixel_color.g, top_left_pixel_color.b));61 RGBColor(top_left_pixel_color.r, top_left_pixel_color.g, top_left_pixel_color.b));
69 }62 }
70 individual_textures_.emplace_back(new Texture(sdl_surface));63 terrain->add_texture(g_gr->images().get(terrain->texture_paths()[j]));
71 ta.add(*individual_textures_.back());
72 }
73 }
74
75 std::vector<TextureAtlas::PackedTexture> packed_texture;
76 std::vector<std::unique_ptr<Texture>> texture_atlases;
77 ta.pack(1024, &texture_atlases, &packed_texture);
78
79 assert(texture_atlases.size() == 1);
80 terrain_texture_ = std::move(texture_atlases[0]);
81
82 int next_texture_to_move = 0;
83 for (size_t i = 0; i < terrains_->size(); ++i) {
84 TerrainDescription* terrain = terrains_->get_mutable(i);
85 for (size_t j = 0; j < terrain->texture_paths().size(); ++j) {
86 terrain->add_texture(std::move(packed_texture.at(next_texture_to_move++).texture));
87 }64 }
88 }65 }
89}66}
9067
=== 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-16 20:47:43 +0000
@@ -28,7 +28,6 @@
2828
29class LuaInterface;29class LuaInterface;
30class LuaTable;30class LuaTable;
31class Texture;
3231
33namespace Widelands {32namespace Widelands {
3433
@@ -99,7 +98,6 @@
99 std::unique_ptr<DescriptionMaintainer<ResourceDescription>> resources_;98 std::unique_ptr<DescriptionMaintainer<ResourceDescription>> resources_;
100 std::unique_ptr<DescriptionMaintainer<EditorCategory>> editor_terrain_categories_;99 std::unique_ptr<DescriptionMaintainer<EditorCategory>> editor_terrain_categories_;
101 std::unique_ptr<DescriptionMaintainer<EditorCategory>> editor_immovable_categories_;100 std::unique_ptr<DescriptionMaintainer<EditorCategory>> editor_immovable_categories_;
102 std::unique_ptr<Texture> terrain_texture_;
103101
104 DISALLOW_COPY_AND_ASSIGN(World);102 DISALLOW_COPY_AND_ASSIGN(World);
105};103};
106104
=== modified file 'src/wlapplication.cc'
--- src/wlapplication.cc 2015-11-28 22:29:26 +0000
+++ src/wlapplication.cc 2016-01-16 20:47:43 +0000
@@ -271,19 +271,32 @@
271 changedir_on_mac();271 changedir_on_mac();
272 cleanup_replays();272 cleanup_replays();
273273
274 // handling of graphics274 Section & config = g_options.pull_section("global");
275 init_hardware();275
276276 //Start the SDL core
277 // This might grab the input.277 if (SDL_Init(SDL_INIT_VIDEO) == -1)
278 refresh_graphics();278 throw wexception("Failed to initialize SDL, no valid video driver: %s", SDL_GetError());
279
280 SDL_ShowCursor(SDL_DISABLE);
281 g_gr = new Graphic();
279282
280 if (TTF_Init() == -1)283 if (TTF_Init() == -1)
281 throw wexception284 throw wexception
282 ("True Type library did not initialize: %s\n", TTF_GetError());285 ("True Type library did not initialize: %s\n", TTF_GetError());
283286
284 UI::g_fh1 = UI::create_fonthandler(g_gr); // This will create the fontset, so loading it first.287 UI::g_fh1 =
288 UI::create_fonthandler(&g_gr->images()); // This will create the fontset, so loading it first.
285 UI::g_fh = new UI::FontHandler();289 UI::g_fh = new UI::FontHandler();
286290
291 g_gr->initialize(config.get_int("xres", DEFAULT_RESOLUTION_W),
292 config.get_int("yres", DEFAULT_RESOLUTION_H),
293 config.get_bool("fullscreen", false));
294 g_sound_handler.init(); // TODO(unknown): memory leak!
295
296
297 // This might grab the input.
298 refresh_graphics();
299
287 if (SDLNet_Init() == -1)300 if (SDLNet_Init() == -1)
288 throw wexception("SDLNet_Init failed: %s\n", SDLNet_GetError());301 throw wexception("SDLNet_Init failed: %s\n", SDLNet_GetError());
289302
@@ -779,33 +792,6 @@
779 }792 }
780}793}
781794
782/**
783 * Start the hardware: switch to graphics mode, start sound handler
784 *
785 * \pre The locale must be known before calling this
786 *
787 * \return true if there were no fatal errors that prevent the game from running
788 */
789bool WLApplication::init_hardware() {
790 Section & s = g_options.pull_section("global");
791
792 //Start the SDL core
793 if (SDL_Init(SDL_INIT_VIDEO) == -1)
794 throw wexception
795 ("Failed to initialize SDL, no valid video driver: %s",
796 SDL_GetError());
797
798 SDL_ShowCursor(SDL_DISABLE);
799
800 g_gr = new Graphic(s.get_int("xres", DEFAULT_RESOLUTION_W),
801 s.get_int("yres", DEFAULT_RESOLUTION_H),
802 s.get_bool("fullscreen", false));
803
804 g_sound_handler.init(); // TODO(unknown): memory leak!
805
806 return true;
807}
808
809void WLApplication::shutdown_hardware()795void WLApplication::shutdown_hardware()
810{796{
811 delete g_gr;797 delete g_gr;
812798
=== modified file 'src/wlapplication.h'
--- src/wlapplication.h 2015-11-08 17:31:06 +0000
+++ src/wlapplication.h 2016-01-16 20:47:43 +0000
@@ -191,7 +191,6 @@
191 void init_language();191 void init_language();
192 void shutdown_settings();192 void shutdown_settings();
193193
194 bool init_hardware();
195 void shutdown_hardware();194 void shutdown_hardware();
196195
197 void parse_commandline(int argc, char const * const * argv);196 void parse_commandline(int argc, char const * const * argv);

Subscribers

People subscribed via source and target branches

to status/vote changes: