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

Proposed by Nicolai Hähnle
Status: Work in progress
Proposed branch: lp:~widelands-dev/widelands/graphics
Merge into: lp:widelands
Diff against target: 5308 lines (+3726/-434)
50 files modified
cmake/codecheck/rules/correct_include_order (+4/-3)
data/shaders/blit.fp (+3/-4)
data/shaders/blit.vp (+5/-4)
data/shaders/blit_gl4.vp (+70/-0)
data/shaders/minimap_gl4.fp (+108/-0)
data/shaders/minimap_gl4.vp (+11/-0)
data/shaders/road_gl4.vp (+89/-0)
data/shaders/terrain_common_gl4.vp (+59/-0)
data/shaders/terrain_gl4.fp (+58/-0)
data/shaders/terrain_gl4.vp (+157/-0)
src/graphic/CMakeLists.txt (+4/-0)
src/graphic/game_renderer.cc (+107/-92)
src/graphic/game_renderer.h (+35/-16)
src/graphic/game_renderer_gl4.cc (+194/-0)
src/graphic/game_renderer_gl4.h (+92/-0)
src/graphic/gl/blit_program.cc (+297/-6)
src/graphic/gl/blit_program.h (+5/-59)
src/graphic/gl/dither_program.cc (+29/-30)
src/graphic/gl/dither_program.h (+5/-6)
src/graphic/gl/fields_to_draw.h (+298/-80)
src/graphic/gl/initialize.cc (+2/-0)
src/graphic/gl/road_program.cc (+17/-16)
src/graphic/gl/road_program.h (+4/-3)
src/graphic/gl/streaming_buffer.h (+173/-0)
src/graphic/gl/terrain_program.cc (+15/-13)
src/graphic/gl/terrain_program.h (+2/-2)
src/graphic/gl/terrain_program_gl4.cc (+1065/-0)
src/graphic/gl/terrain_program_gl4.h (+352/-0)
src/graphic/gl/utils.cc (+79/-33)
src/graphic/gl/utils.h (+29/-11)
src/graphic/image_io.cc (+1/-1)
src/graphic/minimap_layer.h (+45/-0)
src/graphic/minimap_renderer.cc (+129/-8)
src/graphic/minimap_renderer.h (+49/-25)
src/graphic/render_queue.cc (+36/-5)
src/graphic/render_queue.h (+8/-1)
src/graphic/texture.cc (+5/-3)
src/graphic/texture.h (+20/-1)
src/logic/map.cc (+9/-1)
src/logic/map.h (+13/-0)
src/logic/map_objects/map_object.h (+7/-4)
src/logic/map_objects/tribes/road_textures.h (+7/-0)
src/logic/map_objects/world/terrain_description.cc (+1/-1)
src/logic/map_objects/world/terrain_description.h (+1/-1)
src/logic/player.cc (+11/-0)
src/logic/player.h (+5/-0)
src/wlapplication.cc (+1/-0)
src/wui/mapview.cc (+1/-1)
src/wui/minimap.cc (+8/-3)
src/wui/minimap.h (+1/-1)
To merge this branch: bzr merge lp:~widelands-dev/widelands/graphics
Reviewer Review Type Date Requested Status
Widelands Developers Pending
Review via email: mp+314279@code.launchpad.net

Description of the change

A relatively large change to the rendering system that I've been working on occasionally over the last few weeks. I guess this is the best way to get some feedback.

The goal of the changes is to improve rendering performance by using more advanced OpenGL features. Since those are not available everywhere, the blitting and terrain rendering paths are split into "GL2" (existing) and "GL4" (new) rendering paths. The name "GL4" is not to be interpreted too literally: whether the new path is used is simply keyed off some extensions, mostly ARB_shader_storage_buffer_object, which were added with OpenGL 4.x.

Generally, the idea of the GL4 rendering paths is to do as little work as possible on the CPU, and to figure out more information on the GPU. So:

BlitProgram: Rather than set up all vertices on the CPU like the GL2 path, the GL4 path populates an SSBO with rectangle information; the vertex shader extracts the information for the required rectangle based on the vertex ID.

Terrain rendering: Map data like terrain type, height, and brightness is stored in textures. Rendering happens in instanced patches (small rectangular chunks of the map), so that basically no vertex data has to be uploaded per frame. The vertex shader samples the textures to compute vertex positions, terrain texture coordinates, and brightness. Terrain base rendering and dithering is done in a single pass in the fragment shader. Player-based brightness data has to be re-uploaded each frame, but apart from that, the CPU has to do almost no work.

Minimap rendering: The minimap is almost entirely rendered in a fragment shader on the GPU. This requires an additional texture containing information about field ownership and the presence of flags/roads/buildings. This texture must be updated each frame. This requires less CPU work than computing the full minimap on the CPU, but is still quite expensive, so the texture is updated in a "rolling" fashion: each frame, a strip of width 8 across the whole map is updated.

Whether the new rendering path is used or not is based simply on the existence of the required extensions, although there's an override: use --disable_gl4=true to use the old rendering path even when the new one would be supported.

To post a comment you must log in.
Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

This sound interesting, some questions:
* where in the logs can I find out if this new path is used
* which OS and libraries do you use?
  e.g. I use OSX Sierra, with
  libsdl @1.2.15_3+x11 (active)
  libsdl2 @2.0.5_1 (active)
  libsdl2_image @2.0.1_0 (active)
  libsdl2_mixer @2.0.1_0 (active)
  libsdl2_net @2.0.1_0 (active)
  libsdl2_ttf @2.0.14_0 (active)
  libsdl_image @1.2.12_5 (active)
  libsdl_mixer @1.2.12_0 (active)
  libsdl_net @1.2.8_0 (active)
  libsdl_pango @0.1.2_1 (active)
  libsdl_ttf @2.0.11_0 (active)

I assume ther should be no big noticable difference,
except for a "late" minimap?

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 1808. State: failed. Details: https://travis-ci.org/widelands/widelands/builds/189782905.
Appveyor build 1646. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_nha_widelands_graphics-1646.

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

Some comments inline, now going to compile / run this ...

Revision history for this message
Klaus Halfmann (klaus-halfmann) wrote :

Looks like you must fix this first:

/home/travis/build/widelands/widelands/src/graphic/gl/streaming_buffer.h:1: (CRITICAL) File not mentioned in any build rule.
/home/travis/build/widelands/widelands/src/graphic/minimap_layer.h:1: (CRITICAL) File not mentioned in any build rule.

Did you already run the regression tests?

Revision history for this message
SirVer (sirver) wrote :

Welcome back in action, Nicolai!!

The code does not compile without some changes with glbinding and once it is done compiling it crashes: Pastebin link: http://pastebin.com/9iBGe7iS
It also seems that my system (Mac OS X, Early 2013) cannot run the GL 4 code path.

Could you change the owner of the branch to widelands-dev so that fixes can be pushed?

I did not read through the code yet, but I want to open the discussion early. I would be interested in some benchmark numbers comparing the code paths. For my discussion, I am going by these assumptions, please let me know if they are incorrect:

1) GL2 code path is showing up in profiling, but is not the bottleneck in bigger maps/long games, i.e. where it matters.
2) GL4 code path creates the exact same output then GL2.
3) GL2 can not be removed, since this would cut off a significant amount of customers.
4) Offloading work from the CPU to the GPU is desirable, since Widelands is CPU bound while the GPU is mostly idle.

Given 1) and 3), I dislike the idea of introducing another renderer. We got rid of the b18 software renderer because maintaining 2 render paths was painful and therefore not correctly done (many bugs, not the same capabilities after some time for divergence). I would hate to have to support 2 render paths again. Also there is still the dream of having Widelands on mobile - which seems to be essentially GL2 ES or GL3 ES - which seems closer to the current GL2 code path.

Given 4) the question is how much can we get from your changes into the GL 2 code path? For example, using texture lookups for the minimap seems feasible in GL2 as well afaik, but terrain rendering probably is not.

My ideal outcome of this proposals is that we get as many features as possible backported into the GL2 code path and drop the GL4 code path entirely. Is that feasible?

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

Some quick responses:

Output:
- The log contains "Using GL2/GL4 blit path/terrain rendering path" after the "END GRAPHICS REPORT" line

Build errors:
- I'll have a look at the glbinding variant.
- I ran the debug build which complained about style errors which I then fixed. Guess I was too naive to assume that that includes a comprehensive set of checks...

Running, performance, etc:
- Yes, MacOSX is unfortunately pretty crappy at keeping up with modern OpenGL. It's really surprising that Apple gets away with that, to be honest.
- GLES: All the required extensions are actually part of GLES 3.1, despite the GL4 name
- I understand the desire to keep things simple.
- The minimap renderer can probably be used without additional extensions.
- Terrain rendering and the blit path are trickier. Blitting could use instancing instead of SSBOs, but that's probably more of a step backwards (tiny instances aren't very efficient). Terrain rendering needs the table of terrain types in the vertex and fragment shaders; those should fit into a UBO instead of an SSBO, but that still requires an extra extension. Perhaps old-style uniforms could be used as well? I need to look up the available limits for that - it might be possible, but it might not be.

So, I'd give up on the separate blit path without extra extensions. Terrain rendering might be possible, but I need to look into it again. Minimap rendering is probably possible, and is the biggest win anyway.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Bunnybot encountered an error while working on this merge proposal:

HTTP Error 500: Internal Server Error

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 1808. State: failed. Details: https://travis-ci.org/widelands/widelands/builds/189782905.
Appveyor build 1646. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_nha_widelands_graphics-1646.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Bunnybot encountered an error while working on this merge proposal:

HTTP Error 500: Internal Server Error

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 1808. State: failed. Details: https://travis-ci.org/widelands/widelands/builds/189782905.
Appveyor build 1646. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_nha_widelands_graphics-1646.

Revision history for this message
bunnybot (widelandsofficial) wrote :

Continuous integration builds have changed state:

Travis build 1819. State: failed. Details: https://travis-ci.org/widelands/widelands/builds/192103262.
Appveyor build 1657. State: failed. Details: https://ci.appveyor.com/project/widelands-dev/widelands/build/_widelands_dev_widelands_graphics-1657.

Revision history for this message
SirVer (sirver) wrote :

Nicolai, are you still working on this? I am setting to in-progress for the meantime so that it does not show up as an active review.

Revision history for this message
SirVer (sirver) wrote :

Actually, I can only reject it, there is no in-progress for reviews. I am rejecting for now, since this is not gonna be merged like this I think.

Revision history for this message
GunChleoc (gunchleoc) wrote :

You can set it to "Work in Progress" on the top of the page. I have now done so. Below the comment window. "Abstain" might do the trick if you had set a previous state before.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'cmake/codecheck/rules/correct_include_order'
2--- cmake/codecheck/rules/correct_include_order 2015-04-04 15:03:14 +0000
3+++ cmake/codecheck/rules/correct_include_order 2017-01-07 12:36:16 +0000
4@@ -45,9 +45,10 @@
5 delims = set(entry[1] for entry in block)
6 if len(delims) != 1:
7 errors.append((fn, block[0][0], """Use either '"' or '<' for all includes in a block."""))
8- if sorted(includes) != includes:
9- errors.append((fn, block[0][0], "Include block is not sorted alphabetically."))
10- return errors
11+ for include_sorted, include_actual in zip(sorted(includes), includes):
12+ if include_sorted != include_actual:
13+ errors.append((fn, block[0][0], "Include block is not sorted alphabetically: '%s' must be included before '%s'" % (include_sorted, include_actual)))
14+ return errors
15
16 if ".cc" in fn:
17 base_file = os.path.basename(fn)[:-3]
18
19=== modified file 'data/shaders/blit.fp'
20--- data/shaders/blit.fp 2016-02-01 10:24:34 +0000
21+++ data/shaders/blit.fp 2017-01-07 12:36:16 +0000
22@@ -3,13 +3,12 @@
23 uniform sampler2D u_texture;
24 uniform sampler2D u_mask;
25
26-varying vec2 out_mask_texture_coordinate;
27-varying vec2 out_texture_coordinate;
28+varying vec4 out_texture_coordinates;
29 varying vec4 out_blend;
30 varying float out_program_flavor;
31
32 void main() {
33- vec4 texture_color = texture2D(u_texture, out_texture_coordinate);
34+ vec4 texture_color = texture2D(u_texture, out_texture_coordinates.xy);
35
36 // See http://en.wikipedia.org/wiki/YUV.
37 float luminance = dot(vec3(0.299, 0.587, 0.114), texture_color.rgb);
38@@ -19,7 +18,7 @@
39 } else if (out_program_flavor == 1.) {
40 gl_FragColor = vec4(vec3(luminance) * out_blend.rgb, out_blend.a * texture_color.a);
41 } else {
42- vec4 mask_color = texture2D(u_mask, out_mask_texture_coordinate);
43+ vec4 mask_color = texture2D(u_mask, out_texture_coordinates.zw);
44 float blend_influence = mask_color.r * mask_color.a;
45 gl_FragColor = vec4(
46 mix(texture_color.rgb, out_blend.rgb * luminance, blend_influence),
47
48=== modified file 'data/shaders/blit.vp'
49--- data/shaders/blit.vp 2016-02-01 10:24:34 +0000
50+++ data/shaders/blit.vp 2017-01-07 12:36:16 +0000
51@@ -7,14 +7,15 @@
52 attribute vec4 attr_blend;
53 attribute float attr_program_flavor;
54
55-varying vec2 out_mask_texture_coordinate;
56-varying vec2 out_texture_coordinate;
57+// xy = texture_coordinate; zw = mask_texture_coordinate
58+// Sharing a single varying in this way can save hardware resources.
59+varying vec4 out_texture_coordinates;
60 varying vec4 out_blend;
61 varying float out_program_flavor;
62
63 void main() {
64- out_mask_texture_coordinate = attr_mask_texture_position;
65- out_texture_coordinate = attr_texture_position;
66+ out_texture_coordinates.zw = attr_mask_texture_position;
67+ out_texture_coordinates.xy = attr_texture_position;
68 out_blend = attr_blend;
69 out_program_flavor = attr_program_flavor;
70 gl_Position = vec4(attr_position, 1.);
71
72=== added file 'data/shaders/blit_gl4.vp'
73--- data/shaders/blit_gl4.vp 1970-01-01 00:00:00 +0000
74+++ data/shaders/blit_gl4.vp 2017-01-07 12:36:16 +0000
75@@ -0,0 +1,70 @@
76+#version 130
77+#extension GL_ARB_separate_shader_objects : enable
78+#extension GL_ARB_shader_storage_buffer_object : enable
79+#extension GL_ARB_uniform_buffer_object : enable
80+
81+// Varyings.
82+out vec4 out_texture_coordinates;
83+out vec4 out_blend;
84+out float out_program_flavor;
85+
86+struct Rect {
87+ float dst_x, dst_y, dst_width, dst_height;
88+ uint src_x, src_y, src_width, src_height;
89+ uint src_parent_width, src_parent_height;
90+ uint mask_x, mask_y, mask_width, mask_height;
91+ uint mask_parent_width, mask_parent_height;
92+ uint blend_r, blend_g, blend_b, blend_a;
93+ float program_flavor, z;
94+};
95+
96+layout(std140, binding=0) buffer ssbo_rects {
97+ Rect rects[];
98+};
99+
100+void main() {
101+ // Determine rect and vertex relative to rect
102+ uint rect_idx = uint(gl_VertexID) >> 2;
103+ uint vertex_idx = uint(gl_VertexID) & 3u;
104+ Rect r = rects[rect_idx];
105+
106+ out_program_flavor = r.program_flavor;
107+
108+ // Position
109+ gl_Position = vec4(r.dst_x, r.dst_y, r.z, 1.);
110+ if ((vertex_idx & 1u) != 0u)
111+ gl_Position.x += r.dst_width;
112+ if ((vertex_idx & 2u) != 0u)
113+ gl_Position.y += r.dst_height;
114+
115+ // Texture coordinate
116+ uint tx = r.src_x;
117+ uint ty = r.src_y;
118+ if ((vertex_idx & 1u) != 0u)
119+ tx += r.src_width;
120+ if ((vertex_idx & 2u) == 0u)
121+ ty += r.src_height;
122+ out_texture_coordinates.x = tx * (1. / r.src_parent_width);
123+ out_texture_coordinates.y = 1.0 - ty * (1. / r.src_parent_height);
124+
125+ // Blending
126+ out_blend.a = r.blend_a * (1. / 255.);
127+
128+// if (out_program_flavor >= 1.) {
129+ out_blend.r = r.blend_r * (1. / 255.);
130+ out_blend.g = r.blend_g * (1. / 255.);
131+ out_blend.b = r.blend_b * (1. / 255.);
132+
133+// if (out_program_flavor >= 2.) {
134+ // Mask texture coordinate
135+ tx = r.mask_x;
136+ ty = r.mask_y;
137+ if ((vertex_idx & 1u) != 0u)
138+ tx += r.mask_width;
139+ if ((vertex_idx & 2u) == 0u)
140+ ty += r.mask_height;
141+ out_texture_coordinates.z = tx * (1. / r.mask_parent_width);
142+ out_texture_coordinates.w = 1.0 - ty * (1. / r.mask_parent_height);
143+// }
144+// }
145+}
146
147=== added file 'data/shaders/minimap_gl4.fp'
148--- data/shaders/minimap_gl4.fp 1970-01-01 00:00:00 +0000
149+++ data/shaders/minimap_gl4.fp 2017-01-07 12:36:16 +0000
150@@ -0,0 +1,108 @@
151+#version 130
152+#extension GL_ARB_uniform_buffer_object: enable
153+
154+// Varyings
155+in vec2 var_field;
156+
157+// Uniforms
158+uniform bool u_layer_terrain;
159+uniform bool u_layer_owner;
160+uniform uint u_layer_details;
161+
162+uniform vec2 u_frame_topleft;
163+uniform vec2 u_frame_bottomright;
164+
165+// Textures (map data).
166+uniform usampler2D u_terrain_base;
167+uniform sampler2D u_player_brightness;
168+uniform usampler2D u_minimap_extra;
169+uniform sampler2D u_terrain_color;
170+uniform sampler2D u_player_color;
171+
172+float calc_node_brightness(uint node_ubrightness) {
173+ // Brightness is really an 8-bit signed value, but it's stored in an
174+ // GL_RGBA8UI texture, so here we use signed (arithmetic) shifts to do
175+ // the conversion.
176+ int node_brightness = int(node_ubrightness << 24) >> 24;
177+ float brightness = 144. / 255. + node_brightness * (1. / 255.);
178+ brightness = min(1., brightness * (255. / 160.));
179+ return brightness;
180+}
181+
182+// Return true if a and b are closer than threshold modulo 1.
183+bool wrap_close(float a, float b, float threshold) {
184+ float dist = a - b;
185+ dist -= floor(dist + 0.5);
186+ return abs(dist) < threshold;
187+}
188+
189+void main() {
190+ float player_brightness = texture(u_player_brightness, var_field).r;
191+
192+ // Determine whether we're on the frame
193+ bool on_frame = false;
194+ float dfdx = abs(dFdx(var_field.x)) * 0.5;
195+ float dfdy = abs(dFdy(var_field.y)) * 0.5;
196+ float low, high, pix, width;
197+
198+ if (wrap_close(var_field.x, u_frame_topleft.x, dfdx) ||
199+ wrap_close(var_field.x, u_frame_bottomright.x, dfdx)) {
200+ on_frame = true;
201+ low = u_frame_topleft.y;
202+ high = u_frame_bottomright.y;
203+ pix = var_field.y;
204+ width = dfdy;
205+ } else if (wrap_close(var_field.y, u_frame_topleft.y, dfdy) ||
206+ wrap_close(var_field.y, u_frame_bottomright.y, dfdy)) {
207+ on_frame = true;
208+ low = u_frame_topleft.x;
209+ high = u_frame_bottomright.x;
210+ pix = var_field.x;
211+ width = dfdx;
212+ }
213+
214+ if (on_frame) {
215+ pix -= floor(pix - low); // Normalize to range [low, low + 1)
216+ if (pix <= high &&
217+ (int((pix - low) / (2 * width)) & 1) == 0) {
218+ gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
219+ return;
220+ }
221+ }
222+
223+ // Determine minimap color
224+ vec3 color = vec3(0.0, 0.0, 0.0);
225+ if (player_brightness > 0.0) {
226+ if (u_layer_terrain) {
227+ uvec4 node = texture(u_terrain_base, var_field);
228+ float brightness = calc_node_brightness(node.w);
229+
230+ color = texelFetch(u_terrain_color, ivec2(node.y, 0), 0).rgb;
231+ color *= brightness;
232+ }
233+
234+ if (u_layer_owner || u_layer_details != 0u) {
235+ uint extra = texture(u_minimap_extra, var_field).r;
236+
237+ if (u_layer_owner) {
238+ uint owner = extra & 0x3fu;
239+ if (owner > 0u) {
240+ vec3 player_color = texelFetch(u_player_color, ivec2(owner - 1u, 0), 0).rgb;
241+ color = mix(color, player_color, 0.5);
242+ }
243+ }
244+
245+ uint detail = extra >> 6u;
246+ if ((u_layer_details & 1u) != 0u && detail == 1u) {
247+ // Road
248+ color = mix(color, vec3(1, 1, 1), 0.5);
249+ } else if (detail != 0u && (u_layer_details & (1u << (detail - 1u))) != 0u) {
250+ // Flag or building
251+ color = vec3(1, 1, 1);
252+ }
253+ }
254+ }
255+
256+ gl_FragColor.rgb = color;
257+ gl_FragColor.w = 1.0;
258+}
259
260=== added file 'data/shaders/minimap_gl4.vp'
261--- data/shaders/minimap_gl4.vp 1970-01-01 00:00:00 +0000
262+++ data/shaders/minimap_gl4.vp 2017-01-07 12:36:16 +0000
263@@ -0,0 +1,11 @@
264+#version 130
265+
266+in vec3 in_position;
267+in vec2 in_field; // Fractional field
268+
269+out vec2 var_field;
270+
271+void main() {
272+ gl_Position = vec4(in_position, 1.);
273+ var_field = in_field;
274+}
275
276=== added file 'data/shaders/road_gl4.vp'
277--- data/shaders/road_gl4.vp 1970-01-01 00:00:00 +0000
278+++ data/shaders/road_gl4.vp 2017-01-07 12:36:16 +0000
279@@ -0,0 +1,89 @@
280+#version 130
281+#extension GL_ARB_separate_shader_objects : enable
282+#extension GL_ARB_shader_storage_buffer_object : enable
283+#extension GL_ARB_uniform_buffer_object : enable
284+
285+// From terrain_common_gl4:
286+void init_common();
287+uvec4 get_field_base(ivec2 coord);
288+float calc_node_brightness(uint node_ubrightness);
289+float calc_brightness(ivec2 coord, uint node_ubrightness);
290+void calc_basepix(ivec2 coord, out vec2 basepix, out float heightpix, out uint node_brightness);
291+void calc_pix(ivec2 coord, out vec2 pix, out uint node_brightness);
292+
293+// Attributes.
294+uniform vec2 u_position_scale;
295+uniform vec2 u_position_offset;
296+uniform float u_z_value;
297+
298+// Road data.
299+struct Road {
300+ ivec2 start;
301+ uint direction; // WALK_E = 2, WALK_SE = 3, WALK_SW = 4
302+ uint texture;
303+};
304+
305+layout(std140, binding=0) buffer ssbo_roads {
306+ Road u_roads[];
307+};
308+
309+// Road texture information.
310+struct Texture {
311+ float x, y, w, h;
312+};
313+
314+layout(std140) uniform block_textures {
315+ Texture u_textures[128];
316+};
317+
318+// Outputs.
319+varying vec2 out_texture_position;
320+varying float out_brightness;
321+
322+// Constants
323+const float kRoadThicknessInPixels = 5.;
324+const float kRoadElongationFraction = .1;
325+
326+void main() {
327+ init_common();
328+
329+ Road road = u_roads[gl_VertexID >> 2];
330+ bool is_end = (gl_VertexID & 2) != 0;
331+ bool is_right = (gl_VertexID & 1) != 0;
332+
333+ // Calculate end coordinate.
334+ ivec2 end = road.start;
335+ end.x += 3 - int(road.direction);
336+ if (road.direction >= 3u) {
337+ end.y += 1;
338+ end.x += (road.start.y & 1) != 0 ? 1 : 0;
339+ }
340+
341+ // Map coordinates
342+ vec2 start_pix, end_pix;
343+ uint start_node_brightness, end_node_brightness;
344+ calc_pix(road.start, start_pix, start_node_brightness);
345+ calc_pix(end, end_pix, end_node_brightness);
346+
347+ vec2 delta_pix = end_pix - start_pix;
348+ vec2 road_overshoot = delta_pix * kRoadElongationFraction;
349+ vec2 road_thickness = vec2(-delta_pix.y, delta_pix.x) * (kRoadThicknessInPixels / length(delta_pix));
350+
351+ vec2 pix =
352+ (is_end ? end_pix + road_overshoot : start_pix - road_overshoot) +
353+ (is_right ? -road_thickness : road_thickness);
354+ gl_Position = vec4(pix * u_position_scale + u_position_offset, u_z_value, 1.);
355+
356+ // Brightness
357+ out_brightness = calc_brightness(
358+ is_end ? end : road.start,
359+ is_end ? end_node_brightness : start_node_brightness);
360+
361+ // Texture coordinates.
362+ Texture tex = u_textures[road.texture];
363+ out_texture_position = vec2(tex.x, tex.y);
364+ if (is_end)
365+ out_texture_position.x += tex.w;
366+ if (!is_right)
367+ out_texture_position.y += tex.h;
368+}
369
370=== added file 'data/shaders/terrain_common_gl4.vp'
371--- data/shaders/terrain_common_gl4.vp 1970-01-01 00:00:00 +0000
372+++ data/shaders/terrain_common_gl4.vp 2017-01-07 12:36:16 +0000
373@@ -0,0 +1,59 @@
374+// Helper functions that are shared by the vertex shaders for terrain and
375+// roads.
376+#version 130
377+#extension GL_ARB_uniform_buffer_object: enable
378+
379+// Textures (map data).
380+uniform usampler2D u_terrain_base;
381+uniform sampler2D u_player_brightness;
382+
383+// Constants
384+const float kTriangleWidth = 64;
385+const float kTriangleHeight = 32;
386+const float kHeightFactor = 5;
387+
388+vec2 map_size;
389+vec2 map_inv_size;
390+
391+void init_common() {
392+ map_size = vec2(textureSize(u_terrain_base, 0));
393+ map_inv_size = 1. / map_size;
394+}
395+
396+uvec4 get_field_base(ivec2 coord) {
397+ return texture(u_terrain_base, (coord + 0.5) * map_inv_size);
398+}
399+
400+float calc_node_brightness(uint node_ubrightness) {
401+ // Brightness is really an 8-bit signed value, but it's stored in an
402+ // GL_RGBA8UI texture, so here we use signed (arithmetic) shifts to do
403+ // the conversion.
404+ int node_brightness = int(node_ubrightness << 24) >> 24;
405+ float brightness = 144. / 255. + node_brightness * (1. / 255.);
406+ brightness = min(1., brightness * (255. / 160.));
407+ return brightness;
408+}
409+
410+float calc_brightness(ivec2 coord, uint node_ubrightness) {
411+ float brightness = calc_node_brightness(node_ubrightness);
412+
413+ brightness *= texture(u_player_brightness, (coord + 0.5) * map_inv_size).r;
414+
415+ return brightness;
416+}
417+
418+void calc_basepix(ivec2 coord, out vec2 basepix, out float heightpix, out uint node_brightness) {
419+ uvec4 field = get_field_base(coord);
420+ basepix.x = coord.x * kTriangleWidth;
421+ if ((coord.y & 1) != 0)
422+ basepix.x += kTriangleWidth / 2;
423+ basepix.y = coord.y * kTriangleHeight;
424+ heightpix = field.z * kHeightFactor;
425+ node_brightness = field.w;
426+}
427+
428+void calc_pix(ivec2 coord, out vec2 pix, out uint node_brightness) {
429+ float heightpix;
430+ calc_basepix(coord, pix, heightpix, node_brightness);
431+ pix.y -= heightpix;
432+}
433
434=== added file 'data/shaders/terrain_gl4.fp'
435--- data/shaders/terrain_gl4.fp 1970-01-01 00:00:00 +0000
436+++ data/shaders/terrain_gl4.fp 2017-01-07 12:36:16 +0000
437@@ -0,0 +1,58 @@
438+#version 130
439+
440+uniform sampler2D u_terrain_texture;
441+uniform sampler2D u_dither_texture;
442+
443+uniform vec2 u_texture_dimensions;
444+
445+in vec4 var_texture;
446+in vec4 var_dither[3];
447+in float var_brightness;
448+
449+// TODO(sirver): This is a hack to make sure we are sampling inside of the
450+// terrain texture. This is a common problem with OpenGL and texture atlases.
451+#define MARGIN 1e-2
452+
453+vec3 sample_terrain(vec2 offset, vec2 texcoord) {
454+ // The arbitrary multiplication by 0.99 makes sure that we never sample
455+ // outside of the texture in the texture atlas - this means non-perfect
456+ // pixel mapping of textures to the screen, but we are pretty meh about that
457+ // here.
458+ vec2 texture_fract = clamp(
459+ fract(texcoord),
460+ vec2(MARGIN, MARGIN),
461+ vec2(1. - MARGIN, 1. - MARGIN));
462+ return texture2D(u_terrain_texture, offset + u_texture_dimensions * texture_fract).rgb;
463+}
464+
465+vec3 apply_dither(vec3 color, vec2 texcoord, vec4 dither) {
466+ vec2 offset = dither.xy;
467+ vec2 dither_tc = dither.zw;
468+
469+ // Cut-off value to avoid unnecessary texture samples. The cut-off value
470+ // is chosen based on the dither mask, which happens to be 1 in 3/4 of the
471+ // texture.
472+ //
473+ // Note that cuting off in this way would be slightly incorrect if mipmaps
474+ // were used, because derivatives become undefined under non-uniform
475+ // control flow.
476+ if (dither_tc.y < 0.75)
477+ return color;
478+
479+ float dither_factor = texture2D(u_dither_texture, dither_tc).a;
480+ vec3 dither_color = sample_terrain(offset, texcoord);
481+
482+ // dither_factor is 0 when the other texture replace the base texture
483+ // entirely, and 1 when the base texture is not replaced at all.
484+ return mix(dither_color, color, dither_factor);
485+}
486+
487+void main() {
488+ vec2 texcoord = var_texture.zw;
489+ vec3 clr = sample_terrain(var_texture.xy, texcoord);
490+ clr = apply_dither(clr, texcoord, var_dither[0]);
491+ clr = apply_dither(clr, texcoord, var_dither[1]);
492+ clr = apply_dither(clr, texcoord, var_dither[2]);
493+ clr *= var_brightness;
494+ gl_FragColor = vec4(clr, 1.);
495+}
496
497=== added file 'data/shaders/terrain_gl4.vp'
498--- data/shaders/terrain_gl4.vp 1970-01-01 00:00:00 +0000
499+++ data/shaders/terrain_gl4.vp 2017-01-07 12:36:16 +0000
500@@ -0,0 +1,157 @@
501+#version 130
502+#extension GL_ARB_uniform_buffer_object: enable
503+
504+// From terrain_common_gl4:
505+void init_common();
506+uvec4 get_field_base(ivec2 coord);
507+float calc_brightness(ivec2 coord, uint node_ubrightness);
508+void calc_basepix(ivec2 coord, out vec2 basepix, out float heightpix, out uint node_brightness);
509+void calc_pix(ivec2 coord, out vec2 pix, out uint node_brightness);
510+
511+// Per-vertex Attributes.
512+// X,Y: relative vertex map coordinate
513+// Z,W: relative triangle map coordinate
514+// low bit of Z: 0 for d-triangle, 1 for r-triangle
515+// bits 1&2 of Z: vertex ID for dithering
516+in ivec4 in_vertex_coordinate;
517+
518+// Per-instance/patch Attributes.
519+in ivec2 in_patch_coordinate;
520+
521+// Primary Constants.
522+uniform vec2 u_position_scale;
523+uniform vec2 u_position_offset;
524+uniform float u_z_value;
525+
526+// Terrain information (secondary constants in a uniform buffer).
527+struct Terrain {
528+ vec2 offset;
529+ int dither_layer;
530+};
531+
532+layout(std140) uniform block_terrains {
533+ Terrain u_terrains[128];
534+};
535+
536+// Varyings: Output of vertex shader.
537+
538+// X,Y: offset into texture atlas
539+// Z,W: texture coordinate
540+out vec4 var_texture;
541+
542+// X,Y: offset into texture atlas
543+// Z,W: dither texture coordinate
544+out vec4 var_dither[3];
545+
546+out float var_brightness;
547+
548+// Constants
549+const float kTextureSideLength = 64;
550+
551+void main() {
552+ init_common();
553+
554+ // Compute coordinate of field that owns the triangle to which this
555+ // vertex belongs.
556+ ivec2 field_coord;
557+ field_coord.x = in_patch_coordinate.x + (in_vertex_coordinate.z >> 3);
558+ field_coord.y = in_patch_coordinate.y + in_vertex_coordinate.w;
559+
560+ uvec4 field = get_field_base(field_coord);
561+ bool is_down = (in_vertex_coordinate.z & 1) == 0;
562+ uint dither_vid = uint((in_vertex_coordinate.z >> 1) & 3);
563+ uint terrain = is_down ? field.y : field.x;
564+
565+ ivec2 node_coord = in_patch_coordinate.xy + in_vertex_coordinate.xy;
566+ uint node_brightness;
567+ float node_heightpix;
568+ vec2 vertex_basepix;
569+
570+ calc_basepix(node_coord, vertex_basepix, node_heightpix, node_brightness);
571+ // On-screen position
572+ gl_Position.x = vertex_basepix.x;
573+ gl_Position.y = vertex_basepix.y - node_heightpix;
574+ gl_Position.xy = gl_Position.xy * u_position_scale + u_position_offset;
575+ gl_Position.z = u_z_value;
576+ gl_Position.w = 1.;
577+
578+ // Brightness
579+ var_brightness = calc_brightness(node_coord, node_brightness);
580+
581+ // Texture coordinates
582+ vec2 vertex_texcoord;
583+ vertex_texcoord.x = vertex_basepix.x * (1. / kTextureSideLength);
584+ vertex_texcoord.y = -vertex_basepix.y * (1. / kTextureSideLength);
585+
586+ // Base texture information
587+ var_texture.xy = u_terrains[terrain].offset;
588+ var_texture.zw = vertex_texcoord;
589+
590+ // Dithering. Vertices are labeled by their dither_vid. Note that the VIDs
591+ // are different depending on whether the vertex is part of the r- or d-
592+ // triangle.
593+ //
594+ // 2-1---d---0
595+ // / \ /
596+ // r r\d d
597+ // / \ /
598+ // 0---r--1-2
599+ //
600+ // Channel 0: opposite triangle of the same field
601+ // Channel 1: x-major outside neighbor
602+ // Channel 2: y-major outside neighbor
603+ int dither_layer = u_terrains[terrain].dither_layer;
604+ {
605+ uint other_terrain = is_down ? field.x : field.y;
606+
607+ var_dither[0].zw = vec2(0.5, 0);
608+
609+ if (other_terrain != terrain) {
610+ int other_dither_layer = u_terrains[other_terrain].dither_layer;
611+
612+ if (other_dither_layer > dither_layer) {
613+ var_dither[0].xy = u_terrains[other_terrain].offset;
614+ if (dither_vid != 0u)
615+ var_dither[0].zw = vec2(2u - dither_vid, 1);
616+ }
617+ }
618+ }
619+ {
620+ ivec2 other_coord = field_coord;
621+ other_coord.x += is_down ? -1 : 1;
622+ uvec4 other_field = get_field_base(other_coord);
623+ uint other_terrain = is_down ? other_field.x : other_field.y;
624+
625+ var_dither[1].zw = vec2(0.5, 0);
626+
627+ if (other_terrain != terrain) {
628+ int other_dither_layer = u_terrains[other_terrain].dither_layer;
629+
630+ if (other_dither_layer > dither_layer) {
631+ var_dither[1].xy = u_terrains[other_terrain].offset;
632+ if (dither_vid != 1u)
633+ var_dither[1].zw = vec2(dither_vid / 2u, 1);
634+ }
635+ }
636+ }
637+ {
638+ ivec2 other_coord = field_coord;
639+ other_coord.y += is_down ? 1 : -1;
640+ other_coord.x += is_down ? -1 : 0;
641+ other_coord.x += ((field_coord.y & 1) != 0) ? 1 : 0;
642+ uvec4 other_field = get_field_base(other_coord);
643+ uint other_terrain = is_down ? other_field.x : other_field.y;
644+
645+ var_dither[2].zw = vec2(0.5, 0);
646+
647+ if (other_terrain != terrain) {
648+ int other_dither_layer = u_terrains[other_terrain].dither_layer;
649+
650+ if (other_dither_layer > dither_layer) {
651+ var_dither[2].xy = u_terrains[other_terrain].offset;
652+ if (dither_vid != 2u)
653+ var_dither[2].zw = vec2(1u - dither_vid, 1);
654+ }
655+ }
656+ }
657+}
658
659=== modified file 'src/graphic/CMakeLists.txt'
660--- src/graphic/CMakeLists.txt 2016-10-25 07:07:14 +0000
661+++ src/graphic/CMakeLists.txt 2017-01-07 12:36:16 +0000
662@@ -120,6 +120,8 @@
663 gl/road_program.h
664 gl/terrain_program.cc
665 gl/terrain_program.h
666+ gl/terrain_program_gl4.cc
667+ gl/terrain_program_gl4.h
668 gl/dither_program.cc
669 gl/dither_program.h
670 DEPENDS
671@@ -195,6 +197,8 @@
672 SRCS
673 game_renderer.cc
674 game_renderer.h
675+ game_renderer_gl4.cc
676+ game_renderer_gl4.h
677 DEPENDS
678 base_geometry
679 base_macros
680
681=== modified file 'src/graphic/game_renderer.cc'
682--- src/graphic/game_renderer.cc 2016-11-17 18:33:09 +0000
683+++ src/graphic/game_renderer.cc 2017-01-07 12:36:16 +0000
684@@ -1,5 +1,5 @@
685 /*
686- * Copyright (C) 2010-2013 by the Widelands Development Team
687+ * Copyright (C) 2010-2016 by the Widelands Development Team
688 *
689 * This program is free software; you can redistribute it and/or
690 * modify it under the terms of the GNU General Public License
691@@ -21,7 +21,9 @@
692
693 #include <memory>
694
695+#include "graphic/game_renderer_gl4.h"
696 #include "graphic/gl/coordinate_conversion.h"
697+#include "graphic/gl/fields_to_draw.h"
698 #include "graphic/graphic.h"
699 #include "graphic/render_queue.h"
700 #include "graphic/rendertarget.h"
701@@ -71,6 +73,23 @@
702
703 using namespace Widelands;
704
705+class GameRendererGl2 : public GameRenderer {
706+public:
707+ // Draw the map for the given parameters (see rendermap). 'player'
708+ // can be nullptr in which case the whole map is drawn.
709+ void draw(const Widelands::EditorGameBase& egbase,
710+ const Vector2f& view_offset,
711+ const float scale,
712+ const TextToDraw draw_text,
713+ const Widelands::Player* player,
714+ RenderTarget* dst) override;
715+
716+private:
717+ // This is owned and handled by us, but handed to the RenderQueue, so we
718+ // basically promise that this stays valid for one frame.
719+ FieldsToDrawGl2 fields_to_draw_;
720+};
721+
722 // Returns the brightness value in [0, 1.] for 'fcoords' at 'gametime' for
723 // 'player' (which can be nullptr).
724 float field_brightness(const FCoords& fcoords,
725@@ -98,7 +117,7 @@
726 }
727
728 void draw_objects_for_visible_field(const EditorGameBase& egbase,
729- const FieldsToDraw::Field& field,
730+ const FieldToDrawBase& field,
731 const float zoom,
732 const TextToDraw draw_text,
733 const Player* player,
734@@ -128,7 +147,7 @@
735 }
736 }
737
738-void draw_objets_for_formerly_visible_field(const FieldsToDraw::Field& field,
739+void draw_objets_for_formerly_visible_field(const FieldToDrawBase& field,
740 const Player::Field& player_field,
741 const float zoom,
742 RenderTarget* dst) {
743@@ -204,49 +223,51 @@
744 }
745 }
746
747+} // namespace
748+
749 // Draws the objects (animations & overlays).
750-void draw_objects(const EditorGameBase& egbase,
751- const float zoom,
752- const FieldsToDraw& fields_to_draw,
753- const Player* player,
754- const TextToDraw draw_text,
755- RenderTarget* dst) {
756+void GameRenderer::draw_objects(const EditorGameBase& egbase,
757+ const float scale,
758+ const FieldsToDrawRefBase& fields_to_draw,
759+ const Player* player,
760+ const TextToDraw draw_text,
761+ RenderTarget* dst) {
762 std::vector<FieldOverlayManager::OverlayInfo> overlay_info;
763- for (size_t current_index = 0; current_index < fields_to_draw.size(); ++current_index) {
764- const FieldsToDraw::Field& field = fields_to_draw.at(current_index);
765- if (!field.all_neighbors_valid()) {
766+ for (auto cursor = fields_to_draw.cursor(); cursor.valid(); cursor.next()) {
767+ const FieldToDrawBase& field = cursor.field();
768+ if (!cursor.all_neighbors_valid()) {
769 continue;
770 }
771
772- const FieldsToDraw::Field& rn = fields_to_draw.at(field.rn_index);
773- const FieldsToDraw::Field& bln = fields_to_draw.at(field.bln_index);
774- const FieldsToDraw::Field& brn = fields_to_draw.at(field.brn_index);
775+ const FieldToDrawBase& rn = cursor.rn();
776+ const FieldToDrawBase& bln = cursor.bln();
777+ const FieldToDrawBase& brn = cursor.brn();
778
779 if (field.is_border) {
780 assert(field.owner != nullptr);
781 uint32_t const anim_idx = field.owner->tribe().frontier_animation();
782 if (field.vision) {
783 dst->blit_animation(
784- field.rendertarget_pixel, zoom, anim_idx, 0, field.owner->get_playercolor());
785+ field.rendertarget_pixel, scale, anim_idx, 0, field.owner->get_playercolor());
786 }
787 for (const auto& nf : {rn, bln, brn}) {
788 if ((field.vision || nf.vision) && nf.is_border &&
789 (field.owner == nf.owner || nf.owner == nullptr)) {
790- dst->blit_animation(middle(field.rendertarget_pixel, nf.rendertarget_pixel), zoom,
791+ dst->blit_animation(middle(field.rendertarget_pixel, nf.rendertarget_pixel), scale,
792 anim_idx, 0, field.owner->get_playercolor());
793 }
794 }
795 }
796
797 if (1 < field.vision) { // Render stuff that belongs to the node.
798- draw_objects_for_visible_field(egbase, field, zoom, draw_text, player, dst);
799+ draw_objects_for_visible_field(egbase, field, scale, draw_text, player, dst);
800 } else if (field.vision == 1) {
801 // We never show census or statistics for objects in the fog.
802 assert(player != nullptr);
803 const Map& map = egbase.map();
804 const Player::Field& player_field =
805 player->fields()[map.get_index(field.fcoords, map.get_width())];
806- draw_objets_for_formerly_visible_field(field, player_field, zoom, dst);
807+ draw_objets_for_formerly_visible_field(field, player_field, scale, dst);
808 }
809
810 const FieldOverlayManager& overlay_manager = egbase.get_ibase()->field_overlay_manager();
811@@ -255,8 +276,8 @@
812 overlay_manager.get_overlays(field.fcoords, &overlay_info);
813 for (const auto& overlay : overlay_info) {
814 dst->blitrect_scale(
815- Rectf(field.rendertarget_pixel - overlay.hotspot.cast<float>() * zoom,
816- overlay.pic->width() * zoom, overlay.pic->height() * zoom),
817+ Rectf(field.rendertarget_pixel - overlay.hotspot.cast<float>() * scale,
818+ overlay.pic->width() * scale, overlay.pic->height() * scale),
819 overlay.pic, Recti(0, 0, overlay.pic->width(), overlay.pic->height()), 1.f,
820 BlendMode::UseAlpha);
821 }
822@@ -272,8 +293,8 @@
823 (field.rendertarget_pixel.y + rn.rendertarget_pixel.y + brn.rendertarget_pixel.y) /
824 3.f);
825 for (const auto& overlay : overlay_info) {
826- dst->blitrect_scale(Rectf(tripos - overlay.hotspot.cast<float>() * zoom,
827- overlay.pic->width() * zoom, overlay.pic->height() * zoom),
828+ dst->blitrect_scale(Rectf(tripos - overlay.hotspot.cast<float>() * scale,
829+ overlay.pic->width() * scale, overlay.pic->height() * scale),
830 overlay.pic,
831 Recti(0, 0, overlay.pic->width(), overlay.pic->height()), 1.f,
832 BlendMode::UseAlpha);
833@@ -291,8 +312,8 @@
834 (field.rendertarget_pixel.y + bln.rendertarget_pixel.y + brn.rendertarget_pixel.y) /
835 3.f);
836 for (const auto& overlay : overlay_info) {
837- dst->blitrect_scale(Rectf(tripos - overlay.hotspot.cast<float>() * zoom,
838- overlay.pic->width() * zoom, overlay.pic->height() * zoom),
839+ dst->blitrect_scale(Rectf(tripos - overlay.hotspot.cast<float>() * scale,
840+ overlay.pic->width() * scale, overlay.pic->height() * scale),
841 overlay.pic,
842 Recti(0, 0, overlay.pic->width(), overlay.pic->height()), 1.f,
843 BlendMode::UseAlpha);
844@@ -301,37 +322,42 @@
845 }
846 }
847
848-} // namespace
849-
850 GameRenderer::GameRenderer() {
851 }
852
853 GameRenderer::~GameRenderer() {
854 }
855
856+std::unique_ptr<GameRenderer>
857+GameRenderer::create() {
858+ if (GameRendererGl4::supported())
859+ return std::unique_ptr<GameRenderer>(new GameRendererGl4);
860+ return std::unique_ptr<GameRenderer>(new GameRendererGl2);
861+}
862+
863 void GameRenderer::rendermap(const Widelands::EditorGameBase& egbase,
864- const Vector2f& viewpoint,
865- const float zoom,
866+ const Vector2f& view_offset,
867+ float zoom,
868 const Widelands::Player& player,
869- const TextToDraw draw_text,
870+ TextToDraw draw_text,
871 RenderTarget* dst) {
872- draw(egbase, viewpoint, zoom, draw_text, &player, dst);
873+ draw(egbase, view_offset, zoom, draw_text, &player, dst);
874 }
875
876 void GameRenderer::rendermap(const Widelands::EditorGameBase& egbase,
877- const Vector2f& viewpoint,
878- const float zoom,
879- const TextToDraw draw_text,
880+ const Vector2f& view_offset,
881+ float zoom,
882+ TextToDraw draw_text,
883 RenderTarget* dst) {
884- draw(egbase, viewpoint, zoom, draw_text, nullptr, dst);
885+ draw(egbase, view_offset, zoom, draw_text, nullptr, dst);
886 }
887
888-void GameRenderer::draw(const EditorGameBase& egbase,
889- const Vector2f& viewpoint,
890- const float zoom,
891- const TextToDraw draw_text,
892- const Player* player,
893- RenderTarget* dst) {
894+void GameRendererGl2::draw(const EditorGameBase& egbase,
895+ const Vector2f& viewpoint,
896+ const float zoom,
897+ const TextToDraw draw_text,
898+ const Player* player,
899+ RenderTarget* dst) {
900 assert(viewpoint.x >= 0); // divisions involving negative numbers are bad
901 assert(viewpoint.y >= 0);
902 assert(dst->get_offset().x <= 0);
903@@ -372,59 +398,48 @@
904
905 const float scale = 1.f / zoom;
906 fields_to_draw_.reset(minfx, maxfx, minfy, maxfy);
907- for (int32_t fy = minfy; fy <= maxfy; ++fy) {
908- for (int32_t fx = minfx; fx <= maxfx; ++fx) {
909- FieldsToDraw::Field& f =
910- *fields_to_draw_.mutable_field(fields_to_draw_.calculate_index(fx, fy));
911-
912- f.geometric_coords = Coords(fx, fy);
913-
914- f.ln_index = fields_to_draw_.calculate_index(fx - 1, fy);
915- f.rn_index = fields_to_draw_.calculate_index(fx + 1, fy);
916- f.trn_index = fields_to_draw_.calculate_index(fx + (fy & 1), fy - 1);
917- f.bln_index = fields_to_draw_.calculate_index(fx + (fy & 1) - 1, fy + 1);
918- f.brn_index = fields_to_draw_.calculate_index(fx + (fy & 1), fy + 1);
919-
920- // Texture coordinates for pseudo random tiling of terrain and road
921- // graphics. Since screen space X increases top-to-bottom and OpenGL
922- // increases bottom-to-top we flip the y coordinate to not have
923- // terrains and road graphics vertically mirrorerd.
924- Vector2f map_pixel =
925- MapviewPixelFunctions::to_map_pixel_ignoring_height(f.geometric_coords);
926- f.texture_coords.x = map_pixel.x / kTextureSideLength;
927- f.texture_coords.y = -map_pixel.y / kTextureSideLength;
928-
929- Coords normalized = f.geometric_coords;
930- map.normalize_coords(normalized);
931- f.fcoords = map.get_fcoords(normalized);
932-
933- map_pixel.y -= f.fcoords.field->get_height() * kHeightFactor;
934-
935- f.rendertarget_pixel = MapviewPixelFunctions::map_to_panel(viewpoint, zoom, map_pixel);
936- f.gl_position = f.surface_pixel = f.rendertarget_pixel +
937- dst->get_rect().origin().cast<float>() +
938- dst->get_offset().cast<float>();
939- pixel_to_gl_renderbuffer(
940- surface_width, surface_height, &f.gl_position.x, &f.gl_position.y);
941-
942- f.brightness = field_brightness(f.fcoords, gametime, map, player);
943-
944- PlayerNumber owned_by = f.fcoords.field->get_owned_by();
945- f.owner = owned_by != 0 ? &egbase.player(owned_by) : nullptr;
946- f.is_border = f.fcoords.field->is_border();
947- f.vision = 2;
948- f.roads = f.fcoords.field->get_roads();
949- if (player && !player->see_all()) {
950- const Player::Field& pf = player->fields()[map.get_index(f.fcoords, map.get_width())];
951- f.roads = pf.roads;
952- f.vision = pf.vision;
953- if (pf.vision == 1) {
954- f.owner = pf.owner != 0 ? &egbase.player(owned_by) : nullptr;
955- f.is_border = pf.border;
956- }
957+ for (auto cursor = fields_to_draw_.cursor(); cursor.valid(); cursor.next()) {
958+ FieldToDrawGl2& f = cursor.mutable_field();
959+
960+ // Texture coordinates for pseudo random tiling of terrain and road
961+ // graphics. Since screen space X increases top-to-bottom and OpenGL
962+ // increases bottom-to-top we flip the y coordinate to not have
963+ // terrains and road graphics vertically mirrorerd.
964+ Vector2f map_pixel =
965+ MapviewPixelFunctions::to_map_pixel_ignoring_height(cursor.geometric_coords());
966+ f.texture_coords.x = map_pixel.x / kTextureSideLength;
967+ f.texture_coords.y = -map_pixel.y / kTextureSideLength;
968+
969+ Coords normalized = cursor.geometric_coords();
970+ map.normalize_coords(normalized);
971+ f.fcoords = map.get_fcoords(normalized);
972+
973+ map_pixel.y -= f.fcoords.field->get_height() * kHeightFactor;
974+
975+ f.rendertarget_pixel = MapviewPixelFunctions::map_to_panel(viewpoint, zoom, map_pixel);
976+ f.gl_position = f.surface_pixel = f.rendertarget_pixel +
977+ dst->get_rect().origin().cast<float>() +
978+ dst->get_offset().cast<float>();
979+ pixel_to_gl_renderbuffer(
980+ surface_width, surface_height, &f.gl_position.x, &f.gl_position.y);
981+
982+ f.brightness = field_brightness(f.fcoords, gametime, map, player);
983+
984+ PlayerNumber owned_by = f.fcoords.field->get_owned_by();
985+ f.owner = owned_by != 0 ? &egbase.player(owned_by) : nullptr;
986+ f.is_border = f.fcoords.field->is_border();
987+ f.vision = 2;
988+ f.roads = f.fcoords.field->get_roads();
989+ if (player && !player->see_all()) {
990+ const Player::Field& pf = player->fields()[map.get_index(f.fcoords, map.get_width())];
991+ f.roads = pf.roads;
992+ f.vision = pf.vision;
993+ if (pf.vision == 1) {
994+ f.owner = pf.owner != 0 ? &egbase.player(owned_by) : nullptr;
995+ f.is_border = pf.border;
996 }
997- f.roads |= edge_overlay_manager.get_overlay(f.fcoords);
998 }
999+ f.roads |= edge_overlay_manager.get_overlay(f.fcoords);
1000 }
1001
1002 // Enqueue the drawing of the terrain.
1003
1004=== modified file 'src/graphic/game_renderer.h'
1005--- src/graphic/game_renderer.h 2016-10-26 19:43:40 +0000
1006+++ src/graphic/game_renderer.h 2017-01-07 12:36:16 +0000
1007@@ -34,11 +34,26 @@
1008
1009 class RenderTarget;
1010
1011-// Renders the MapView on screen.
1012+/**
1013+ * This abstract base class renders the main game view into an
1014+ * arbitrary @ref RenderTarget.
1015+ *
1016+ * Specializations exist for different OpenGL rendering paths.
1017+ *
1018+ * Users of this class should keep instances alive for as long as possible,
1019+ * so that target-specific optimizations (such as caching data) can
1020+ * be effective.
1021+ *
1022+ * Every instance can only perform one render operation per frame. When
1023+ * multiple views of the map are open, each needs its own instance of
1024+ * GameRenderer.
1025+ */
1026 class GameRenderer {
1027 public:
1028- GameRenderer();
1029- ~GameRenderer();
1030+ virtual ~GameRenderer();
1031+
1032+ // Create a game renderer instance.
1033+ static std::unique_ptr<GameRenderer> create();
1034
1035 // Renders the map from a player's point of view into the given drawing
1036 // window. The 'viewpoint' is the top left screens pixel map pixel and
1037@@ -58,19 +73,23 @@
1038 TextToDraw draw_text,
1039 RenderTarget* dst);
1040
1041-private:
1042- // Draw the map for the given parameters (see rendermap). 'player'
1043- // can be nullptr in which case the whole map is drawn.
1044- void draw(const Widelands::EditorGameBase& egbase,
1045- const Vector2f& viewpoint,
1046- float scale,
1047- TextToDraw draw_text,
1048- const Widelands::Player* player,
1049- RenderTarget* dst);
1050-
1051- // This is owned and handled by us, but handed to the RenderQueue, so we
1052- // basically promise that this stays valid for one frame.
1053- FieldsToDraw fields_to_draw_;
1054+protected:
1055+ GameRenderer();
1056+
1057+ virtual void draw(const Widelands::EditorGameBase& egbase,
1058+ const Vector2f& view_offset,
1059+ const float zoom,
1060+ const TextToDraw draw_text,
1061+ const Widelands::Player* player,
1062+ RenderTarget* dst) = 0;
1063+
1064+ // Draws the objects (animations & overlays).
1065+ void draw_objects(const Widelands::EditorGameBase& egbase,
1066+ const float scale,
1067+ const FieldsToDrawRefBase& fields_to_draw,
1068+ const Widelands::Player* player,
1069+ const TextToDraw draw_text,
1070+ RenderTarget* dst);
1071
1072 DISALLOW_COPY_AND_ASSIGN(GameRenderer);
1073 };
1074
1075=== added file 'src/graphic/game_renderer_gl4.cc'
1076--- src/graphic/game_renderer_gl4.cc 1970-01-01 00:00:00 +0000
1077+++ src/graphic/game_renderer_gl4.cc 2017-01-07 12:36:16 +0000
1078@@ -0,0 +1,194 @@
1079+/*
1080+ * Copyright (C) 2010-2016 by the Widelands Development Team
1081+ *
1082+ * This program is free software; you can redistribute it and/or
1083+ * modify it under the terms of the GNU General Public License
1084+ * as published by the Free Software Foundation; either version 2
1085+ * of the License, or (at your option) any later version.
1086+ *
1087+ * This program is distributed in the hope that it will be useful,
1088+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1089+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1090+ * GNU General Public License for more details.
1091+ *
1092+ * You should have received a copy of the GNU General Public License
1093+ * along with this program; if not, write to the Free Software
1094+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1095+ *
1096+ */
1097+
1098+#include "graphic/game_renderer_gl4.h"
1099+
1100+#include "graphic/gl/terrain_program_gl4.h"
1101+#include "graphic/render_queue.h"
1102+#include "graphic/rendertarget.h"
1103+#include "graphic/surface.h"
1104+#include "logic/editor_game_base.h"
1105+#include "logic/map.h"
1106+#include "logic/map_objects/walkingdir.h"
1107+#include "logic/player.h"
1108+#include "logic/roadtype.h"
1109+#include "wui/edge_overlay_manager.h"
1110+#include "wui/interactive_base.h"
1111+#include "wui/mapviewpixelconstants.h"
1112+#include "wui/mapviewpixelfunctions.h"
1113+
1114+using namespace Widelands;
1115+
1116+/**
1117+ * This is the front-end of the GL4 rendering path. See game_renderer.cc for a
1118+ * general overview of terrain rendering.
1119+ *
1120+ * Only terrain rendering (terrain textures, dithering, and roads) differs
1121+ * substantially from the GL2 rendering path. To avoid CPU work, a persistent
1122+ * TerrainInformationGl4 object maintains texture data and other information
1123+ * across frames.
1124+ *
1125+ * The GameRendererGl4 contains per-view information, but the underlying
1126+ * TerrainInformationGl4 instance is automatically shared between different
1127+ * views when possible.
1128+ */
1129+
1130+GameRendererGl4::GameRendererGl4() {
1131+}
1132+
1133+GameRendererGl4::~GameRendererGl4() {
1134+}
1135+
1136+bool GameRendererGl4::supported() {
1137+ return TerrainProgramGl4::supported();
1138+}
1139+
1140+void GameRendererGl4::draw(const EditorGameBase& egbase,
1141+ const Vector2f& view_offset,
1142+ const float zoom,
1143+ const TextToDraw draw_text,
1144+ const Player* player,
1145+ RenderTarget* dst) {
1146+ Surface* surface = dst->get_surface();
1147+ if (!surface)
1148+ return;
1149+
1150+ // Upload map changes.
1151+ if (!args_.terrain || &args_.terrain->egbase() != &egbase ||
1152+ args_.terrain->player() != player)
1153+ args_.terrain = TerrainInformationGl4::get(egbase, player);
1154+
1155+ // Determine the set of patches to draw.
1156+ float scale = 1.f / zoom;
1157+ Vector2f tl_map = view_offset - dst->get_offset().cast<float>() * zoom;
1158+
1159+ assert(tl_map.x >= 0); // divisions involving negative numbers are bad
1160+ assert(tl_map.y >= 0);
1161+
1162+ args_.minfx = tl_map.x / kTriangleWidth - 1;
1163+ args_.minfy = tl_map.y / kTriangleHeight - 1;
1164+ args_.maxfx = (tl_map.x + dst->get_rect().w * zoom + (kTriangleWidth / 2)) / kTriangleWidth;
1165+ args_.maxfy = (tl_map.y + dst->get_rect().h * zoom) / kTriangleHeight;
1166+
1167+ // Fudge for triangle boundary effects, for height differences, and for
1168+ // large immovables.
1169+ args_.minfx -= 1;
1170+ args_.minfy -= 1;
1171+ args_.maxfx += 3;
1172+ args_.maxfy += 10;
1173+
1174+ const Recti& bounding_rect = dst->get_rect();
1175+ const uint32_t gametime = egbase.get_gametime();
1176+
1177+ args_.scale = scale;
1178+ args_.surface_offset = (bounding_rect.origin() + dst->get_offset()).cast<float>() * zoom - view_offset;
1179+ args_.surface_width = surface->width();
1180+ args_.surface_height = surface->height();
1181+
1182+ scan_fields(view_offset);
1183+
1184+ args_.terrain->update(args_.minfx, args_.maxfx, args_.minfy, args_.maxfy);
1185+
1186+ // Enqueue the drawing of the terrain.
1187+ RenderQueue::Item i;
1188+ i.program_id = RenderQueue::Program::kTerrainGl4;
1189+ i.blend_mode = BlendMode::Copy;
1190+ i.terrain_arguments.destination_rect =
1191+ Rectf(bounding_rect.x, args_.surface_height - bounding_rect.y - bounding_rect.h,
1192+ bounding_rect.w, bounding_rect.h);
1193+ i.terrain_arguments.gametime = gametime;
1194+ i.terrain_arguments.renderbuffer_width = args_.surface_width;
1195+ i.terrain_arguments.renderbuffer_height = args_.surface_height;
1196+ i.terrain_gl4_arguments = &args_;
1197+ RenderQueue::instance().enqueue(i);
1198+
1199+ if (!args_.roads.empty()) {
1200+ i.program_id = RenderQueue::Program::kTerrainRoadGl4;
1201+ i.blend_mode = BlendMode::UseAlpha;
1202+ RenderQueue::instance().enqueue(i);
1203+ }
1204+
1205+ draw_objects(egbase, scale, fields_to_draw_, player, draw_text, dst);
1206+}
1207+
1208+void GameRendererGl4::scan_fields(const Vector2f& view_offset) {
1209+ const EditorGameBase& egbase = args_.terrain->egbase();
1210+ const Player* player = args_.terrain->player();
1211+ auto& map = egbase.map();
1212+ const EdgeOverlayManager& edge_overlay_manager = egbase.get_ibase()->edge_overlay_manager();
1213+
1214+ args_.roads.clear();
1215+ fields_to_draw_.reset(args_.minfx, args_.maxfx, args_.minfy, args_.maxfy);
1216+
1217+ for (auto cursor = fields_to_draw_.cursor(); cursor.valid(); cursor.next()) {
1218+ FieldToDrawBase& f = cursor.mutable_field();
1219+
1220+ Vector2f map_pixel =
1221+ MapviewPixelFunctions::to_map_pixel_ignoring_height(cursor.geometric_coords());
1222+
1223+ Coords normalized = cursor.geometric_coords();
1224+ map.normalize_coords(normalized);
1225+ f.fcoords = map.get_fcoords(normalized);
1226+
1227+ map_pixel.y -= f.fcoords.field->get_height() * kHeightFactor;
1228+
1229+ f.rendertarget_pixel = MapviewPixelFunctions::map_to_panel(view_offset, 1. / args_.scale, map_pixel);
1230+
1231+ PlayerNumber owned_by = f.fcoords.field->get_owned_by();
1232+ f.owner = owned_by != 0 ? &egbase.player(owned_by) : nullptr;
1233+ f.is_border = f.fcoords.field->is_border();
1234+ f.vision = 2;
1235+ if (player && !player->see_all()) {
1236+ const Player::Field& pf = player->fields()[map.get_index(f.fcoords, map.get_width())];
1237+ f.vision = pf.vision;
1238+ if (pf.vision == 1) {
1239+ f.owner = pf.owner != 0 ? &egbase.player(owned_by) : nullptr;
1240+ f.is_border = pf.border;
1241+ }
1242+ }
1243+
1244+ uint8_t roads;
1245+ if (!player || player->see_all()) {
1246+ roads = f.fcoords.field->get_roads();
1247+ } else {
1248+ const Player::Field& pf = player->fields()[map.get_index(f.fcoords, map.get_width())];
1249+ roads = pf.roads;
1250+ }
1251+ if (player)
1252+ roads |= edge_overlay_manager.get_overlay(f.fcoords);
1253+
1254+ uint8_t type = (roads >> RoadType::kEast) & RoadType::kMask;
1255+ if (type) {
1256+ args_.roads.emplace_back(cursor.geometric_coords(), type,
1257+ WalkingDir::WALK_E, f.fcoords.field->get_owned_by());
1258+ }
1259+
1260+ type = (roads >> RoadType::kSouthEast) & RoadType::kMask;
1261+ if (type) {
1262+ args_.roads.emplace_back(cursor.geometric_coords(), type,
1263+ WalkingDir::WALK_SE, f.fcoords.field->get_owned_by());
1264+ }
1265+
1266+ type = (roads >> RoadType::kSouthWest) & RoadType::kMask;
1267+ if (type) {
1268+ args_.roads.emplace_back(cursor.geometric_coords(), type,
1269+ WalkingDir::WALK_SW, f.fcoords.field->get_owned_by());
1270+ }
1271+ }
1272+}
1273
1274=== added file 'src/graphic/game_renderer_gl4.h'
1275--- src/graphic/game_renderer_gl4.h 1970-01-01 00:00:00 +0000
1276+++ src/graphic/game_renderer_gl4.h 2017-01-07 12:36:16 +0000
1277@@ -0,0 +1,92 @@
1278+/*
1279+ * Copyright (C) 2010-2016 by the Widelands Development Team
1280+ *
1281+ * This program is free software; you can redistribute it and/or
1282+ * modify it under the terms of the GNU General Public License
1283+ * as published by the Free Software Foundation; either version 2
1284+ * of the License, or (at your option) any later version.
1285+ *
1286+ * This program is distributed in the hope that it will be useful,
1287+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1288+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1289+ * GNU General Public License for more details.
1290+ *
1291+ * You should have received a copy of the GNU General Public License
1292+ * along with this program; if not, write to the Free Software
1293+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1294+ *
1295+ */
1296+
1297+#ifndef WL_GRAPHIC_GAME_RENDERER_GL4_H
1298+#define WL_GRAPHIC_GAME_RENDERER_GL4_H
1299+
1300+#include <vector>
1301+
1302+#include "graphic/game_renderer.h"
1303+#include "graphic/minimap_layer.h"
1304+#include "logic/widelands_geometry.h"
1305+
1306+class TerrainInformationGl4;
1307+
1308+/**
1309+ * This structure is used for the @ref RenderQueue by terrain rendering, road
1310+ * rendering, and minimap rendering. Each use only uses a subset of memers.
1311+ */
1312+struct TerrainGl4Arguments {
1313+ struct Road {
1314+ Widelands::Coords coord;
1315+
1316+ // One of the RoadTypes
1317+ uint8_t type;
1318+
1319+ // One of the WalkingDirs
1320+ uint8_t direction;
1321+
1322+ // Player number
1323+ uint8_t owner;
1324+
1325+ Road(Widelands::Coords coord_, uint8_t type_, uint8_t direction_, uint8_t owner_)
1326+ : coord(coord_), type(type_), direction(direction_), owner(owner_) {
1327+ }
1328+ };
1329+ static_assert(sizeof(Road) == 8, "bad alignment");
1330+
1331+ std::shared_ptr<TerrainInformationGl4> terrain;
1332+ float scale;
1333+ Vector2f surface_offset;
1334+ int surface_width;
1335+ int surface_height;
1336+ int minfx, minfy, maxfx, maxfy;
1337+ std::vector<Road> roads;
1338+
1339+ int minimap_tl_fx, minimap_tl_fy;
1340+ MiniMapLayer minimap_layers;
1341+};
1342+
1343+/**
1344+ * This is the front-end of the GL4 rendering path game renderer.
1345+ */
1346+class GameRendererGl4 : public GameRenderer {
1347+public:
1348+ GameRendererGl4();
1349+ virtual ~GameRendererGl4();
1350+
1351+ static bool supported();
1352+
1353+ void draw(const Widelands::EditorGameBase& egbase,
1354+ const Vector2f& view_offset,
1355+ const float zoom,
1356+ const TextToDraw draw_text,
1357+ const Widelands::Player* player,
1358+ RenderTarget* dst) override;
1359+
1360+private:
1361+ void scan_fields(const Vector2f& view_offset);
1362+
1363+ TerrainGl4Arguments args_;
1364+ FieldsToDrawBase fields_to_draw_;
1365+
1366+ DISALLOW_COPY_AND_ASSIGN(GameRendererGl4);
1367+};
1368+
1369+#endif // end of include guard: WL_GRAPHIC_GAME_RENDERER_H
1370
1371=== modified file 'src/graphic/gl/blit_program.cc'
1372--- src/graphic/gl/blit_program.cc 2016-12-03 13:32:28 +0000
1373+++ src/graphic/gl/blit_program.cc 2017-01-07 12:36:16 +0000
1374@@ -19,13 +19,16 @@
1375
1376 #include "graphic/gl/blit_program.h"
1377
1378+#include <memory>
1379 #include <vector>
1380
1381 #include "base/log.h"
1382 #include "graphic/blit_mode.h"
1383 #include "graphic/gl/blit_data.h"
1384 #include "graphic/gl/coordinate_conversion.h"
1385+#include "graphic/gl/streaming_buffer.h"
1386 #include "graphic/gl/utils.h"
1387+#include "profile/profile.h"
1388
1389 namespace {
1390
1391@@ -41,9 +44,120 @@
1392 BlendMode blend_mode;
1393 };
1394
1395+class BlitProgramGl2 : public BlitProgram {
1396+public:
1397+ BlitProgramGl2();
1398+
1399+ void draw(const std::vector<Arguments>& arguments) override;
1400+
1401+private:
1402+ struct PerVertexData {
1403+ PerVertexData(float init_gl_x,
1404+ float init_gl_y,
1405+ float init_gl_z,
1406+ float init_texture_x,
1407+ float init_texture_y,
1408+ float init_mask_texture_x,
1409+ float init_mask_texture_y,
1410+ float init_blend_r,
1411+ float init_blend_g,
1412+ float init_blend_b,
1413+ float init_blend_a,
1414+ float init_program_flavor)
1415+ : gl_x(init_gl_x),
1416+ gl_y(init_gl_y),
1417+ gl_z(init_gl_z),
1418+ texture_x(init_texture_x),
1419+ texture_y(init_texture_y),
1420+ mask_texture_x(init_mask_texture_x),
1421+ mask_texture_y(init_mask_texture_y),
1422+ blend_r(init_blend_r),
1423+ blend_g(init_blend_g),
1424+ blend_b(init_blend_b),
1425+ blend_a(init_blend_a),
1426+ program_flavor(init_program_flavor) {
1427+ }
1428+
1429+ float gl_x, gl_y, gl_z;
1430+ float texture_x, texture_y;
1431+ float mask_texture_x, mask_texture_y;
1432+ float blend_r, blend_g, blend_b, blend_a;
1433+ float program_flavor;
1434+ };
1435+ static_assert(sizeof(PerVertexData) == 48, "Wrong padding.");
1436+
1437+ // The buffer that will contain the quad for rendering.
1438+ Gl::Buffer<PerVertexData> gl_array_buffer_;
1439+
1440+ // The program.
1441+ Gl::Program gl_program_;
1442+
1443+ // Attributes.
1444+ GLint attr_blend_;
1445+ GLint attr_mask_texture_position_;
1446+ GLint attr_position_;
1447+ GLint attr_texture_position_;
1448+ GLint attr_program_flavor_;
1449+
1450+ // Uniforms.
1451+ GLint u_texture_;
1452+ GLint u_mask_;
1453+
1454+ // Cached for efficiency.
1455+ std::vector<PerVertexData> vertices_;
1456+};
1457+
1458+class BlitProgramGl4 : public BlitProgram {
1459+public:
1460+ BlitProgramGl4();
1461+
1462+ static bool supported();
1463+
1464+ void draw(const std::vector<Arguments>& arguments) override;
1465+
1466+private:
1467+ struct PerRectData {
1468+ float dst_x, dst_y, dst_width, dst_height;
1469+ uint32_t src_x, src_y, src_width, src_height;
1470+ uint32_t src_parent_width, src_parent_height;
1471+ uint32_t mask_x, mask_y, mask_width, mask_height;
1472+ uint32_t mask_parent_width, mask_parent_height;
1473+ uint32_t blend_r, blend_g, blend_b, blend_a;
1474+ float program_flavor, z;
1475+
1476+ // Standard OpenGL packing aligns arrays to a multiple of 16 bytes.
1477+ float padding[2];
1478+ };
1479+ static_assert(sizeof(PerRectData) == 96, "Wrong padding.");
1480+
1481+ void setup_index_buffer(unsigned num_rects);
1482+
1483+ // The index buffer.
1484+ Gl::Buffer<uint16_t> gl_index_buffer_;
1485+ unsigned num_index_rects_;
1486+
1487+ // The per-rect data buffer.
1488+ Gl::StreamingBuffer<PerRectData> gl_rects_buffer_;
1489+
1490+ // The program.
1491+ Gl::Program gl_program_;
1492+
1493+ // Uniform locations.
1494+ GLint u_texture_;
1495+ GLint u_mask_;
1496+};
1497+
1498 } // namespace
1499
1500 BlitProgram::BlitProgram() {
1501+}
1502+
1503+BlitProgram::~BlitProgram() {
1504+}
1505+
1506+BlitProgramGl2::BlitProgramGl2() {
1507+ log("Using GL2 blit path\n");
1508+
1509 gl_program_.build("blit");
1510
1511 attr_blend_ = glGetAttribLocation(gl_program_.object(), "attr_blend");
1512@@ -57,10 +171,7 @@
1513 u_mask_ = glGetUniformLocation(gl_program_.object(), "u_mask");
1514 }
1515
1516-BlitProgram::~BlitProgram() {
1517-}
1518-
1519-void BlitProgram::draw(const std::vector<Arguments>& arguments) {
1520+void BlitProgramGl2::draw(const std::vector<Arguments>& arguments) {
1521 glUseProgram(gl_program_.object());
1522
1523 auto& gl_state = Gl::State::instance();
1524@@ -176,6 +287,165 @@
1525 }
1526 }
1527
1528+BlitProgramGl4::BlitProgramGl4()
1529+ : gl_rects_buffer_(GL_ARRAY_BUFFER) {
1530+ log("Using GL4 blit path\n");
1531+
1532+ gl_program_.build_vp_fp({"blit_gl4"}, {"blit"});
1533+
1534+ u_texture_ = glGetUniformLocation(gl_program_.object(), "u_texture");
1535+ u_mask_ = glGetUniformLocation(gl_program_.object(), "u_mask");
1536+
1537+ num_index_rects_ = 0;
1538+}
1539+
1540+bool BlitProgramGl4::supported() {
1541+ const auto& caps = Gl::State::instance().capabilities();
1542+
1543+ if (caps.glsl_version < 130)
1544+ return false;
1545+
1546+ if (!caps.ARB_separate_shader_objects ||
1547+ !caps.ARB_shader_storage_buffer_object ||
1548+ !caps.ARB_uniform_buffer_object)
1549+ return false;
1550+
1551+ return !g_options.pull_section("global").get_bool("disable_gl4", false);
1552+}
1553+
1554+void BlitProgramGl4::draw(const std::vector<Arguments>& arguments) {
1555+ glUseProgram(gl_program_.object());
1556+
1557+ auto& gl_state = Gl::State::instance();
1558+
1559+ gl_state.enable_vertex_attrib_array({});
1560+
1561+ glUniform1i(u_texture_, 0);
1562+ glUniform1i(u_mask_, 1);
1563+
1564+ // Prepare the buffer for many draw calls.
1565+ std::vector<DrawBatch> draw_batches;
1566+ auto rects = gl_rects_buffer_.stream(arguments.size());
1567+
1568+ size_t i = 0;
1569+ while (i < arguments.size()) {
1570+ const auto& template_args = arguments[i];
1571+ const int start = i;
1572+
1573+ // Batch common blit operations up.
1574+ while (i < arguments.size()) {
1575+ const auto& current_args = arguments[i];
1576+ if (current_args.blend_mode != template_args.blend_mode ||
1577+ current_args.texture.texture_id != template_args.texture.texture_id ||
1578+ (current_args.mask.texture_id != 0 &&
1579+ current_args.mask.texture_id != template_args.mask.texture_id)) {
1580+ break;
1581+ }
1582+
1583+ rects.emplace_back();
1584+ auto& rect = rects.back();
1585+ rect.dst_x = current_args.destination_rect.x;
1586+ rect.dst_y = current_args.destination_rect.y;
1587+ rect.dst_width = current_args.destination_rect.w;
1588+ rect.dst_height = current_args.destination_rect.h;
1589+
1590+ rect.src_x = current_args.texture.rect.x;
1591+ rect.src_y = current_args.texture.rect.y;
1592+ rect.src_width = current_args.texture.rect.w;
1593+ rect.src_height = current_args.texture.rect.h;
1594+ rect.src_parent_width = current_args.texture.parent_width;
1595+ rect.src_parent_height = current_args.texture.parent_height;
1596+
1597+ rect.mask_x = current_args.mask.rect.x;
1598+ rect.mask_y = current_args.mask.rect.y;
1599+ rect.mask_width = current_args.mask.rect.w;
1600+ rect.mask_height = current_args.mask.rect.h;
1601+ rect.mask_parent_width = current_args.mask.parent_width;
1602+ rect.mask_parent_height = current_args.mask.parent_height;
1603+
1604+ rect.blend_r = current_args.blend.r;
1605+ rect.blend_g = current_args.blend.g;
1606+ rect.blend_b = current_args.blend.b;
1607+ rect.blend_a = current_args.blend.a;
1608+
1609+ switch (current_args.blit_mode) {
1610+ case BlitMode::kDirect:
1611+ rect.program_flavor = 0.;
1612+ break;
1613+
1614+ case BlitMode::kMonochrome:
1615+ rect.program_flavor = 1.;
1616+ break;
1617+
1618+ case BlitMode::kBlendedWithMask:
1619+ rect.program_flavor = 2.;
1620+ break;
1621+ }
1622+
1623+ rect.z = current_args.z_value;
1624+
1625+ ++i;
1626+ }
1627+
1628+ draw_batches.emplace_back(DrawBatch{int(start), int(i - start),
1629+ template_args.texture.texture_id,
1630+ template_args.mask.texture_id, template_args.blend_mode});
1631+ }
1632+
1633+ glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, gl_rects_buffer_.object(),
1634+ rects.unmap(), i * sizeof(PerRectData));
1635+
1636+ setup_index_buffer(i);
1637+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl_index_buffer_.object());
1638+
1639+ // Now do the draw calls.
1640+ for (const auto& draw_arg : draw_batches) {
1641+ gl_state.bind(GL_TEXTURE0, draw_arg.texture);
1642+ gl_state.bind(GL_TEXTURE1, draw_arg.mask);
1643+
1644+ if (draw_arg.blend_mode == BlendMode::Copy) {
1645+ glBlendFunc(GL_ONE, GL_ZERO);
1646+ }
1647+
1648+ glDrawElements(GL_TRIANGLES, 6 * draw_arg.count, GL_UNSIGNED_SHORT,
1649+ static_cast<uint16_t*>(nullptr) + 6 * draw_arg.offset);
1650+
1651+ if (draw_arg.blend_mode == BlendMode::Copy) {
1652+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1653+ }
1654+ }
1655+
1656+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1657+}
1658+
1659+void BlitProgramGl4::setup_index_buffer(unsigned num_rects)
1660+{
1661+ if (num_rects <= num_index_rects_)
1662+ return;
1663+
1664+ if (num_rects > 65536 / 4)
1665+ throw wexception("Too many rectangles for 16-bit indices");
1666+
1667+ std::vector<uint16_t> indices;
1668+ indices.reserve(num_rects * 6);
1669+
1670+ for (unsigned i = 0; i < num_rects; ++i) {
1671+ indices.push_back(4 * i);
1672+ indices.push_back(4 * i + 1);
1673+ indices.push_back(4 * i + 2);
1674+
1675+ indices.push_back(4 * i + 2);
1676+ indices.push_back(4 * i + 1);
1677+ indices.push_back(4 * i + 3);
1678+ }
1679+
1680+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl_index_buffer_.object());
1681+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint16_t) * indices.size(),
1682+ indices.data(), GL_STATIC_DRAW);
1683+ num_index_rects_ = num_rects;
1684+}
1685+
1686+
1687 void BlitProgram::draw(const Rectf& gl_dest_rect,
1688 const float z_value,
1689 const BlitData& texture,
1690@@ -194,8 +464,29 @@
1691 BlendMode::UseAlpha, BlitMode::kMonochrome}});
1692 }
1693
1694+namespace {
1695+
1696+class BlitProgramHolder {
1697+public:
1698+ BlitProgramHolder() {
1699+ if (BlitProgramGl4::supported())
1700+ program_.reset(new BlitProgramGl4);
1701+ else
1702+ program_.reset(new BlitProgramGl2);
1703+ }
1704+
1705+ BlitProgram& program() {
1706+ return *program_;
1707+ }
1708+
1709+private:
1710+ std::unique_ptr<BlitProgram> program_;
1711+};
1712+
1713+} // namespace
1714+
1715 // static
1716 BlitProgram& BlitProgram::instance() {
1717- static BlitProgram blit_program;
1718- return blit_program;
1719+ static BlitProgramHolder holder;
1720+ return holder.program();
1721 }
1722
1723=== modified file 'src/graphic/gl/blit_program.h'
1724--- src/graphic/gl/blit_program.h 2016-10-16 09:50:48 +0000
1725+++ src/graphic/gl/blit_program.h 2017-01-07 12:36:16 +0000
1726@@ -47,7 +47,7 @@
1727
1728 // Returns the (singleton) instance of this class.
1729 static BlitProgram& instance();
1730- ~BlitProgram();
1731+ virtual ~BlitProgram();
1732
1733 // Draws the rectangle 'gl_src_rect' from the texture with the name
1734 // 'gl_texture_image' to 'gl_dest_rect' in the currently bound framebuffer. All
1735@@ -70,66 +70,12 @@
1736 const RGBAColor& blend);
1737
1738 // Draws a bunch of items at once.
1739- void draw(const std::vector<Arguments>& arguments);
1740+ virtual void draw(const std::vector<Arguments>& arguments) = 0;
1741+
1742+protected:
1743+ BlitProgram();
1744
1745 private:
1746- BlitProgram();
1747-
1748- struct PerVertexData {
1749- PerVertexData(float init_gl_x,
1750- float init_gl_y,
1751- float init_gl_z,
1752- float init_texture_x,
1753- float init_texture_y,
1754- float init_mask_texture_x,
1755- float init_mask_texture_y,
1756- float init_blend_r,
1757- float init_blend_g,
1758- float init_blend_b,
1759- float init_blend_a,
1760- float init_program_flavor)
1761- : gl_x(init_gl_x),
1762- gl_y(init_gl_y),
1763- gl_z(init_gl_z),
1764- texture_x(init_texture_x),
1765- texture_y(init_texture_y),
1766- mask_texture_x(init_mask_texture_x),
1767- mask_texture_y(init_mask_texture_y),
1768- blend_r(init_blend_r),
1769- blend_g(init_blend_g),
1770- blend_b(init_blend_b),
1771- blend_a(init_blend_a),
1772- program_flavor(init_program_flavor) {
1773- }
1774-
1775- float gl_x, gl_y, gl_z;
1776- float texture_x, texture_y;
1777- float mask_texture_x, mask_texture_y;
1778- float blend_r, blend_g, blend_b, blend_a;
1779- float program_flavor;
1780- };
1781- static_assert(sizeof(PerVertexData) == 48, "Wrong padding.");
1782-
1783- // The buffer that will contain the quad for rendering.
1784- Gl::Buffer<PerVertexData> gl_array_buffer_;
1785-
1786- // The program.
1787- Gl::Program gl_program_;
1788-
1789- // Attributes.
1790- GLint attr_blend_;
1791- GLint attr_mask_texture_position_;
1792- GLint attr_position_;
1793- GLint attr_texture_position_;
1794- GLint attr_program_flavor_;
1795-
1796- // Uniforms.
1797- GLint u_texture_;
1798- GLint u_mask_;
1799-
1800- // Cached for efficiency.
1801- std::vector<PerVertexData> vertices_;
1802-
1803 DISALLOW_COPY_AND_ASSIGN(BlitProgram);
1804 };
1805
1806
1807=== modified file 'src/graphic/gl/dither_program.cc'
1808--- src/graphic/gl/dither_program.cc 2016-12-03 13:32:28 +0000
1809+++ src/graphic/gl/dither_program.cc 2017-01-07 12:36:16 +0000
1810@@ -54,7 +54,7 @@
1811 DitherProgram::~DitherProgram() {
1812 }
1813
1814-void DitherProgram::add_vertex(const FieldsToDraw::Field& field,
1815+void DitherProgram::add_vertex(const FieldToDrawGl2& field,
1816 const TrianglePoint triangle_point,
1817 const Vector2f& texture_offset) {
1818 vertices_.emplace_back();
1819@@ -87,10 +87,9 @@
1820 void DitherProgram::maybe_add_dithering_triangle(
1821 const uint32_t gametime,
1822 const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
1823- const FieldsToDraw& fields_to_draw,
1824- const int idx1,
1825- const int idx2,
1826- const int idx3,
1827+ const FieldToDrawGl2& f1,
1828+ const FieldToDrawGl2& f2,
1829+ const FieldToDrawGl2& f3,
1830 const int my_terrain,
1831 const int other_terrain) {
1832 if (my_terrain == other_terrain) {
1833@@ -100,9 +99,9 @@
1834 if (terrains.get(my_terrain).dither_layer() < other_terrain_description.dither_layer()) {
1835 const Vector2f texture_offset =
1836 to_gl_texture(other_terrain_description.get_texture(gametime).blit_data()).origin();
1837- add_vertex(fields_to_draw.at(idx1), TrianglePoint::kTopRight, texture_offset);
1838- add_vertex(fields_to_draw.at(idx2), TrianglePoint::kTopLeft, texture_offset);
1839- add_vertex(fields_to_draw.at(idx3), TrianglePoint::kBottomMiddle, texture_offset);
1840+ add_vertex(f1, TrianglePoint::kTopRight, texture_offset);
1841+ add_vertex(f2, TrianglePoint::kTopLeft, texture_offset);
1842+ add_vertex(f3, TrianglePoint::kBottomMiddle, texture_offset);
1843 }
1844 }
1845
1846@@ -141,7 +140,7 @@
1847
1848 void DitherProgram::draw(const uint32_t gametime,
1849 const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
1850- const FieldsToDraw& fields_to_draw,
1851+ const FieldsToDrawGl2& fields_to_draw,
1852 const float z_value) {
1853 // This method expects that all terrains have the same dimensions and that
1854 // all are packed into the same texture atlas, i.e. all are in the same GL
1855@@ -150,49 +149,49 @@
1856 vertices_.clear();
1857 vertices_.reserve(fields_to_draw.size() * 3);
1858
1859- for (size_t current_index = 0; current_index < fields_to_draw.size(); ++current_index) {
1860- const FieldsToDraw::Field& field = fields_to_draw.at(current_index);
1861+ for (auto cursor = fields_to_draw.cursor(); cursor.valid(); cursor.next()) {
1862+ const FieldToDrawGl2& field = cursor.field();
1863
1864 // The bottom right neighbor fields_to_draw is needed for both triangles
1865 // associated with this field. If it is not in fields_to_draw, there is no need to
1866 // draw any triangles.
1867- if (field.brn_index == FieldsToDraw::kInvalidIndex) {
1868+ if (!cursor.brn_valid()) {
1869 continue;
1870 }
1871
1872 // Dithering triangles for Down triangle.
1873- if (field.bln_index != FieldsToDraw::kInvalidIndex) {
1874+ if (cursor.bln_valid()) {
1875 maybe_add_dithering_triangle(
1876- gametime, terrains, fields_to_draw, field.brn_index, current_index, field.bln_index,
1877+ gametime, terrains, cursor.brn(), cursor.field(), cursor.bln(),
1878 field.fcoords.field->terrain_d(), field.fcoords.field->terrain_r());
1879
1880- const int terrain_dd = fields_to_draw.at(field.bln_index).fcoords.field->terrain_r();
1881- maybe_add_dithering_triangle(gametime, terrains, fields_to_draw, field.bln_index,
1882- field.brn_index, current_index,
1883+ const int terrain_dd = cursor.bln().fcoords.field->terrain_r();
1884+ maybe_add_dithering_triangle(gametime, terrains, cursor.bln(),
1885+ cursor.brn(), cursor.field(),
1886 field.fcoords.field->terrain_d(), terrain_dd);
1887
1888- if (field.ln_index != FieldsToDraw::kInvalidIndex) {
1889- const int terrain_l = fields_to_draw.at(field.ln_index).fcoords.field->terrain_r();
1890- maybe_add_dithering_triangle(gametime, terrains, fields_to_draw, current_index,
1891- field.bln_index, field.brn_index,
1892+ if (cursor.ln_valid()) {
1893+ const int terrain_l = cursor.ln().fcoords.field->terrain_r();
1894+ maybe_add_dithering_triangle(gametime, terrains, cursor.field(),
1895+ cursor.bln(), cursor.brn(),
1896 field.fcoords.field->terrain_d(), terrain_l);
1897 }
1898 }
1899
1900 // Dithering for right triangle.
1901- if (field.rn_index != FieldsToDraw::kInvalidIndex) {
1902+ if (cursor.rn_valid()) {
1903 maybe_add_dithering_triangle(
1904- gametime, terrains, fields_to_draw, current_index, field.brn_index, field.rn_index,
1905+ gametime, terrains, cursor.field(), cursor.brn(), cursor.rn(),
1906 field.fcoords.field->terrain_r(), field.fcoords.field->terrain_d());
1907- int terrain_rr = fields_to_draw.at(field.rn_index).fcoords.field->terrain_d();
1908- maybe_add_dithering_triangle(gametime, terrains, fields_to_draw, field.brn_index,
1909- field.rn_index, current_index,
1910+ int terrain_rr = cursor.rn().fcoords.field->terrain_d();
1911+ maybe_add_dithering_triangle(gametime, terrains, cursor.brn(),
1912+ cursor.rn(), cursor.field(),
1913 field.fcoords.field->terrain_r(), terrain_rr);
1914
1915- if (field.trn_index != FieldsToDraw::kInvalidIndex) {
1916- const int terrain_u = fields_to_draw.at(field.trn_index).fcoords.field->terrain_d();
1917- maybe_add_dithering_triangle(gametime, terrains, fields_to_draw, field.rn_index,
1918- current_index, field.brn_index,
1919+ if (cursor.trn_valid()) {
1920+ const int terrain_u = cursor.trn().fcoords.field->terrain_d();
1921+ maybe_add_dithering_triangle(gametime, terrains, cursor.rn(),
1922+ cursor.field(), cursor.brn(),
1923 field.fcoords.field->terrain_r(), terrain_u);
1924 }
1925 }
1926
1927=== modified file 'src/graphic/gl/dither_program.h'
1928--- src/graphic/gl/dither_program.h 2016-10-16 09:31:42 +0000
1929+++ src/graphic/gl/dither_program.h 2017-01-07 12:36:16 +0000
1930@@ -38,7 +38,7 @@
1931 // Draws the terrain.
1932 void draw(uint32_t gametime,
1933 const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
1934- const FieldsToDraw& fields_to_draw,
1935+ const FieldsToDrawGl2& fields_to_draw,
1936 float z_value);
1937
1938 private:
1939@@ -54,17 +54,16 @@
1940 void maybe_add_dithering_triangle(
1941 uint32_t gametime,
1942 const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
1943- const FieldsToDraw& fields_to_draw,
1944- int idx1,
1945- int idx2,
1946- int idx3,
1947+ const FieldToDrawGl2& f1,
1948+ const FieldToDrawGl2& f2,
1949+ const FieldToDrawGl2& f3,
1950 int my_terrain,
1951 int other_terrain);
1952
1953 // Adds the 'field' as an vertex to the 'vertices_'. The 'order_index'
1954 // defines which texture position in the dithering texture will be used for
1955 // this vertex.
1956- void add_vertex(const FieldsToDraw::Field& field,
1957+ void add_vertex(const FieldToDrawGl2& field,
1958 TrianglePoint triangle_point,
1959 const Vector2f& texture_offset);
1960
1961
1962=== modified file 'src/graphic/gl/fields_to_draw.h'
1963--- src/graphic/gl/fields_to_draw.h 2016-10-24 20:07:22 +0000
1964+++ src/graphic/gl/fields_to_draw.h 2017-01-07 12:36:16 +0000
1965@@ -33,51 +33,47 @@
1966 #include "logic/widelands.h"
1967 #include "logic/widelands_geometry.h"
1968
1969+template<typename FTD>
1970+class FieldsToDrawCursor;
1971+
1972+template<typename F>
1973+class FieldsToDrawRef;
1974+
1975+struct FieldToDrawBase {
1976+ Widelands::FCoords fcoords; // The normalized coords and the field this is refering to.
1977+ Vector2f rendertarget_pixel;
1978+
1979+ // The next values are not necessarily the true data of this field, but
1980+ // what the player should see. For example in fog of war we always draw
1981+ // what we saw last.
1982+ //
1983+ // Note: we put the roads member variable here for better alignment even
1984+ // though it isn't needed in the Gl4 path.
1985+ Widelands::Player* owner; // can be nullptr.
1986+ uint8_t roads; // Bitmask of roads to render, see logic/roadtype.h.
1987+ bool is_border;
1988+ Widelands::Vision vision;
1989+};
1990+
1991+struct FieldToDrawGl2 : FieldToDrawBase {
1992+ Vector2f gl_position; // GL Position of this field.
1993+
1994+ // Surface pixel this will be plotted on.
1995+ Vector2f surface_pixel;
1996+
1997+ // Rendertarget pixel this will be plotted on. This is only different by
1998+ // the Rendertarget::get_rect().origin() of the view window.
1999+ Vector2f texture_coords; // Texture coordinates.
2000+ float brightness; // brightness of the pixel
2001+};
2002+
2003 // Helper struct that contains the data needed for drawing all fields. All
2004 // methods are inlined for performance reasons.
2005-class FieldsToDraw {
2006+class FieldsToDrawImplBase {
2007 public:
2008 static constexpr int kInvalidIndex = std::numeric_limits<int>::min();
2009
2010- struct Field {
2011- Widelands::Coords geometric_coords; // geometric coordinates (i.e. map coordinates that can
2012- // be out of bounds).
2013- Widelands::FCoords fcoords; // The normalized coords and the field this is refering to.
2014- Vector2f gl_position; // GL Position of this field.
2015-
2016- // Surface pixel this will be plotted on.
2017- Vector2f surface_pixel;
2018-
2019- // Rendertarget pixel this will be plotted on. This is only different by
2020- // the Rendertarget::get_rect().origin() of the view window.
2021- Vector2f rendertarget_pixel;
2022- Vector2f texture_coords; // Texture coordinates.
2023- float brightness; // brightness of the pixel
2024-
2025- // The next values are not necessarily the true data of this field, but
2026- // what the player should see. For example in fog of war we always draw
2027- // what we saw last.
2028- uint8_t roads; // Bitmask of roads to render, see logic/roadtype.h.
2029- bool is_border;
2030- Widelands::Vision vision;
2031- Widelands::Player* owner; // can be nullptr.
2032-
2033- // Index of neighbors in this 'FieldsToDraw'. kInvalidIndex if this
2034- // neighbor is not contained.
2035- int ln_index;
2036- int rn_index;
2037- int trn_index;
2038- int bln_index;
2039- int brn_index;
2040-
2041- inline bool all_neighbors_valid() const {
2042- return ln_index != kInvalidIndex && rn_index != kInvalidIndex &&
2043- trn_index != kInvalidIndex && bln_index != kInvalidIndex &&
2044- brn_index != kInvalidIndex;
2045- }
2046- };
2047-
2048- FieldsToDraw() {
2049+ FieldsToDrawImplBase() {
2050 }
2051
2052 // Resize this fields to draw for reuse.
2053@@ -87,55 +83,277 @@
2054 min_fy_ = minfy;
2055 max_fy_ = maxfy;
2056 w_ = max_fx_ - min_fx_ + 1;
2057- h_ = max_fy_ - min_fy_ + 1;
2058- const size_t dimension = w_ * h_;
2059- if (fields_.size() != dimension) {
2060- fields_.resize(dimension);
2061- }
2062- }
2063-
2064- // Calculates the index of the given field with ('fx', 'fy') being geometric
2065- // coordinates in the map. Returns kInvalidIndex if this field is not in the
2066- // fields_to_draw.
2067- inline int calculate_index(int fx, int fy) const {
2068- uint16_t xidx = fx - min_fx_;
2069- if (xidx >= w_) {
2070- return kInvalidIndex;
2071- }
2072- uint16_t yidx = fy - min_fy_;
2073- if (yidx >= h_) {
2074- return kInvalidIndex;
2075- }
2076- return yidx * w_ + xidx;
2077- }
2078-
2079- // The number of fields to draw.
2080- inline size_t size() const {
2081- return fields_.size();
2082- }
2083-
2084- // Get the field at 'index' which must be in bound.
2085- inline const Field& at(const int index) const {
2086- return fields_.at(index);
2087- }
2088-
2089- // Returns a mutable field at 'index' which must be in bound.
2090- inline Field* mutable_field(const int index) {
2091- return &fields_[index];
2092- }
2093-
2094-private:
2095+ }
2096+
2097+ int min_fx() const {
2098+ return min_fx_;
2099+ }
2100+
2101+ int max_fx() const {
2102+ return max_fx_;
2103+ }
2104+
2105+ int min_fy() const {
2106+ return min_fy_;
2107+ }
2108+
2109+ int max_fy() const {
2110+ return max_fy_;
2111+ }
2112+
2113+ int get_w() const {
2114+ return w_;
2115+ }
2116+
2117+protected:
2118 // Minimum and maximum field coordinates (geometric) to render. Can be negative.
2119 int min_fx_;
2120 int max_fx_;
2121 int min_fy_;
2122 int max_fy_;
2123
2124- // Width and height in number of fields.
2125+ // Width in number of fields.
2126 int w_;
2127- int h_;
2128+};
2129+
2130+// Helper struct that contains the data needed for drawing all fields. All
2131+// methods are inlined for performance reasons.
2132+template<typename Field_>
2133+class FieldsToDraw : public FieldsToDrawImplBase {
2134+public:
2135+ using Field = Field_;
2136+
2137+ FieldsToDraw() {
2138+ }
2139+
2140+ // Resize this fields to draw for reuse.
2141+ void reset(int minfx, int maxfx, int minfy, int maxfy) {
2142+ FieldsToDrawImplBase::reset(minfx, maxfx, minfy, maxfy);
2143+
2144+ int h = max_fy_ - min_fy_ + 1;
2145+ const size_t dimension = FieldsToDrawImplBase::get_w() * h;
2146+ if (fields_.size() != dimension) {
2147+ fields_.resize(dimension);
2148+ }
2149+ }
2150+
2151+ // The number of fields to draw.
2152+ inline size_t size() const {
2153+ return fields_.size();
2154+ }
2155+
2156+ Field& operator[](int index) {
2157+ return fields_[index];
2158+ }
2159+
2160+ const Field& operator[](int index) const {
2161+ return fields_[index];
2162+ }
2163+
2164+ FieldsToDrawCursor<FieldsToDraw> cursor();
2165+ FieldsToDrawCursor<const FieldsToDraw> cursor() const;
2166+
2167+private:
2168+ template<typename F>
2169+ friend class FieldsToDrawRef;
2170+
2171+ const Field* get_fields() const {
2172+ return &fields_[0];
2173+ }
2174+
2175+ intptr_t get_fields_stride() const {
2176+ return sizeof(fields_[0]);
2177+ }
2178
2179 std::vector<Field> fields_;
2180 };
2181
2182+template<typename Field_>
2183+class FieldsToDrawRef : public FieldsToDrawImplBase {
2184+public:
2185+ using Field = Field_;
2186+
2187+ template<typename FTD>
2188+ FieldsToDrawRef(const FTD& fields_to_draw) {
2189+ reset(fields_to_draw.min_fx(), fields_to_draw.max_fx(),
2190+ fields_to_draw.min_fy(), fields_to_draw.max_fy());
2191+
2192+ // First assign to type Field* for type-checking, before reinterpreting
2193+ // to the char* that will be used for pointer arithmetic.
2194+ const Field* fields = fields_to_draw.get_fields();
2195+
2196+ fields_ = reinterpret_cast<const char*>(fields);
2197+ stride_ = fields_to_draw.get_fields_stride();
2198+ }
2199+
2200+ const Field& operator[](int index) const {
2201+ const char* base = fields_ + index * stride_;
2202+ return *reinterpret_cast<const Field*>(base);
2203+ }
2204+
2205+ FieldsToDrawCursor<const FieldsToDrawRef> cursor() const;
2206+
2207+private:
2208+ const char* fields_;
2209+ intptr_t stride_;
2210+};
2211+
2212+using FieldsToDrawBase = FieldsToDraw<FieldToDrawBase>;
2213+using FieldsToDrawGl2 = FieldsToDraw<FieldToDrawGl2>;
2214+using FieldsToDrawRefBase = FieldsToDrawRef<FieldToDrawBase>;
2215+
2216+// For iteration over fields.
2217+//
2218+// Template for const-correctness.
2219+template<typename FTD>
2220+class FieldsToDrawCursor {
2221+public:
2222+ using Field = typename FTD::Field;
2223+
2224+ FieldsToDrawCursor(FTD& fields_to_draw)
2225+ : fields_(fields_to_draw) {
2226+ type_check(fields_to_draw);
2227+
2228+ geometric_coords_.x = fields_.min_fx();
2229+ geometric_coords_.y = fields_.min_fy();
2230+ geometric_tblx_shift_ = (fields_.min_fy() & 1) - 1;
2231+
2232+ index_ = 0;
2233+ }
2234+
2235+ bool valid() const {
2236+ return index_ >= 0;
2237+ }
2238+
2239+ void next() {
2240+ assert(valid());
2241+
2242+ index_++;
2243+ geometric_coords_.x++;
2244+ if (geometric_coords_.x > fields_.max_fx()) {
2245+ geometric_coords_.x = fields_.min_fx();
2246+ geometric_tblx_shift_ = -1 - geometric_tblx_shift_;
2247+ geometric_coords_.y++;
2248+ if (geometric_coords_.y > fields_.max_fy())
2249+ index_ = -1;
2250+ }
2251+ }
2252+
2253+ const Field& field() const {
2254+ assert(valid());
2255+ return fields_[index_];
2256+ }
2257+
2258+ Field& mutable_field() {
2259+ assert(valid());
2260+ return fields_[index_];
2261+ }
2262+
2263+ // Return current geometric coordinates (i.e. map coordinates that can
2264+ // be out of bounds).
2265+ Widelands::Coords geometric_coords() const {
2266+ assert(valid());
2267+ return geometric_coords_;
2268+ }
2269+
2270+ bool tln_valid() const {
2271+ assert(valid());
2272+ return (geometric_coords_.y > fields_.min_fy()) &&
2273+ (geometric_coords_.x + geometric_tblx_shift_ >= fields_.min_fx());
2274+ }
2275+
2276+ const Field& tln() const {
2277+ assert(tln_valid());
2278+ return fields_[index_ + geometric_tblx_shift_ - fields_.get_w()];
2279+ }
2280+
2281+ bool trn_valid() const {
2282+ assert(valid());
2283+ return (geometric_coords_.y > fields_.min_fy()) &&
2284+ (geometric_coords_.x + geometric_tblx_shift_ + 1 <= fields_.max_fx());
2285+ }
2286+
2287+ const Field& trn() const {
2288+ assert(trn_valid());
2289+ return fields_[index_ + geometric_tblx_shift_ + 1 - fields_.get_w()];
2290+ }
2291+
2292+ bool ln_valid() const {
2293+ assert(valid());
2294+ return (geometric_coords_.x - 1 >= fields_.min_fx());
2295+ }
2296+
2297+ const Field& ln() const {
2298+ assert(ln_valid());
2299+ return fields_[index_ - 1];
2300+ }
2301+
2302+ bool rn_valid() const {
2303+ assert(valid());
2304+ return (geometric_coords_.x + 1 <= fields_.max_fx());
2305+ }
2306+
2307+ const Field& rn() const {
2308+ assert(rn_valid());
2309+ return fields_[index_ + 1];
2310+ }
2311+
2312+ bool bln_valid() const {
2313+ assert(valid());
2314+ return (geometric_coords_.y < fields_.max_fy()) &&
2315+ (geometric_coords_.x + geometric_tblx_shift_ >= fields_.min_fx());
2316+ }
2317+
2318+ const Field& bln() const {
2319+ assert(bln_valid());
2320+ return fields_[index_ + geometric_tblx_shift_ + fields_.get_w()];
2321+ }
2322+
2323+ bool brn_valid() const {
2324+ assert(valid());
2325+ return (geometric_coords_.y < fields_.max_fy()) &&
2326+ (geometric_coords_.x + geometric_tblx_shift_ + 1 <= fields_.max_fx());
2327+ }
2328+
2329+ const Field& brn() const {
2330+ assert(brn_valid());
2331+ return fields_[index_ + geometric_tblx_shift_ + 1 + fields_.get_w()];
2332+ }
2333+
2334+ bool all_neighbors_valid() const {
2335+ assert(valid());
2336+ return (geometric_coords_.y > fields_.min_fy() &&
2337+ geometric_coords_.y < fields_.max_fy() &&
2338+ geometric_coords_.x > fields_.min_fx() &&
2339+ geometric_coords_.x < fields_.max_fx());
2340+ }
2341+
2342+private:
2343+ template<typename F>
2344+ void type_check(const FieldsToDraw<F>&) {}
2345+ template<typename F>
2346+ void type_check(const FieldsToDrawRef<F>&) {}
2347+
2348+ FTD& fields_;
2349+
2350+ Widelands::Coords geometric_coords_;
2351+ int geometric_tblx_shift_; // top/bottom left neighbor geometric x-coordinate offset
2352+ int index_;
2353+};
2354+
2355+template<typename F>
2356+inline FieldsToDrawCursor<FieldsToDraw<F>> FieldsToDraw<F>::cursor() {
2357+ return {*this};
2358+}
2359+
2360+template<typename F>
2361+inline FieldsToDrawCursor<const FieldsToDraw<F>> FieldsToDraw<F>::cursor() const {
2362+ return {*this};
2363+}
2364+
2365+template<typename F>
2366+inline FieldsToDrawCursor<const FieldsToDrawRef<F>> FieldsToDrawRef<F>::cursor() const {
2367+ return {*this};
2368+}
2369+
2370 #endif // end of include guard: WL_GRAPHIC_GL_FIELDS_TO_DRAW_H
2371
2372=== modified file 'src/graphic/gl/initialize.cc'
2373--- src/graphic/gl/initialize.cc 2016-08-04 15:49:05 +0000
2374+++ src/graphic/gl/initialize.cc 2017-01-07 12:36:16 +0000
2375@@ -135,6 +135,8 @@
2376 log("Graphics: OpenGL: ShadingLanguage: \"%s\"\n",
2377 reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION)));
2378
2379+ Gl::State::instance().check_capabilities();
2380+
2381 glDrawBuffer(GL_BACK);
2382
2383 glDisable(GL_DEPTH_TEST);
2384
2385=== modified file 'src/graphic/gl/road_program.cc'
2386--- src/graphic/gl/road_program.cc 2016-12-01 17:35:34 +0000
2387+++ src/graphic/gl/road_program.cc 2017-01-07 12:36:16 +0000
2388@@ -48,8 +48,9 @@
2389
2390 void RoadProgram::add_road(const int renderbuffer_width,
2391 const int renderbuffer_height,
2392- const FieldsToDraw::Field& start,
2393- const FieldsToDraw::Field& end,
2394+ Widelands::Coords geometric_coords,
2395+ const FieldToDrawGl2& start,
2396+ const FieldToDrawGl2& end,
2397 const float scale,
2398 const Widelands::RoadType road_type,
2399 const Direction direction,
2400@@ -82,8 +83,8 @@
2401 const Image& texture =
2402 road_type == Widelands::RoadType::kNormal ?
2403 visible_owner->tribe().road_textures().get_normal_texture(
2404- start.geometric_coords, direction) :
2405- visible_owner->tribe().road_textures().get_busy_texture(start.geometric_coords, direction);
2406+ geometric_coords, direction) :
2407+ visible_owner->tribe().road_textures().get_busy_texture(geometric_coords, direction);
2408 if (*gl_texture == 0) {
2409 *gl_texture = texture.blit_data().texture_id;
2410 }
2411@@ -135,42 +136,42 @@
2412
2413 void RoadProgram::draw(const int renderbuffer_width,
2414 const int renderbuffer_height,
2415- const FieldsToDraw& fields_to_draw,
2416+ const FieldsToDrawGl2& fields_to_draw,
2417 const float scale,
2418 const float z_value) {
2419 vertices_.clear();
2420
2421 uint32_t gl_texture = 0;
2422- for (size_t current_index = 0; current_index < fields_to_draw.size(); ++current_index) {
2423- const FieldsToDraw::Field& field = fields_to_draw.at(current_index);
2424+ for (auto cursor = fields_to_draw.cursor(); cursor.valid(); cursor.next()) {
2425+ const FieldToDrawGl2& field = cursor.field();
2426
2427 // Road to right neighbor.
2428- if (field.rn_index != FieldsToDraw::kInvalidIndex) {
2429+ if (cursor.rn_valid()) {
2430 const Widelands::RoadType road =
2431 static_cast<Widelands::RoadType>(field.roads & Widelands::RoadType::kMask);
2432 if (road != Widelands::RoadType::kNone) {
2433- add_road(renderbuffer_width, renderbuffer_height, field,
2434- fields_to_draw.at(field.rn_index), scale, road, kEast, &gl_texture);
2435+ add_road(renderbuffer_width, renderbuffer_height, cursor.geometric_coords(), field,
2436+ cursor.rn(), scale, road, kEast, &gl_texture);
2437 }
2438 }
2439
2440 // Road to bottom right neighbor.
2441- if (field.brn_index != FieldsToDraw::kInvalidIndex) {
2442+ if (cursor.brn_valid()) {
2443 const Widelands::RoadType road =
2444 static_cast<Widelands::RoadType>((field.roads >> 2) & Widelands::RoadType::kMask);
2445 if (road != Widelands::RoadType::kNone) {
2446- add_road(renderbuffer_width, renderbuffer_height, field,
2447- fields_to_draw.at(field.brn_index), scale, road, kSouthEast, &gl_texture);
2448+ add_road(renderbuffer_width, renderbuffer_height, cursor.geometric_coords(), field,
2449+ cursor.brn(), scale, road, kSouthEast, &gl_texture);
2450 }
2451 }
2452
2453 // Road to bottom right neighbor.
2454- if (field.bln_index != FieldsToDraw::kInvalidIndex) {
2455+ if (cursor.bln_valid()) {
2456 const Widelands::RoadType road =
2457 static_cast<Widelands::RoadType>((field.roads >> 4) & Widelands::RoadType::kMask);
2458 if (road != Widelands::RoadType::kNone) {
2459- add_road(renderbuffer_width, renderbuffer_height, field,
2460- fields_to_draw.at(field.bln_index), scale, road, kSouthWest, &gl_texture);
2461+ add_road(renderbuffer_width, renderbuffer_height, cursor.geometric_coords(), field,
2462+ cursor.bln(), scale, road, kSouthWest, &gl_texture);
2463 }
2464 }
2465 }
2466
2467=== modified file 'src/graphic/gl/road_program.h'
2468--- src/graphic/gl/road_program.h 2016-11-03 07:20:57 +0000
2469+++ src/graphic/gl/road_program.h 2017-01-07 12:36:16 +0000
2470@@ -41,7 +41,7 @@
2471 // space.
2472 void draw(int renderbuffer_width,
2473 int renderbuffer_height,
2474- const FieldsToDraw& fields_to_draw,
2475+ const FieldsToDrawGl2& fields_to_draw,
2476 float scale,
2477 float z_value);
2478
2479@@ -60,8 +60,9 @@
2480 enum Direction { kEast, kSouthEast, kSouthWest };
2481 void add_road(int renderbuffer_width,
2482 int renderbuffer_height,
2483- const FieldsToDraw::Field& start,
2484- const FieldsToDraw::Field& end,
2485+ Widelands::Coords geometric_coords,
2486+ const FieldToDrawGl2& start,
2487+ const FieldToDrawGl2& end,
2488 float scale,
2489 const Widelands::RoadType road_type,
2490 const Direction direction,
2491
2492=== added file 'src/graphic/gl/streaming_buffer.h'
2493--- src/graphic/gl/streaming_buffer.h 1970-01-01 00:00:00 +0000
2494+++ src/graphic/gl/streaming_buffer.h 2017-01-07 12:36:16 +0000
2495@@ -0,0 +1,173 @@
2496+/*
2497+ * Copyright 2016 by the Widelands Development Team
2498+ *
2499+ * This program is free software; you can redistribute it and/or
2500+ * modify it under the terms of the GNU General Public License
2501+ * as published by the Free Software Foundation; either version 2
2502+ * of the License, or (at your option) any later version.
2503+ *
2504+ * This program is distributed in the hope that it will be useful,
2505+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2506+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2507+ * GNU General Public License for more details.
2508+ *
2509+ * You should have received a copy of the GNU General Public License
2510+ * along with this program; if not, write to the Free Software
2511+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
2512+ */
2513+
2514+#ifndef WL_GRAPHIC_GL_STREAMING_BUFFER_H
2515+#define WL_GRAPHIC_GL_STREAMING_BUFFER_H
2516+
2517+#include <cassert>
2518+
2519+#include "base/macros.h"
2520+#include "base/wexception.h"
2521+#include "graphic/gl/system_headers.h"
2522+
2523+namespace Gl {
2524+
2525+// Wrapper around an OpenGL buffer object that is intended for streaming use.
2526+//
2527+// Requires GL_ARB_direct_state_access.
2528+template <typename T> class StreamingBuffer {
2529+public:
2530+ class Inserter {
2531+ public:
2532+ Inserter(Inserter&& o)
2533+ : buffer_(o.buffer_), map_(o.map_), count_(o.count_), max_(o.max_) {
2534+ o.buffer_ = nullptr;
2535+ o.map_ = nullptr;
2536+ }
2537+
2538+ ~Inserter() {
2539+ assert(!buffer_ || buffer_->inserting_);
2540+ if (buffer_)
2541+ buffer_->inserting_ = false;
2542+
2543+ if (map_) {
2544+ buffer_->bind();
2545+ glUnmapBuffer(buffer_->target());
2546+ }
2547+ }
2548+
2549+ // The number of elements that have already been inserted.
2550+ size_t count() const {
2551+ return count_;
2552+ }
2553+
2554+ // The maximum number of elements that can be inserted.
2555+ size_t max() const {
2556+ return max_;
2557+ }
2558+
2559+ // Finish inserting. Return the buffer offset for the beginning of
2560+ // the inserted range, as must be passed to OpenGL.
2561+ GLintptr unmap() {
2562+ assert(map_);
2563+ buffer_->bind();
2564+ glUnmapBuffer(buffer_->target());
2565+ map_ = nullptr;
2566+ return 0;
2567+ }
2568+
2569+ // Append an item to the buffer.
2570+ template<typename... Args>
2571+ void emplace_back(Args&&... args) {
2572+ assert(map_);
2573+ assert(count_ < max_);
2574+
2575+ new(&map_[count_]) T(std::forward<Args>(args)...);
2576+ count_++;
2577+ }
2578+
2579+ // Append space for count items, return a pointer to the first one.
2580+ T* add(size_t count) {
2581+ assert(count <= max_ && count_ <= max_ - count);
2582+ T* ret = &map_[count_];
2583+ count_ += count;
2584+ return ret;
2585+ }
2586+
2587+ T& back() {
2588+ assert(map_ && count_ >= 1);
2589+ return map_[count_ - 1];
2590+ }
2591+
2592+ private:
2593+ friend class StreamingBuffer;
2594+
2595+ Inserter(StreamingBuffer& buffer, size_t max)
2596+ : buffer_(&buffer), map_(nullptr), count_(0), max_(max)
2597+ {
2598+ assert(!buffer_->inserting_);
2599+ buffer_->inserting_ = true;
2600+
2601+ buffer_->bind();
2602+ map_ = reinterpret_cast<T*>(glMapBuffer(buffer_->target(), GL_WRITE_ONLY));
2603+ if (!map_)
2604+ throw wexception("Could not map GL buffer.");
2605+ }
2606+
2607+ StreamingBuffer* buffer_;
2608+ T* map_;
2609+ size_t count_;
2610+ size_t max_;
2611+
2612+ DISALLOW_COPY_AND_ASSIGN(Inserter);
2613+ };
2614+
2615+ StreamingBuffer(GLenum target) {
2616+ target_ = target;
2617+ glGenBuffers(1, &object_);
2618+ if (!object_) {
2619+ throw wexception("Could not create GL buffer.");
2620+ }
2621+ }
2622+
2623+ ~StreamingBuffer() {
2624+ assert(!inserting_);
2625+
2626+ if (object_) {
2627+ glDeleteBuffers(1, &object_);
2628+ }
2629+ }
2630+
2631+ // Returns the OpenGL object for direct use.
2632+ GLuint object() const {
2633+ return object_;
2634+ }
2635+
2636+ GLenum target() const {
2637+ return target_;
2638+ }
2639+
2640+ void bind() const {
2641+ glBindBuffer(target_, object_);
2642+ }
2643+
2644+ // Set the buffer up for streaming up to the given number of elements.
2645+ //
2646+ // Previous contents of the buffer are discarded (this does not affect
2647+ // OpenGL functions that have been called previously).
2648+ Inserter stream(size_t max) {
2649+ // Always re-allocate the buffer. We rely on fast swap-out by the
2650+ // driver. If backing store were to be shared globally, it might make
2651+ // sense to consider an alternative scheme using unsynchronized maps
2652+ // and explicit flushing.
2653+ glBindBuffer(target_, object_);
2654+ glBufferData(target_, sizeof(T) * max, NULL, GL_STREAM_DRAW);
2655+ return Inserter(*this, max);
2656+ }
2657+
2658+private:
2659+ GLenum target_;
2660+ GLuint object_;
2661+ bool inserting_ = false;
2662+
2663+ DISALLOW_COPY_AND_ASSIGN(StreamingBuffer);
2664+};
2665+
2666+} // namespace Gl
2667+
2668+#endif // end of include guard: WL_GRAPHIC_GL_STREAMING_BUFFER_H
2669
2670=== modified file 'src/graphic/gl/terrain_program.cc'
2671--- src/graphic/gl/terrain_program.cc 2016-11-03 07:20:57 +0000
2672+++ src/graphic/gl/terrain_program.cc 2017-01-07 12:36:16 +0000
2673@@ -30,6 +30,8 @@
2674 // http://www.opengl.org/registry/doc/GLSLangSpec.Full.1.20.8.pdf
2675 // We target OpenGL 2.1 for the desktop here.
2676 TerrainProgram::TerrainProgram() {
2677+ log("Using GL2 terrain rendering path\n");
2678+
2679 gl_program_.build("terrain");
2680
2681 attr_brightness_ = glGetAttribLocation(gl_program_.object(), "attr_brightness");
2682@@ -70,7 +72,7 @@
2683 glDrawArrays(GL_TRIANGLES, 0, vertices_.size());
2684 }
2685
2686-void TerrainProgram::add_vertex(const FieldsToDraw::Field& field, const Vector2f& texture_offset) {
2687+void TerrainProgram::add_vertex(const FieldToDrawGl2& field, const Vector2f& texture_offset) {
2688 vertices_.emplace_back();
2689 PerVertexData& back = vertices_.back();
2690
2691@@ -85,7 +87,7 @@
2692
2693 void TerrainProgram::draw(uint32_t gametime,
2694 const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
2695- const FieldsToDraw& fields_to_draw,
2696+ const FieldsToDrawGl2& fields_to_draw,
2697 float z_value) {
2698 // This method expects that all terrains have the same dimensions and that
2699 // all are packed into the same texture atlas, i.e. all are in the same GL
2700@@ -94,36 +96,36 @@
2701 vertices_.clear();
2702 vertices_.reserve(fields_to_draw.size() * 3);
2703
2704- for (size_t current_index = 0; current_index < fields_to_draw.size(); ++current_index) {
2705- const FieldsToDraw::Field& field = fields_to_draw.at(current_index);
2706+ for (auto cursor = fields_to_draw.cursor(); cursor.valid(); cursor.next()) {
2707+ const FieldToDrawGl2& field = cursor.field();
2708
2709 // The bottom right neighbor fields_to_draw is needed for both triangles
2710 // associated with this field. If it is not in fields_to_draw, there is no need to
2711 // draw any triangles.
2712- if (field.brn_index == FieldsToDraw::kInvalidIndex) {
2713+ if (!cursor.brn_valid()) {
2714 continue;
2715 }
2716
2717 // Down triangle.
2718- if (field.bln_index != FieldsToDraw::kInvalidIndex) {
2719+ if (cursor.bln_valid()) {
2720 const Vector2f texture_offset =
2721 to_gl_texture(
2722 terrains.get(field.fcoords.field->terrain_d()).get_texture(gametime).blit_data())
2723 .origin();
2724- add_vertex(fields_to_draw.at(current_index), texture_offset);
2725- add_vertex(fields_to_draw.at(field.bln_index), texture_offset);
2726- add_vertex(fields_to_draw.at(field.brn_index), texture_offset);
2727+ add_vertex(field, texture_offset);
2728+ add_vertex(cursor.bln(), texture_offset);
2729+ add_vertex(cursor.brn(), texture_offset);
2730 }
2731
2732 // Right triangle.
2733- if (field.rn_index != FieldsToDraw::kInvalidIndex) {
2734+ if (cursor.rn_valid()) {
2735 const Vector2f texture_offset =
2736 to_gl_texture(
2737 terrains.get(field.fcoords.field->terrain_r()).get_texture(gametime).blit_data())
2738 .origin();
2739- add_vertex(fields_to_draw.at(current_index), texture_offset);
2740- add_vertex(fields_to_draw.at(field.brn_index), texture_offset);
2741- add_vertex(fields_to_draw.at(field.rn_index), texture_offset);
2742+ add_vertex(field, texture_offset);
2743+ add_vertex(cursor.brn(), texture_offset);
2744+ add_vertex(cursor.rn(), texture_offset);
2745 }
2746 }
2747
2748
2749=== modified file 'src/graphic/gl/terrain_program.h'
2750--- src/graphic/gl/terrain_program.h 2016-10-16 09:31:42 +0000
2751+++ src/graphic/gl/terrain_program.h 2017-01-07 12:36:16 +0000
2752@@ -36,7 +36,7 @@
2753 // Draws the terrain.
2754 void draw(uint32_t gametime,
2755 const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
2756- const FieldsToDraw& fields_to_draw,
2757+ const FieldsToDrawGl2& fields_to_draw,
2758 float z_value);
2759
2760 private:
2761@@ -54,7 +54,7 @@
2762 void gl_draw(int gl_texture, float texture_w, float texture_h, float z_value);
2763
2764 // Adds a vertex to the end of vertices with data from 'field' and 'texture_coordinates'.
2765- void add_vertex(const FieldsToDraw::Field& field, const Vector2f& texture_coordinates);
2766+ void add_vertex(const FieldToDrawGl2& field, const Vector2f& texture_coordinates);
2767
2768 // The program used for drawing the terrain.
2769 Gl::Program gl_program_;
2770
2771=== added file 'src/graphic/gl/terrain_program_gl4.cc'
2772--- src/graphic/gl/terrain_program_gl4.cc 1970-01-01 00:00:00 +0000
2773+++ src/graphic/gl/terrain_program_gl4.cc 2017-01-07 12:36:16 +0000
2774@@ -0,0 +1,1065 @@
2775+/*
2776+ * Copyright (C) 2006-2016 by the Widelands Development Team
2777+ *
2778+ * This program is free software; you can redistribute it and/or
2779+ * modify it under the terms of the GNU General Public License
2780+ * as published by the Free Software Foundation; either version 2
2781+ * of the License, or (at your option) any later version.
2782+ *
2783+ * This program is distributed in the hope that it will be useful,
2784+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2785+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2786+ * GNU General Public License for more details.
2787+ *
2788+ * You should have received a copy of the GNU General Public License
2789+ * along with this program; if not, write to the Free Software
2790+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
2791+ *
2792+ */
2793+
2794+#include "graphic/gl/terrain_program_gl4.h"
2795+
2796+#include "graphic/game_renderer_gl4.h"
2797+#include "graphic/gl/coordinate_conversion.h"
2798+#include "graphic/gl/utils.h"
2799+#include "graphic/image_io.h"
2800+#include "io/filesystem/layered_filesystem.h"
2801+#include "logic/editor_game_base.h"
2802+#include "logic/map.h"
2803+#include "logic/map_objects/tribes/tribe_descr.h"
2804+#include "logic/map_objects/world/terrain_description.h"
2805+#include "logic/map_objects/world/world.h"
2806+#include "profile/profile.h"
2807+#include "wui/mapviewpixelfunctions.h"
2808+
2809+using namespace Widelands;
2810+
2811+/**
2812+ * This is the back-end of the GL4 rendering path.
2813+ *
2814+ * Per-field data is uploaded directly into integer-valued textures that span
2815+ * the entire map, and vertex shaders do most of the heavy lifting. The
2816+ * following textures are used:
2817+ *
2818+ * Fields texture: data that is usually constant throughout a game, GL_RGBA8UI:
2819+ * R = r-triangle texture index
2820+ * G = d-triangle texture index
2821+ * B = height
2822+ * A = brightness
2823+ * This information can be modified in the editor and in scenarios. Note that
2824+ * the triangle textures depend on the player perspective.
2825+ *
2826+ * Player brightness texture:
2827+ * R = player-perspective-dependent brightness modulation
2828+ * This information is re-uploaded every frame.
2829+ *
2830+ * Semi-permanent information (GL_R8UI):
2831+ * R = bits 0..5: field ownership (player number)
2832+ * bits 6..7: road/flag/building data:
2833+ * 0: nothing, 1: road, 2: flag, 3: building
2834+ * This information is only needed for the minimap, and re-uploaded every frame
2835+ * when it is shown.
2836+ *
2837+ * Terrain is rendered in patches of a fixed structure, and many patches are
2838+ * rendered in one call via instancing. Per-instance data is processed in a
2839+ * vertex shader.
2840+ *
2841+ * Each patch consists of the triangles associated to a WxH "rectangle" of
2842+ * fields, where the top-left field must be at an even y-coordinate, and H is
2843+ * even, e.g. a 2x4-patch:
2844+ *
2845+ * (0,0)
2846+ * O-------O-------*
2847+ * / \ / \ /
2848+ * / \ / \ /
2849+ * / \ / \ /
2850+ * *-------O-------O-------*
2851+ * / \ / \ /
2852+ * / \ / \ /
2853+ * / \ / \ /
2854+ * O-------O-------*
2855+ * / \ / \ /
2856+ * / \ / \ /
2857+ * / \ / \ /
2858+ * *-------O-------O-------*
2859+ * / \ / \ /
2860+ * / \ / \ /
2861+ * / \ / \ /
2862+ * *-------*-------*
2863+ *
2864+ * OpenGL vertices of triangles are not shared; this allows separate textures
2865+ * and dithering in a single pass.
2866+ *
2867+ * Road rendering is also handled here. Roads are rendered as two triangles per
2868+ * segment. Only per-road data is uploaded; the vertex shader sources data
2869+ * from the per-road buffer based on the vertex ID, and an index buffer
2870+ * (element array buffer in OpenGL terms) is used to share two vertices between
2871+ * the triangles that make up each segment.
2872+ */
2873+
2874+TerrainInformationGl4::GlobalMap TerrainInformationGl4::global_map_;
2875+
2876+std::shared_ptr<TerrainInformationGl4>
2877+TerrainInformationGl4::get(const Widelands::EditorGameBase& egbase,
2878+ const Widelands::Player* player) {
2879+ GlobalKey key(&egbase, player);
2880+ auto it = global_map_.find(key);
2881+ if (it != global_map_.end())
2882+ return it->second.lock();
2883+
2884+ std::shared_ptr<TerrainInformationGl4> instance(
2885+ new TerrainInformationGl4(egbase, player));
2886+ global_map_[key] = instance;
2887+ return instance;
2888+}
2889+
2890+TerrainInformationGl4::TerrainInformationGl4(const Widelands::EditorGameBase& egbase,
2891+ const Widelands::Player* player)
2892+ : egbase_(egbase), player_(player), uploads_(GL_PIXEL_UNPACK_BUFFER) {
2893+ glGenTextures(1, &brightness_texture_);
2894+ glGenTextures(1, &fields_texture_);
2895+ glGenTextures(1, &minimap_texture_);
2896+
2897+ const Map& map = egbase.map();
2898+ auto& gl = Gl::State::instance();
2899+ gl.bind(GL_TEXTURE0, fields_texture_);
2900+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
2901+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
2902+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2903+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2904+
2905+ gl.bind(GL_TEXTURE0, brightness_texture_);
2906+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
2907+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
2908+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2909+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2910+ glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, map.get_width(), map.get_height(), 0,
2911+ GL_RED, GL_UNSIGNED_BYTE, NULL);
2912+ brightness_see_all_ = false;
2913+
2914+ gl.bind(GL_TEXTURE0, minimap_texture_);
2915+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
2916+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
2917+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2918+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2919+ glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, map.get_width(), map.get_height(), 0,
2920+ GL_RED_INTEGER, GL_UNSIGNED_BYTE, NULL);
2921+
2922+ fields_update();
2923+ upload_road_textures();
2924+ upload_constant_textures();
2925+
2926+ updated_minimap_ = false;
2927+ need_update_minimap_ = false;
2928+ minimap_update_next_ = 0;
2929+}
2930+
2931+TerrainInformationGl4::~TerrainInformationGl4() {
2932+ if (brightness_texture_)
2933+ glDeleteTextures(1, &brightness_texture_);
2934+
2935+ if (fields_texture_)
2936+ glDeleteTextures(1, &fields_texture_);
2937+
2938+ if (minimap_texture_)
2939+ glDeleteTextures(1, &minimap_texture_);
2940+
2941+ if (terrain_color_texture_)
2942+ glDeleteTextures(1, &terrain_color_texture_);
2943+
2944+ global_map_.erase(GlobalKey(&egbase_, player_));
2945+}
2946+
2947+/// Add @p rect to @p rects, merging it with any overlapping or touching
2948+/// pre-existing rects (where merging means that the rectangles are replaced by
2949+/// a single rectangle that contains their union). The order of rectangles in
2950+/// @p rects is not preserved.
2951+///
2952+/// Rectangles are interpreted as half-open.
2953+static void add_rect(std::vector<Recti>& rects, const Recti& rect) {
2954+ Recti new_rect = rect;
2955+
2956+ for (size_t i = 0; i < rects.size(); ++i) {
2957+ // Merge rectangles even if they only touch instead of fully overlapping.
2958+ // The rationale is that reducing the number of uploads is often a
2959+ // benefit even when the total size of uploads becomes larger.
2960+ if (new_rect.x + new_rect.w < rects[i].x ||
2961+ rects[i].x + rects[i].w < new_rect.x ||
2962+ new_rect.y + new_rect.h < rects[i].y ||
2963+ rects[i].y + rects[i].h < new_rect.y)
2964+ continue;
2965+
2966+ rects[i] = rects.back();
2967+ rects.pop_back();
2968+ i--;
2969+ }
2970+
2971+ rects.push_back(new_rect);
2972+}
2973+
2974+void TerrainInformationGl4::update(int minfx, int maxfx, int minfy, int maxfy) {
2975+ const Map& map = egbase().map();
2976+ int width = map.get_width();
2977+ int height = map.get_height();
2978+
2979+ auto normalize = [](int& min, int& max, int size) {
2980+ while (min < 0) {
2981+ min += size;
2982+ max += size;
2983+ }
2984+ while (min >= size) {
2985+ min -= size;
2986+ max -= size;
2987+ }
2988+ };
2989+
2990+ normalize(minfx, maxfx, width);
2991+ normalize(minfy, maxfy, height);
2992+
2993+ // Ensure proper row alignment during texture uploads.
2994+ minfx = (minfx / 4) * 4;
2995+ maxfx = ((maxfx + 4) / 4) * 4 - 1;
2996+
2997+ auto add = [&](int startx, int endx) {
2998+ add_rect(update_, Recti(startx, minfy, endx - startx, std::min(maxfy + 1, height) - minfy));
2999+ if (maxfy >= height)
3000+ add_rect(update_, Recti(startx, 0, endx - startx, std::min(maxfy + 1 - height, height)));
3001+ };
3002+
3003+ add(minfx, std::min(maxfx + 1, width));
3004+ if (maxfx >= width)
3005+ add(0, std::min(maxfx + 1 - width, width));
3006+}
3007+
3008+void TerrainInformationGl4::update_minimap() {
3009+ need_update_minimap_ = true;
3010+}
3011+
3012+void TerrainInformationGl4::do_prepare_frame() {
3013+ const Map& map = egbase().map();
3014+
3015+ upload_terrain_data();
3016+
3017+ if (need_update_minimap_) {
3018+ if (!updated_minimap_) {
3019+ // Need a full update when the minimap is drawn for the first
3020+ // time.
3021+ update_.clear();
3022+ update_.emplace_back(0, 0, map.get_width(), map.get_height());
3023+ } else {
3024+ // For the minimap, we want to do rolling texture updates of
3025+ // stripes that cover the whole width or height of the map,
3026+ // depending on which is smaller. For consistency and simplicity,
3027+ // expand all other dirty rectangles to full strips as well.
3028+ //
3029+ // Furthermore, use stripes of a size that is a multiple of a small
3030+ // power of two, since that likely has bandwidth benefits due to
3031+ // how textures are laid out in memory. This also avoids confusion
3032+ // due to pixel (un)packing row alignments.
3033+ unsigned width = map.get_width();
3034+ unsigned height = map.get_height();
3035+ bool horiz = width <= height;
3036+ std::vector<std::pair<unsigned, unsigned>> stripes;
3037+
3038+ // Massage existing stripes, effectively a form of insertion sort.
3039+ stripes.reserve(update_.size() + 1);
3040+ for (size_t i = 0; i < update_.size(); ++i) {
3041+ unsigned min = horiz ? update_[i].y : update_[i].x;
3042+ unsigned max = min + (horiz ? update_[i].h : update_[i].w);
3043+
3044+ min = (min / 8) * 8;
3045+ max = ((max + 7) / 8) * 8;
3046+
3047+ size_t j;
3048+ for (j = 0; j < stripes.size(); ++j) {
3049+ if (max < stripes[j].first) {
3050+ stripes.insert(stripes.begin() + j, std::make_pair(min, max));
3051+ break;
3052+ }
3053+
3054+ if (min <= stripes[j].second) {
3055+ stripes[j].first = std::min(stripes[j].first, min);
3056+ stripes[j].second = std::max(stripes[j].second, max);
3057+
3058+ size_t k;
3059+ for (k = j + 1; k < stripes.size(); ++k) {
3060+ if (stripes[j].second < stripes[k].first)
3061+ break;
3062+
3063+ stripes[j].second = std::max(stripes[j].second, stripes[k].second);
3064+ }
3065+
3066+ stripes.erase(stripes.begin() + j + 1, stripes.begin() + k);
3067+ break;
3068+ }
3069+ }
3070+ if (j >= stripes.size())
3071+ stripes.emplace_back(min, max);
3072+ }
3073+
3074+ if (stripes.empty() || stripes[0].first != 0 ||
3075+ stripes[0].second < (horiz ? height : width)) {
3076+ // Add a stripe (or expand an existing one) for the rolling minimap
3077+ // update.
3078+ if (minimap_update_next_ >= (horiz ? height : width))
3079+ minimap_update_next_ = 0;
3080+
3081+ unsigned min = minimap_update_next_;
3082+ size_t j;
3083+ for (j = 0; j < stripes.size(); ++j) {
3084+ unsigned max = min + 8;
3085+ if (max < stripes[j].first) {
3086+ stripes.insert(stripes.begin() + j, std::make_pair(min, max));
3087+ break;
3088+ }
3089+
3090+ if (min <= stripes[j].second) {
3091+ if (min < stripes[j].first) {
3092+ assert(max == stripes[j].first); // due to multiples of 8
3093+ stripes[j].first = min;
3094+ break;
3095+ }
3096+
3097+ min = minimap_update_next_ = stripes[j].second;
3098+ if (min >= (horiz ? height : width)) {
3099+ min = 0;
3100+ j = 0;
3101+ continue;
3102+ }
3103+ max = std::min(min + 8, horiz ? height : width);
3104+ stripes[j].second = max;
3105+
3106+ size_t k;
3107+ for (k = j + 1; k < stripes.size(); ++k) {
3108+ if (stripes[j].second < stripes[k].first)
3109+ break;
3110+
3111+ stripes[j].second = std::max(stripes[j].second, stripes[k].second);
3112+ }
3113+ stripes.erase(stripes.begin() + j + 1, stripes.begin() + k);
3114+ break;
3115+ }
3116+ }
3117+ if (j >= stripes.size())
3118+ stripes.emplace_back(min, min + 8);
3119+
3120+ minimap_update_next_ += 8;
3121+ }
3122+
3123+ // Convert stripes back to update rectangles.
3124+ update_.resize(stripes.size());
3125+ for (size_t i = 0; i < stripes.size(); ++i) {
3126+ if (horiz) {
3127+ update_[i].x = 0;
3128+ update_[i].w = width;
3129+ update_[i].y = stripes[i].first;
3130+ update_[i].h = stripes[i].second - stripes[i].first;
3131+ } else {
3132+ update_[i].y = 0;
3133+ update_[i].h = height;
3134+ update_[i].x = stripes[i].first;
3135+ update_[i].w = stripes[i].second - stripes[i].first;
3136+ }
3137+ }
3138+ }
3139+ }
3140+
3141+ // Fields data updates are guarded by version numbers instead of
3142+ // rectangles.
3143+ if (fields_base_version_ != map.get_fields_base_version() ||
3144+ (player() && terrain_vision_version_ != player()->get_terrain_vision_version()))
3145+ fields_update();
3146+
3147+ brightness_update();
3148+
3149+ if (need_update_minimap_)
3150+ do_update_minimap();
3151+
3152+ update_.clear();
3153+ updated_minimap_ = need_update_minimap_;
3154+ need_update_minimap_ = false;
3155+}
3156+
3157+void TerrainInformationGl4::prepare_frame() {
3158+ for (auto& entries : global_map_) {
3159+ std::shared_ptr<TerrainInformationGl4> ti = entries.second.lock();
3160+
3161+ ti->do_prepare_frame();
3162+ }
3163+}
3164+
3165+void TerrainInformationGl4::do_update_minimap() {
3166+ // Re-upload minimap data.
3167+ auto& gl = Gl::State::instance();
3168+ const Map& map = egbase().map();
3169+ unsigned width = map.get_width();
3170+ std::vector<uint8_t> data;
3171+ const bool see_all = !player() || player()->see_all();
3172+
3173+ auto detail_bits = [&](const Widelands::BaseImmovable* imm) -> uint8_t {
3174+ if (imm) {
3175+ Widelands::MapObjectType type = imm->descr().type();
3176+ if (type == Widelands::MapObjectType::ROAD)
3177+ return 1u << 6;
3178+ if (type == Widelands::MapObjectType::FLAG)
3179+ return 2u << 6;
3180+ if (type >= Widelands::MapObjectType::BUILDING)
3181+ return 3u << 6;
3182+ }
3183+ return 0;
3184+ };
3185+
3186+ gl.bind(GL_TEXTURE0, minimap_texture_);
3187+
3188+ for (const Recti& rect : update_) {
3189+ data.resize(rect.w * rect.h);
3190+ if (see_all) {
3191+ unsigned i = 0;
3192+ for (unsigned y = 0; y < unsigned(rect.h); ++y) {
3193+ unsigned idx = (rect.y + y) * width + rect.x;
3194+ for (unsigned x = 0; x < unsigned(rect.w); ++x, ++i, ++idx) {
3195+ const Field& f = map[idx];
3196+ data[i] = f.get_owned_by();
3197+ data[i] |= detail_bits(f.get_immovable());
3198+ }
3199+ }
3200+ } else {
3201+ unsigned i = 0;
3202+ for (unsigned y = 0; y < unsigned(rect.h); ++y) {
3203+ unsigned idx = (rect.y + y) * width + rect.x;
3204+ for (unsigned x = 0; x < unsigned(rect.w); ++x, ++i, ++idx) {
3205+ const Player::Field& pf = player()->fields()[idx];
3206+ data[i] = pf.owner;
3207+
3208+ if (pf.vision >= 2) {
3209+ const Field& f = map[idx];
3210+ data[i] |= detail_bits(f.get_immovable());
3211+ }
3212+ }
3213+ }
3214+ }
3215+
3216+ glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x, rect.y, rect.w, rect.h,
3217+ GL_RED_INTEGER, GL_UNSIGNED_BYTE, data.data());
3218+ }
3219+}
3220+
3221+void TerrainInformationGl4::fields_update() {
3222+ auto& gl = Gl::State::instance();
3223+ const Map& map = egbase().map();
3224+ auto stream = uploads_.stream(sizeof(PerFieldData) * uint(map.get_width()) * map.get_height());
3225+ PerFieldData* fd =
3226+ reinterpret_cast<PerFieldData*>
3227+ (stream.add(sizeof(PerFieldData) * uint(map.get_width()) * map.get_height()));
3228+ MapIndex max_index = map.max_index();
3229+ const bool see_all = !player() || player()->see_all();
3230+
3231+ if (see_all) {
3232+ for (MapIndex i = 0; i < max_index; ++i) {
3233+ const Field& f = map[i];
3234+ fd[i].terrain_r = f.terrain_r();
3235+ fd[i].terrain_d = f.terrain_d();
3236+ fd[i].height = f.get_height();
3237+ fd[i].brightness = f.get_brightness();
3238+ }
3239+ } else {
3240+ const Player::Field* player_fields = player()->fields();
3241+
3242+ for (MapIndex i = 0; i < max_index; ++i) {
3243+ const Field& f = map[i];
3244+ const Player::Field& pf = player_fields[i];
3245+ fd[i].terrain_r = pf.terrains.r;
3246+ fd[i].terrain_d = pf.terrains.d;
3247+ fd[i].height = f.get_height();
3248+ fd[i].brightness = f.get_brightness();
3249+ }
3250+ }
3251+
3252+ GLintptr offset = stream.unmap();
3253+ uploads_.bind();
3254+
3255+ gl.bind(GL_TEXTURE0, fields_texture_);
3256+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8UI, map.get_width(), map.get_height(), 0,
3257+ GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, reinterpret_cast<void*>(offset));
3258+
3259+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
3260+
3261+ fields_base_version_ = map.get_fields_base_version();
3262+ if (player())
3263+ terrain_vision_version_ = player()->get_terrain_vision_version();
3264+}
3265+
3266+TerrainInformationGl4::PerRoadTextureData::PerRoadTextureData(const Rectf& rect)
3267+ : x(rect.x), y(rect.y), w(rect.w), h(rect.h) {
3268+}
3269+
3270+void TerrainInformationGl4::upload_road_textures() {
3271+ std::vector<PerRoadTextureData> roads;
3272+ std::map<const TribeDescr*, unsigned> tribe_map;
3273+ PlayerNumber const nr_players = egbase().map().get_nrplayers();
3274+
3275+ player_roads_.resize(nr_players + 1);
3276+
3277+ iterate_players_existing_const(p, nr_players, egbase(), player) {
3278+ const TribeDescr& tribe = player->tribe();
3279+ auto it = tribe_map.find(&tribe);
3280+ if (it != tribe_map.end()) {
3281+ player_roads_[p] = player_roads_[it->second];
3282+ } else {
3283+ const auto& normal_textures = tribe.road_textures().get_normal_textures();
3284+ player_roads_[p].normal_roads = roads.size();
3285+ player_roads_[p].num_normal_roads = normal_textures.size();
3286+ for (const Image* image : normal_textures) {
3287+ const BlitData& blit_data = image->blit_data();
3288+ roads.emplace_back(to_gl_texture(blit_data));
3289+ road_texture_object_ = blit_data.texture_id;
3290+ }
3291+
3292+ const auto& busy_textures = tribe.road_textures().get_busy_textures();
3293+ player_roads_[p].busy_roads = roads.size();
3294+ player_roads_[p].num_busy_roads = busy_textures.size();
3295+ for (const Image* image : busy_textures)
3296+ roads.emplace_back(to_gl_texture(image->blit_data()));
3297+
3298+ tribe_map[&tribe] = p;
3299+ }
3300+ }
3301+
3302+ road_textures_.bind();
3303+ road_textures_.update(roads);
3304+}
3305+
3306+unsigned TerrainInformationGl4::road_texture_idx(PlayerNumber owner,
3307+ RoadType road_type,
3308+ const Coords& coords,
3309+ WalkingDir direction) const {
3310+ const PlayerRoads& roads = player_roads_[owner];
3311+ unsigned base, count;
3312+
3313+ if (road_type == RoadType::kNormal) {
3314+ base = roads.normal_roads;
3315+ count = roads.num_normal_roads;
3316+ } else {
3317+ base = roads.busy_roads;
3318+ count = roads.num_busy_roads;
3319+ }
3320+
3321+ return base + unsigned(coords.x + coords.y + direction) % count;
3322+}
3323+
3324+// Upload the per-terrain texture data. This is done on every draw call because
3325+// it depends on the gametime.
3326+void TerrainInformationGl4::upload_terrain_data() {
3327+ uint32_t gametime = egbase().get_gametime();
3328+ const auto& terrains = egbase().world().terrains();
3329+ std::vector<PerTerrainData> data;
3330+
3331+ data.resize(terrains.size());
3332+
3333+ for (unsigned i = 0; i < terrains.size(); ++i) {
3334+ PerTerrainData& terrain = data[i];
3335+ const TerrainDescription& descr = terrains.get(i);
3336+ terrain.offset =
3337+ to_gl_texture(descr.get_texture(gametime).blit_data()).origin();
3338+ terrain.dither_layer = descr.dither_layer();
3339+ }
3340+
3341+ terrain_data_.bind();
3342+ terrain_data_.update(data);
3343+}
3344+
3345+void TerrainInformationGl4::brightness_update() {
3346+ auto& gl = Gl::State::instance();
3347+ bool see_all = !player_ || player_->see_all();
3348+
3349+ gl.bind(GL_TEXTURE0, brightness_texture_);
3350+
3351+ if (see_all) {
3352+ if (!brightness_see_all_) {
3353+ // Pixel unpacking has a per-row alignment of 4 bytes. Usually this
3354+ // is not a problem for us, because maps' widths are always multiples
3355+ // of 4, but in this particular case, OpenGL implementations disagree
3356+ // about whether the alignment should be considered for the bounds
3357+ // check in glTexImage2D. If we only allocate 1 byte, some
3358+ // implementations flag a GL_INVALID_OPERATION.
3359+ static const uint8_t data[4] = {255, 255, 255, 255};
3360+
3361+ glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, 1, 1, 0, GL_RED, GL_UNSIGNED_BYTE,
3362+ data);
3363+ brightness_see_all_ = true;
3364+ }
3365+ } else {
3366+ const Map& map = egbase().map();
3367+ int width = map.get_width();
3368+ int height = map.get_height();
3369+ uint32_t gametime = egbase().get_gametime();
3370+ std::vector<uint8_t> data;
3371+
3372+ if (brightness_see_all_) {
3373+ // Resize the texture when switching between see-all and not-see-all.
3374+ glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED,
3375+ GL_UNSIGNED_BYTE, NULL);
3376+ brightness_see_all_ = false;
3377+ }
3378+
3379+ for (const Recti& rect : update_) {
3380+ data.resize(rect.w * rect.h);
3381+
3382+ unsigned dst = 0;
3383+ for (unsigned y = 0; y < unsigned(rect.h); ++y) {
3384+ unsigned src = (rect.y + y) * width + rect.x;
3385+ for (unsigned x = 0; x < unsigned(rect.w); ++x, ++src, ++dst) {
3386+ const Player::Field& pf = player_->fields()[src];
3387+ if (pf.vision == 0) {
3388+ data[dst] = 0;
3389+ } else if (pf.vision == 1) {
3390+ static const uint32_t kDecayTimeInMs = 20000;
3391+ const Duration time_ago = gametime - pf.time_node_last_unseen;
3392+ if (time_ago < kDecayTimeInMs) {
3393+ data[dst] = 255 * (2 * kDecayTimeInMs - time_ago) / (2 * kDecayTimeInMs);
3394+ } else {
3395+ data[dst] = 128;
3396+ }
3397+ } else {
3398+ data[dst] = 255;
3399+ }
3400+ }
3401+ }
3402+
3403+ glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x, rect.y, rect.w, rect.h,
3404+ GL_RED, GL_UNSIGNED_BYTE, &data[0]);
3405+ }
3406+ }
3407+}
3408+
3409+void TerrainInformationGl4::upload_constant_textures() {
3410+ auto& gl = Gl::State::instance();
3411+ const auto& terrains = egbase().world().terrains();
3412+ std::vector<RGBColor> colors;
3413+
3414+ for (Widelands::DescriptionIndex i = 0; i < terrains.size(); ++i)
3415+ colors.push_back(terrains.get(i).get_minimap_color(0));
3416+
3417+ glGenTextures(1, &terrain_color_texture_);
3418+
3419+ gl.bind(GL_TEXTURE0, terrain_color_texture_);
3420+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, colors.size(), 1, 0, GL_RGB, GL_UNSIGNED_BYTE,
3421+ colors.data());
3422+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3423+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3424+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3425+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3426+
3427+ colors.resize(kMaxPlayers);
3428+ for (int i = 1; i <= kMaxPlayers; ++i) {
3429+ const Widelands::Player* player = egbase().get_player(i);
3430+ if (player)
3431+ colors[i - 1] = player->get_playercolor();
3432+ }
3433+
3434+ glGenTextures(1, &player_color_texture_);
3435+
3436+ gl.bind(GL_TEXTURE0, player_color_texture_);
3437+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, colors.size(), 1, 0, GL_RGB, GL_UNSIGNED_BYTE,
3438+ colors.data());
3439+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
3440+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
3441+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3442+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3443+}
3444+
3445+TerrainProgramGl4::Terrain::Terrain()
3446+ : instance_data(GL_ARRAY_BUFFER) {
3447+ // Initialize program.
3448+ gl_program.build_vp_fp({"terrain_gl4", "terrain_common_gl4"}, {"terrain_gl4"});
3449+
3450+ in_vertex_coordinate = glGetAttribLocation(gl_program.object(), "in_vertex_coordinate");
3451+ in_patch_coordinate = glGetAttribLocation(gl_program.object(), "in_patch_coordinate");
3452+
3453+ u_position_scale = glGetUniformLocation(gl_program.object(), "u_position_scale");
3454+ u_position_offset = glGetUniformLocation(gl_program.object(), "u_position_offset");
3455+ u_z_value = glGetUniformLocation(gl_program.object(), "u_z_value");
3456+ u_texture_dimensions = glGetUniformLocation(gl_program.object(), "u_texture_dimensions");
3457+
3458+ u_terrain_base = glGetUniformLocation(gl_program.object(), "u_terrain_base");
3459+ u_player_brightness = glGetUniformLocation(gl_program.object(), "u_player_brightness");
3460+ u_terrain_texture = glGetUniformLocation(gl_program.object(), "u_terrain_texture");
3461+ u_dither_texture = glGetUniformLocation(gl_program.object(), "u_dither_texture");
3462+
3463+ block_terrains_idx = glGetUniformBlockIndex(gl_program.object(), "block_terrains");
3464+}
3465+
3466+TerrainProgramGl4::Terrain::~Terrain() {
3467+}
3468+
3469+TerrainProgramGl4::Roads::Roads()
3470+ : road_data(GL_SHADER_STORAGE_BUFFER) {
3471+ num_index_roads = 0;
3472+
3473+ // Initialize program.
3474+ gl_program.build_vp_fp({"road_gl4", "terrain_common_gl4"}, {"road"});
3475+
3476+ u_position_scale = glGetUniformLocation(gl_program.object(), "u_position_scale");
3477+ u_position_offset = glGetUniformLocation(gl_program.object(), "u_position_offset");
3478+ u_z_value = glGetUniformLocation(gl_program.object(), "u_z_value");
3479+
3480+ u_terrain_base = glGetUniformLocation(gl_program.object(), "u_terrain_base");
3481+ u_player_brightness = glGetUniformLocation(gl_program.object(), "u_player_brightness");
3482+ u_texture = glGetUniformLocation(gl_program.object(), "u_texture");
3483+
3484+ block_textures_idx = glGetUniformBlockIndex(gl_program.object(), "block_textures");
3485+}
3486+
3487+TerrainProgramGl4::Roads::~Roads() {
3488+}
3489+
3490+TerrainProgramGl4::MiniMap::MiniMap()
3491+ : vertex_data(GL_ARRAY_BUFFER) {
3492+ gl_program.build_vp_fp({"minimap_gl4"}, {"minimap_gl4"});
3493+
3494+ in_position = glGetAttribLocation(gl_program.object(), "in_position");
3495+ in_field = glGetAttribLocation(gl_program.object(), "in_field");
3496+
3497+ u_layer_terrain = glGetUniformLocation(gl_program.object(), "u_layer_terrain");
3498+ u_layer_owner = glGetUniformLocation(gl_program.object(), "u_layer_owner");
3499+ u_layer_details = glGetUniformLocation(gl_program.object(), "u_layer_details");
3500+
3501+ u_frame_topleft = glGetUniformLocation(gl_program.object(), "u_frame_topleft");
3502+ u_frame_bottomright = glGetUniformLocation(gl_program.object(), "u_frame_bottomright");
3503+
3504+ u_terrain_base = glGetUniformLocation(gl_program.object(), "u_terrain_base");
3505+ u_player_brightness = glGetUniformLocation(gl_program.object(), "u_player_brightness");
3506+ u_minimap_extra = glGetUniformLocation(gl_program.object(), "u_minimap_extra");
3507+ u_terrain_color = glGetUniformLocation(gl_program.object(), "u_terrain_color");
3508+ u_player_color = glGetUniformLocation(gl_program.object(), "u_player_color");
3509+}
3510+
3511+TerrainProgramGl4::MiniMap::~MiniMap() {
3512+}
3513+
3514+TerrainProgramGl4::TerrainProgramGl4() {
3515+ log("Using GL4 terrain rendering path\n");
3516+
3517+ // Initialize vertex buffer (every instance/path has the same structure).
3518+ init_vertex_data();
3519+
3520+ // Load mask texture for dithering.
3521+ terrain_.dither_mask.reset(new Texture(load_image_as_sdl_surface("world/pics/edge.png", g_fs), true));
3522+
3523+ Gl::State::instance().bind(GL_TEXTURE0, terrain_.dither_mask->blit_data().texture_id);
3524+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, static_cast<GLint>(GL_CLAMP_TO_EDGE));
3525+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, static_cast<GLint>(GL_CLAMP_TO_EDGE));
3526+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, static_cast<GLint>(GL_LINEAR));
3527+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, static_cast<GLint>(GL_LINEAR));
3528+}
3529+
3530+TerrainProgramGl4::~TerrainProgramGl4() {
3531+}
3532+
3533+bool TerrainProgramGl4::supported() {
3534+ const auto& caps = Gl::State::instance().capabilities();
3535+
3536+ if (caps.glsl_version < 130)
3537+ return false;
3538+
3539+ if (!caps.ARB_separate_shader_objects ||
3540+ !caps.ARB_shader_storage_buffer_object ||
3541+ !caps.ARB_uniform_buffer_object)
3542+ return false;
3543+
3544+ return !g_options.pull_section("global").get_bool("disable_gl4", false);
3545+}
3546+
3547+void TerrainProgramGl4::draw(const TerrainGl4Arguments* args,
3548+ float z_value) {
3549+ auto& gl = Gl::State::instance();
3550+
3551+ // First, draw the terrain.
3552+ glUseProgram(terrain_.gl_program.object());
3553+
3554+ // Coordinate transform from map coordinates to GL coordinates.
3555+ float scale_x = 2.0 / args->surface_width * args->scale;
3556+ float scale_y = -2.0 / args->surface_height * args->scale;
3557+ float offset_x = args->surface_offset.x * scale_x - 1.0;
3558+ float offset_y = args->surface_offset.y * scale_y + 1.0;
3559+
3560+ // Texture size
3561+ const BlitData& blit_data = args->terrain->egbase().world().terrains().get(0).get_texture(0).blit_data();
3562+ const Rectf texture_coordinates = to_gl_texture(blit_data);
3563+
3564+ // Prepare uniforms.
3565+ glBindBufferBase(GL_UNIFORM_BUFFER, terrain_.block_terrains_idx,
3566+ args->terrain->terrain_data_buffer_object());
3567+
3568+ glUniform2f(terrain_.u_position_scale, scale_x, scale_y);
3569+ glUniform2f(terrain_.u_position_offset, offset_x, offset_y);
3570+ glUniform1f(terrain_.u_z_value, z_value);
3571+ glUniform2f(terrain_.u_texture_dimensions, texture_coordinates.w, texture_coordinates.h);
3572+
3573+ // Prepare textures & sampler uniforms.
3574+ glUniform1i(terrain_.u_terrain_base, 0);
3575+ gl.bind(GL_TEXTURE0, args->terrain->fields_texture());
3576+
3577+ glUniform1i(terrain_.u_player_brightness, 1);
3578+ gl.bind(GL_TEXTURE1, args->terrain->player_brightness_texture());
3579+
3580+ glUniform1i(terrain_.u_terrain_texture, 2);
3581+ gl.bind(GL_TEXTURE2, blit_data.texture_id);
3582+
3583+ glUniform1i(terrain_.u_dither_texture, 3);
3584+ gl.bind(GL_TEXTURE3, terrain_.dither_mask->blit_data().texture_id);
3585+
3586+ // Setup vertex and instance attribute data.
3587+ gl.enable_vertex_attrib_array(
3588+ {terrain_.in_vertex_coordinate, terrain_.in_patch_coordinate});
3589+
3590+ unsigned num_instances = upload_instance_data(args);
3591+
3592+ terrain_.vertex_data.bind();
3593+ glVertexAttribIPointer(terrain_.in_vertex_coordinate, 4, GL_INT, sizeof(PerVertexData), nullptr);
3594+
3595+ glDrawArraysInstanced(GL_TRIANGLES, 0, 6 * kPatchWidth * kPatchHeight, num_instances);
3596+
3597+ glVertexBindingDivisor(terrain_.in_patch_coordinate, 0);
3598+}
3599+
3600+void TerrainProgramGl4::draw_minimap(const TerrainGl4Arguments* args,
3601+ float z_value) {
3602+ auto& gl = Gl::State::instance();
3603+ const Widelands::Map& map = args->terrain->egbase().map();
3604+ float width = map.get_width();
3605+ float height = map.get_height();
3606+
3607+ glUseProgram(minimap_.gl_program.object());
3608+
3609+ // Prepare minimap setting uniforms
3610+ glUniform1i(minimap_.u_layer_terrain, (args->minimap_layers & MiniMapLayer::Terrain) ? 1 : 0);
3611+ glUniform1i(minimap_.u_layer_owner, (args->minimap_layers & MiniMapLayer::Owner) ? 1 : 0);
3612+
3613+ uint details = 0;
3614+ if (args->minimap_layers & MiniMapLayer::Road)
3615+ details |= 1;
3616+ if (args->minimap_layers & MiniMapLayer::Flag)
3617+ details |= 2;
3618+ if (args->minimap_layers & MiniMapLayer::Building)
3619+ details |= 4;
3620+
3621+ glUniform1ui(minimap_.u_layer_details, details);
3622+
3623+ // Prepare textures & sampler uniforms.
3624+ glUniform1i(minimap_.u_terrain_base, 0);
3625+ gl.bind(GL_TEXTURE0, args->terrain->fields_texture());
3626+
3627+ glUniform1i(minimap_.u_player_brightness, 1);
3628+ gl.bind(GL_TEXTURE1, args->terrain->player_brightness_texture());
3629+
3630+ glUniform1i(minimap_.u_minimap_extra, 2);
3631+ gl.bind(GL_TEXTURE2, args->terrain->minimap_texture());
3632+
3633+ glUniform1i(minimap_.u_terrain_color, 3);
3634+ gl.bind(GL_TEXTURE3, args->terrain->terrain_color_texture());
3635+
3636+ glUniform1i(minimap_.u_player_color, 4);
3637+ gl.bind(GL_TEXTURE4, args->terrain->player_color_texture());
3638+
3639+ glUniform2f(minimap_.u_frame_topleft, (args->minfx + 0.001) / width, (args->minfy + 0.001) / height);
3640+ glUniform2f(minimap_.u_frame_bottomright, (args->maxfx - 0.001) / width, (args->maxfy - 0.001) / height);
3641+
3642+ // Compute coordinates and upload vertex data.
3643+ if (args->minimap_layers & MiniMapLayer::Zoom2) {
3644+ width *= 2;
3645+ height *= 2;
3646+ }
3647+
3648+ float left = args->surface_offset.x;
3649+ float right = left + width;
3650+ float top = args->surface_offset.y;
3651+ float bottom = top + height;
3652+
3653+ pixel_to_gl_renderbuffer(args->surface_width, args->surface_height, &left, &top);
3654+ pixel_to_gl_renderbuffer(args->surface_width, args->surface_height, &right, &bottom);
3655+
3656+ float tx = args->minimap_tl_fx * (1.0 / map.get_width());
3657+ float ty = args->minimap_tl_fy * (1.0 / map.get_height());
3658+
3659+ auto stream = minimap_.vertex_data.stream(4);
3660+ MiniMap::VertexData* v = stream.add(4);
3661+
3662+ v[0].x = left;
3663+ v[0].y = top;
3664+ v[0].z = z_value;
3665+ v[0].tx = tx;
3666+ v[0].ty = ty;
3667+
3668+ v[1].x = left;
3669+ v[1].y = bottom;
3670+ v[1].z = z_value;
3671+ v[1].tx = tx;
3672+ v[1].ty = ty + 1.0;
3673+
3674+ v[2].x = right;
3675+ v[2].y = top;
3676+ v[2].z = z_value;
3677+ v[2].tx = tx + 1.0;
3678+ v[2].ty = ty;
3679+
3680+ v[3].x = right;
3681+ v[3].y = bottom;
3682+ v[3].z = z_value;
3683+ v[3].tx = tx + 1.0;
3684+ v[3].ty = ty + 1.0;
3685+
3686+ GLintptr offset = stream.unmap();
3687+
3688+ gl.enable_vertex_attrib_array({minimap_.in_position, minimap_.in_field});
3689+
3690+ minimap_.vertex_data.bind();
3691+ Gl::vertex_attrib_pointer(minimap_.in_position, 3, sizeof(MiniMap::VertexData), offset);
3692+ Gl::vertex_attrib_pointer(minimap_.in_field, 2, sizeof(MiniMap::VertexData),
3693+ offset + offsetof(MiniMap::VertexData, tx));
3694+
3695+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
3696+}
3697+
3698+void TerrainProgramGl4::draw_roads(const TerrainGl4Arguments* args,
3699+ float z_value) {
3700+ auto& gl = Gl::State::instance();
3701+
3702+ // Coordinate transform from map coordinates to GL coordinates.
3703+ float scale_x = 2.0 / args->surface_width * args->scale;
3704+ float scale_y = -2.0 / args->surface_height * args->scale;
3705+ float offset_x = args->surface_offset.x * scale_x - 1.0;
3706+ float offset_y = args->surface_offset.y * scale_y + 1.0;
3707+
3708+ glUseProgram(roads_.gl_program.object());
3709+
3710+ setup_road_index_buffer(args->roads.size());
3711+
3712+ // Prepare uniforms.
3713+ glUniform2f(roads_.u_position_scale, scale_x, scale_y);
3714+ glUniform2f(roads_.u_position_offset, offset_x, offset_y);
3715+ glUniform1f(roads_.u_z_value, z_value);
3716+
3717+ // Prepare textures & sampler uniforms.
3718+ glUniform1i(roads_.u_terrain_base, 0);
3719+ gl.bind(GL_TEXTURE0, args->terrain->fields_texture());
3720+
3721+ glUniform1i(roads_.u_player_brightness, 1);
3722+ gl.bind(GL_TEXTURE1, args->terrain->player_brightness_texture());
3723+
3724+ glUniform1i(roads_.u_texture, 2);
3725+ gl.bind(GL_TEXTURE2, args->terrain->road_texture_object());
3726+
3727+ glBindBufferBase(GL_UNIFORM_BUFFER, roads_.block_textures_idx,
3728+ args->terrain->road_textures_buffer_object());
3729+
3730+ upload_road_data(args);
3731+
3732+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, roads_.gl_index_buffer.object());
3733+ glDrawElements(GL_TRIANGLES, 6 * args->roads.size(), GL_UNSIGNED_SHORT, nullptr);
3734+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
3735+}
3736+
3737+void TerrainProgramGl4::init_vertex_data() {
3738+ std::vector<PerVertexData> vertices;
3739+
3740+ for (int y = 0; y < int(kPatchHeight); ++y) {
3741+ for (int x = 0; x < int(kPatchWidth); ++x) {
3742+ int blx = (y & 1) ? x : (x - 1);
3743+
3744+ // Down triangle.
3745+ vertices.emplace_back(x, y, x, y, false, 2);
3746+ vertices.emplace_back(blx, y + 1, x, y, false, 0);
3747+ vertices.emplace_back(blx + 1, y + 1, x, y, false, 1);
3748+
3749+ // Right triangle.
3750+ vertices.emplace_back(x, y, x, y, true, 1);
3751+ vertices.emplace_back(blx + 1, y + 1, x, y, true, 2);
3752+ vertices.emplace_back(x + 1, y, x, y, true, 0);
3753+ }
3754+ }
3755+
3756+ assert(vertices.size() == 6 * kPatchWidth * kPatchHeight);
3757+
3758+ terrain_.vertex_data.bind();
3759+ terrain_.vertex_data.update(vertices);
3760+}
3761+
3762+// Determine which instances/patches to draw, upload the data and set up the
3763+// vertex attributes.
3764+unsigned TerrainProgramGl4::upload_instance_data(const TerrainGl4Arguments* args) {
3765+ int minfx = args->minfx;
3766+ int minfy = args->minfy;
3767+ int maxfx = args->maxfx;
3768+ int maxfy = args->maxfy;
3769+ if (minfy & 1)
3770+ minfy--;
3771+
3772+ int ph = (maxfy - minfy + kPatchHeight) / kPatchHeight;
3773+ int pw = (maxfx - minfx + kPatchWidth) / kPatchWidth;
3774+ int num_patches = pw * ph;
3775+
3776+ auto stream = terrain_.instance_data.stream(num_patches);
3777+ for (int py = 0; py < ph; ++py) {
3778+ for (int px = 0; px < pw; ++px) {
3779+ const int fx = minfx + px * kPatchWidth;
3780+ const int fy = minfy + py * kPatchHeight;
3781+
3782+ stream.emplace_back();
3783+ PerInstanceData& i = stream.back();
3784+ i.coordinate.x = fx;
3785+ i.coordinate.y = fy;
3786+ }
3787+ }
3788+
3789+ GLintptr offset = stream.unmap();
3790+
3791+ glVertexAttribIPointer(terrain_.in_patch_coordinate, 2, GL_INT, sizeof(PerInstanceData),
3792+ reinterpret_cast<void*>(offset + offsetof(PerInstanceData, coordinate)));
3793+
3794+ glVertexBindingDivisor(terrain_.in_patch_coordinate, 1);
3795+
3796+ return num_patches;
3797+}
3798+
3799+void TerrainProgramGl4::setup_road_index_buffer(unsigned num_roads) {
3800+ if (num_roads <= roads_.num_index_roads)
3801+ return;
3802+
3803+ if (num_roads > 65536 / 4)
3804+ throw wexception("Too many roads for 16-bit indices");
3805+
3806+ std::vector<uint16_t> indices;
3807+ indices.reserve(num_roads * 6);
3808+
3809+ for (unsigned i = 0; i < num_roads; ++i) {
3810+ indices.push_back(4 * i);
3811+ indices.push_back(4 * i + 1);
3812+ indices.push_back(4 * i + 2);
3813+
3814+ indices.push_back(4 * i + 2);
3815+ indices.push_back(4 * i + 1);
3816+ indices.push_back(4 * i + 3);
3817+ }
3818+
3819+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, roads_.gl_index_buffer.object());
3820+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint16_t) * indices.size(),
3821+ indices.data(), GL_STATIC_DRAW);
3822+ roads_.num_index_roads = num_roads;
3823+}
3824+
3825+void TerrainProgramGl4::upload_road_data(const TerrainGl4Arguments* args) {
3826+ assert(!args->roads.empty());
3827+
3828+ auto stream = roads_.road_data.stream(args->roads.size());
3829+
3830+ for (const TerrainGl4Arguments::Road& road : args->roads) {
3831+ stream.emplace_back(
3832+ Vector2i(road.coord.x, road.coord.y), road.direction,
3833+ args->terrain->road_texture_idx(
3834+ road.owner, RoadType(road.type), road.coord, WalkingDir(road.direction)));
3835+ }
3836+
3837+ glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, roads_.road_data.object(),
3838+ stream.unmap(), args->roads.size() * sizeof(PerRoadData));
3839+}
3840
3841=== added file 'src/graphic/gl/terrain_program_gl4.h'
3842--- src/graphic/gl/terrain_program_gl4.h 1970-01-01 00:00:00 +0000
3843+++ src/graphic/gl/terrain_program_gl4.h 2017-01-07 12:36:16 +0000
3844@@ -0,0 +1,352 @@
3845+/*
3846+ * Copyright (C) 2006-2016 by the Widelands Development Team
3847+ *
3848+ * This program is free software; you can redistribute it and/or
3849+ * modify it under the terms of the GNU General Public License
3850+ * as published by the Free Software Foundation; either version 2
3851+ * of the License, or (at your option) any later version.
3852+ *
3853+ * This program is distributed in the hope that it will be useful,
3854+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3855+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3856+ * GNU General Public License for more details.
3857+ *
3858+ * You should have received a copy of the GNU General Public License
3859+ * along with this program; if not, write to the Free Software
3860+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
3861+ *
3862+ */
3863+
3864+#ifndef WL_GRAPHIC_GL_TERRAIN_PROGRAM_GL4_H
3865+#define WL_GRAPHIC_GL_TERRAIN_PROGRAM_GL4_H
3866+
3867+#include <map>
3868+#include <memory>
3869+#include <unordered_map>
3870+
3871+#include "base/rect.h"
3872+#include "graphic/gl/streaming_buffer.h"
3873+#include "graphic/gl/utils.h"
3874+#include "logic/map_objects/walkingdir.h"
3875+#include "logic/roadtype.h"
3876+#include "logic/widelands.h"
3877+
3878+namespace Widelands {
3879+struct Coords;
3880+class EditorGameBase;
3881+class Player;
3882+}
3883+
3884+struct TerrainGl4Arguments;
3885+class Texture;
3886+
3887+/**
3888+ * This class maintains the terrain information textures.
3889+ */
3890+class TerrainInformationGl4 {
3891+public:
3892+ ~TerrainInformationGl4();
3893+
3894+ // Get the global instance associated to the given editor/game instance
3895+ // and player perspective. If player is nullptr, an omniscient perspective
3896+ // is returned.
3897+ static std::shared_ptr<TerrainInformationGl4>
3898+ get(const Widelands::EditorGameBase& egbase,
3899+ const Widelands::Player* player = nullptr);
3900+
3901+ static void prepare_frame();
3902+
3903+ const Widelands::EditorGameBase& egbase() const {
3904+ return egbase_;
3905+ }
3906+
3907+ const Widelands::Player* player() const {
3908+ return player_;
3909+ }
3910+
3911+ GLuint fields_texture() const {
3912+ return fields_texture_;
3913+ }
3914+
3915+ GLuint player_brightness_texture() const {
3916+ return brightness_texture_;
3917+ }
3918+
3919+ GLint road_texture_object() const {
3920+ return road_texture_object_;
3921+ }
3922+
3923+ GLuint road_textures_buffer_object() const {
3924+ return road_textures_.object();
3925+ }
3926+
3927+ GLuint terrain_data_buffer_object() const {
3928+ return terrain_data_.object();
3929+ }
3930+
3931+ GLuint minimap_texture() const {
3932+ return minimap_texture_;
3933+ }
3934+
3935+ GLuint terrain_color_texture() const {
3936+ return terrain_color_texture_;
3937+ }
3938+
3939+ GLuint player_color_texture() const {
3940+ return player_color_texture_;
3941+ }
3942+
3943+ // Get the index into the road textures array stored in the road textures
3944+ // buffer.
3945+ unsigned road_texture_idx(Widelands::PlayerNumber owner,
3946+ Widelands::RoadType road_type,
3947+ const Widelands::Coords& coords,
3948+ Widelands::WalkingDir direction) const;
3949+
3950+ // Mark regions/types of information that need to be updated.
3951+ void update(int minfx, int maxfx, int minfy, int maxfy);
3952+ void update_minimap();
3953+
3954+private:
3955+ TerrainInformationGl4(const Widelands::EditorGameBase& egbase,
3956+ const Widelands::Player* player);
3957+
3958+ using GlobalKey = std::pair<const Widelands::EditorGameBase*, const Widelands::Player*>;
3959+ using GlobalMap = std::map<GlobalKey, std::weak_ptr<TerrainInformationGl4>>;
3960+
3961+ void do_prepare_frame();
3962+ void fields_update();
3963+ void upload_road_textures();
3964+ void upload_terrain_data();
3965+ void brightness_update();
3966+ void do_update_minimap();
3967+ void upload_constant_textures();
3968+
3969+ struct PerTerrainData {
3970+ Vector2f offset;
3971+ int dither_layer;
3972+ float padding[1];
3973+ };
3974+ static_assert(sizeof(PerTerrainData) == 16, "incorrect padding");
3975+
3976+ struct PerFieldData {
3977+ uint8_t terrain_r;
3978+ uint8_t terrain_d;
3979+ uint8_t height;
3980+
3981+ // Will be interpreted as unsigned by the texel fetch in the shader.
3982+ int8_t brightness;
3983+ };
3984+ static_assert(sizeof(PerFieldData) == 4, "incorrect padding");
3985+
3986+ struct PerRoadTextureData {
3987+ float x, y, w, h;
3988+
3989+ PerRoadTextureData(const Rectf& rect);
3990+ };
3991+ static_assert(sizeof(PerRoadTextureData) == 16, "incorrect padding");
3992+
3993+ struct PlayerRoads {
3994+ unsigned normal_roads = 0;
3995+ unsigned num_normal_roads = 1;
3996+ unsigned busy_roads = 0;
3997+ unsigned num_busy_roads = 1;
3998+ };
3999+
4000+ static GlobalMap global_map_;
4001+
4002+ const Widelands::EditorGameBase& egbase_;
4003+ const Widelands::Player* player_;
4004+ uint32_t fields_base_version_;
4005+ uint32_t terrain_vision_version_;
4006+ std::vector<Recti> update_;
4007+ bool updated_minimap_;
4008+ bool need_update_minimap_;
4009+ unsigned minimap_update_next_;
4010+
4011+ Gl::StreamingBuffer<uint8_t> uploads_;
4012+
4013+ // The texture containing per-field information.
4014+ GLuint fields_texture_;
4015+
4016+ // Brightness texture: GL_R8.
4017+ GLuint brightness_texture_;
4018+ bool brightness_see_all_;
4019+
4020+ // Road textures information
4021+ Gl::Buffer<PerRoadTextureData> road_textures_;
4022+ std::vector<PlayerRoads> player_roads_;
4023+ GLuint road_texture_object_;
4024+
4025+ // Uniform buffer with per-terrain information.
4026+ Gl::Buffer<PerTerrainData> terrain_data_;
4027+
4028+ // Texture containing additional, minimap-only information.
4029+ GLuint minimap_texture_;
4030+
4031+ // Texture containing terrain colors for minimap.
4032+ GLuint terrain_color_texture_;
4033+
4034+ // Texture containing player colors.
4035+ GLuint player_color_texture_;
4036+
4037+ DISALLOW_COPY_AND_ASSIGN(TerrainInformationGl4);
4038+};
4039+
4040+class TerrainProgramGl4 {
4041+public:
4042+ // The patch height must be even.
4043+ static constexpr unsigned kPatchWidth = 8;
4044+ static constexpr unsigned kPatchHeight = 8;
4045+
4046+public:
4047+ TerrainProgramGl4();
4048+ ~TerrainProgramGl4();
4049+
4050+ static bool supported();
4051+
4052+ // Draws the terrain.
4053+ void draw(const TerrainGl4Arguments* args,
4054+ float z_value);
4055+
4056+ // Draws a mini-map.
4057+ void draw_minimap(const TerrainGl4Arguments* args,
4058+ float z_value);
4059+
4060+ // Draw roads.
4061+ void draw_roads(const TerrainGl4Arguments* args, float z_value);
4062+
4063+private:
4064+ void init_vertex_data();
4065+ void upload_terrain_data(const TerrainGl4Arguments* args, uint32_t gametime);
4066+ unsigned upload_instance_data(const TerrainGl4Arguments* args);
4067+
4068+ void setup_road_index_buffer(unsigned num_roads);
4069+ void upload_road_data(const TerrainGl4Arguments* args);
4070+
4071+ struct PerInstanceData {
4072+ Vector2i coordinate;
4073+ };
4074+ static_assert(sizeof(PerInstanceData) == 8, "incorrect padding");
4075+
4076+ struct PerVertexData {
4077+ Vector2i vertex_coordinate;
4078+ Vector2i triangle_coordinate;
4079+
4080+ PerVertexData(int vx, int vy, int tx, int ty, bool r, uint dither_vid)
4081+ : vertex_coordinate(vx, vy), triangle_coordinate((tx << 3) | (dither_vid << 1) | r, ty) {
4082+ }
4083+ };
4084+ static_assert(sizeof(PerVertexData) == 16, "incorrect padding");
4085+
4086+ struct PerRoadData {
4087+ Vector2i start;
4088+ uint32_t direction;
4089+ uint32_t texture;
4090+
4091+ PerRoadData(const Vector2i& start_, uint32_t direction_, uint32_t texture_)
4092+ : start(start_), direction(direction_), texture(texture_) {
4093+ }
4094+ };
4095+ static_assert(sizeof(PerRoadData) == 16, "incorrect padding");
4096+
4097+ struct Terrain {
4098+ Terrain();
4099+ ~Terrain();
4100+
4101+ // The program used for drawing the terrain.
4102+ Gl::Program gl_program;
4103+
4104+ // Per-instance/patch data.
4105+ Gl::StreamingBuffer<PerInstanceData> instance_data;
4106+
4107+ // Per-vertex data.
4108+ Gl::Buffer<PerVertexData> vertex_data;
4109+
4110+ std::unique_ptr<Texture> dither_mask;
4111+
4112+ // Vertex attributes.
4113+ GLint in_vertex_coordinate;
4114+ GLint in_patch_coordinate;
4115+
4116+ // Uniforms.
4117+ GLint u_position_scale;
4118+ GLint u_position_offset;
4119+ GLint u_z_value;
4120+ GLint u_texture_dimensions;
4121+
4122+ GLint u_terrain_base;
4123+ GLint u_player_brightness;
4124+ GLint u_terrain_texture;
4125+ GLint u_dither_texture;
4126+
4127+ // Uniform block.
4128+ GLint block_terrains_idx;
4129+ } terrain_;
4130+
4131+ struct Roads {
4132+ Roads();
4133+ ~Roads();
4134+
4135+ // The program used for drawing the roads.
4136+ Gl::Program gl_program;
4137+
4138+ // Index (element array) buffer.
4139+ Gl::Buffer<uint16_t> gl_index_buffer;
4140+ unsigned num_index_roads;
4141+
4142+ // The per-road data buffer.
4143+ Gl::StreamingBuffer<PerRoadData> road_data;
4144+
4145+ // Uniforms.
4146+ GLint u_position_scale;
4147+ GLint u_position_offset;
4148+ GLint u_z_value;
4149+
4150+ GLint u_terrain_base;
4151+ GLint u_player_brightness;
4152+ GLint u_texture;
4153+
4154+ // Uniform block.
4155+ GLint block_textures_idx;
4156+ } roads_;
4157+
4158+ struct MiniMap {
4159+ MiniMap();
4160+ ~MiniMap();
4161+
4162+ struct VertexData {
4163+ float x, y, z;
4164+ float tx, ty;
4165+ };
4166+ static_assert(sizeof(VertexData) == 20, "incorrect padding");
4167+
4168+ // The program used for drawing the minimap.
4169+ Gl::Program gl_program;
4170+
4171+ // The vertex array.
4172+ Gl::StreamingBuffer<VertexData> vertex_data;
4173+
4174+ // Vertex attributes.
4175+ GLint in_position;
4176+ GLint in_field;
4177+
4178+ // Uniforms.
4179+ GLint u_layer_terrain;
4180+ GLint u_layer_owner;
4181+ GLint u_layer_details;
4182+
4183+ GLint u_frame_topleft;
4184+ GLint u_frame_bottomright;
4185+
4186+ GLint u_terrain_base;
4187+ GLint u_player_brightness;
4188+ GLint u_minimap_extra;
4189+ GLint u_terrain_color;
4190+ GLint u_player_color;
4191+ } minimap_;
4192+
4193+ DISALLOW_COPY_AND_ASSIGN(TerrainProgramGl4);
4194+};
4195+
4196+#endif // end of include guard: WL_GRAPHIC_GL_TERRAIN_PROGRAM_GL4_H
4197
4198=== modified file 'src/graphic/gl/utils.cc'
4199--- src/graphic/gl/utils.cc 2016-09-06 07:59:30 +0000
4200+++ src/graphic/gl/utils.cc 2017-01-07 12:36:16 +0000
4201@@ -82,6 +82,7 @@
4202 class Shader {
4203 public:
4204 Shader(GLenum type);
4205+ Shader(Shader&& other);
4206 ~Shader();
4207
4208 GLuint object() const {
4209@@ -89,11 +90,11 @@
4210 }
4211
4212 // Compiles 'source'. Throws an exception on error.
4213- void compile(const char* source);
4214+ void compile(const char* source, const char* shader_name = nullptr);
4215
4216 private:
4217- const GLenum type_;
4218- const GLuint shader_object_;
4219+ GLenum type_;
4220+ GLuint shader_object_;
4221
4222 DISALLOW_COPY_AND_ASSIGN(Shader);
4223 };
4224@@ -104,13 +105,19 @@
4225 }
4226 }
4227
4228+Shader::Shader(Shader&& other) {
4229+ type_ = other.type_;
4230+ shader_object_ = other.shader_object_;
4231+ other.shader_object_ = 0;
4232+}
4233+
4234 Shader::~Shader() {
4235 if (shader_object_) {
4236 glDeleteShader(shader_object_);
4237 }
4238 }
4239
4240-void Shader::compile(const char* source) {
4241+void Shader::compile(const char* source, const char* shader_name) {
4242 glShaderSource(shader_object_, 1, &source, nullptr);
4243
4244 glCompileShader(shader_object_);
4245@@ -123,7 +130,8 @@
4246 std::unique_ptr<char[]> infoLog(new char[infoLen]);
4247 glGetShaderInfoLog(shader_object_, infoLen, NULL, infoLog.get());
4248 throw wexception(
4249- "Error compiling %s shader:\n%s", shader_to_string(type_).c_str(), infoLog.get());
4250+ "Error compiling %s shader (%s):\n%s", shader_to_string(type_).c_str(),
4251+ shader_name ? shader_name : "unnamed", infoLog.get());
4252 }
4253 }
4254 }
4255@@ -140,17 +148,23 @@
4256 }
4257 }
4258
4259-void Program::build(const std::string& program_name) {
4260- std::string fragment_shader_source = read_file("shaders/" + program_name + ".fp");
4261- std::string vertex_shader_source = read_file("shaders/" + program_name + ".vp");
4262-
4263- vertex_shader_.reset(new Shader(GL_VERTEX_SHADER));
4264- vertex_shader_->compile(vertex_shader_source.c_str());
4265- glAttachShader(program_object_, vertex_shader_->object());
4266-
4267- fragment_shader_.reset(new Shader(GL_FRAGMENT_SHADER));
4268- fragment_shader_->compile(fragment_shader_source.c_str());
4269- glAttachShader(program_object_, fragment_shader_->object());
4270+void Program::build_vp_fp(const std::vector<std::string>& vp_names,
4271+ const std::vector<std::string>& fp_names) {
4272+ // Shader objects are marked for deletion immediately, but the GL holds
4273+ // onto them as long as they're attached to the program object.
4274+ for (const std::string& vp_name : vp_names) {
4275+ std::string vertex_shader_source = read_file("shaders/" + vp_name + ".vp");
4276+ Shader shader(GL_VERTEX_SHADER);
4277+ shader.compile(vertex_shader_source.c_str(), vp_name.c_str());
4278+ glAttachShader(program_object_, shader.object());
4279+ }
4280+
4281+ for (const std::string& fp_name : fp_names) {
4282+ std::string fragment_shader_source = read_file("shaders/" + fp_name + ".fp");
4283+ Shader shader(GL_FRAGMENT_SHADER);
4284+ shader.compile(fragment_shader_source.c_str(), fp_name.c_str());
4285+ glAttachShader(program_object_, shader.object());
4286+ }
4287
4288 glLinkProgram(program_object_);
4289
4290@@ -169,8 +183,49 @@
4291 }
4292 }
4293
4294+void Program::build(const std::string& program_name) {
4295+ build_vp_fp({program_name}, {program_name});
4296+}
4297+
4298+void Capabilities::check() {
4299+ // Reset all variables
4300+ *this = {};
4301+
4302+ const char* glsl_version_string = reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION));
4303+ int major = 0, minor = 0;
4304+
4305+ if (sscanf(glsl_version_string, "%d.%d", &major, &minor) != 2)
4306+ log("Warning: Malformed GLSL version string: %s\n", glsl_version_string);
4307+
4308+ glsl_version = major * 100 + minor;
4309+
4310+ GLint num_extensions;
4311+ glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
4312+
4313+ for (GLint i = 0; i < num_extensions; ++i) {
4314+ const char* extension = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i));
4315+
4316+#define EXTENSION(basename) \
4317+ do { \
4318+ if (!strcmp(extension, "GL_" #basename)) \
4319+ basename = true; \
4320+ } while (false)
4321+
4322+ EXTENSION(ARB_separate_shader_objects);
4323+ EXTENSION(ARB_shader_storage_buffer_object);
4324+ EXTENSION(ARB_uniform_buffer_object);
4325+
4326+#undef EXTENSION
4327+ }
4328+}
4329+
4330 State::State()
4331- : last_active_texture_(NONE), current_framebuffer_(0), current_framebuffer_texture_(0) {
4332+ : target_to_texture_(kMaxTextureTargets), last_active_texture_(NONE),
4333+ current_framebuffer_(0), current_framebuffer_texture_(0) {
4334+}
4335+
4336+void State::check_capabilities() {
4337+ caps_.check();
4338 }
4339
4340 void State::bind(const GLenum target, const GLuint texture) {
4341@@ -181,7 +236,8 @@
4342 }
4343
4344 void State::do_bind(const GLenum target, const GLuint texture) {
4345- const auto currently_bound_texture = target_to_texture_[target];
4346+ const unsigned target_idx = target - GL_TEXTURE0;
4347+ const auto currently_bound_texture = target_to_texture_[target_idx];
4348 if (currently_bound_texture == texture) {
4349 return;
4350 }
4351@@ -191,28 +247,19 @@
4352 }
4353 glBindTexture(GL_TEXTURE_2D, texture);
4354
4355- target_to_texture_[target] = texture;
4356- texture_to_target_[currently_bound_texture] = NONE;
4357- texture_to_target_[texture] = target;
4358-}
4359-
4360-void State::unbind_texture_if_bound(const GLuint texture) {
4361- if (texture == 0) {
4362- return;
4363- }
4364- const auto target = texture_to_target_[texture];
4365- if (target != 0) {
4366- do_bind(target, 0);
4367- }
4368+ target_to_texture_[target_idx] = texture;
4369 }
4370
4371 void State::delete_texture(const GLuint texture) {
4372- unbind_texture_if_bound(texture);
4373 glDeleteTextures(1, &texture);
4374
4375 if (current_framebuffer_texture_ == texture) {
4376 current_framebuffer_texture_ = 0;
4377 }
4378+ for (unsigned i = 0; i < target_to_texture_.size(); ++i) {
4379+ if (target_to_texture_[i] == texture)
4380+ target_to_texture_[i] = 0;
4381+ }
4382 }
4383
4384 void State::bind_framebuffer(const GLuint framebuffer, const GLuint texture) {
4385@@ -227,7 +274,6 @@
4386
4387 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
4388 if (framebuffer != 0) {
4389- unbind_texture_if_bound(texture);
4390 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
4391 assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
4392 }
4393
4394=== modified file 'src/graphic/gl/utils.h'
4395--- src/graphic/gl/utils.h 2016-08-04 15:49:05 +0000
4396+++ src/graphic/gl/utils.h 2017-01-07 12:36:16 +0000
4397@@ -53,11 +53,11 @@
4398 // Creates and compiles shader objects based on the corresponding files in data/shaders,
4399 // then links them into the program.
4400 void build(const std::string& program_name);
4401+ void build_vp_fp(const std::vector<std::string>& vp_names,
4402+ const std::vector<std::string>& fp_names);
4403
4404 private:
4405 const GLuint program_object_;
4406- std::unique_ptr<Shader> vertex_shader_;
4407- std::unique_ptr<Shader> fragment_shader_;
4408
4409 DISALLOW_COPY_AND_ASSIGN(Program);
4410 };
4411@@ -79,6 +79,11 @@
4412 }
4413 }
4414
4415+ // Returns the OpenGL object for direct use.
4416+ GLuint object() const {
4417+ return object_;
4418+ }
4419+
4420 // Calls glBindBuffer on the underlying buffer data.
4421 void bind() const {
4422 glBindBuffer(GL_ARRAY_BUFFER, object_);
4423@@ -99,6 +104,17 @@
4424 DISALLOW_COPY_AND_ASSIGN(Buffer);
4425 };
4426
4427+// Selected GL capabilities that are queried during initialization.
4428+struct Capabilities {
4429+ unsigned glsl_version = 0; // major * 100 + minor
4430+
4431+ bool ARB_separate_shader_objects = false;
4432+ bool ARB_shader_storage_buffer_object = false;
4433+ bool ARB_uniform_buffer_object = false;
4434+
4435+ void check();
4436+};
4437+
4438 // Some GL drivers do not remember the current pipeline state. If you rebind a
4439 // texture that has already bound to the same target, they will happily stall
4440 // the pipeline. We therefore cache the state of the GL driver in this class
4441@@ -110,28 +126,30 @@
4442 void bind_framebuffer(GLuint framebuffer, GLuint texture);
4443
4444 // Wrapper around glActiveTexture() and glBindTexture(). We never unbind a
4445- // texture, i.e. calls with texture == 0 are ignored. It costs only time and
4446- // is only needed when the bounded texture is rendered on - see
4447- // 'unbind_texture_if_bound'.
4448+ // texture, i.e. calls with texture == 0 are ignored.
4449 void bind(GLenum target, GLuint texture);
4450
4451- // Checks if the texture is bound to any target. If so, unbinds it. This is
4452- // needed before the texture is used as target for rendering.
4453- void unbind_texture_if_bound(GLuint texture);
4454-
4455 void delete_texture(GLuint texture);
4456
4457 // Calls glEnableVertexAttribArray on all 'entries' and disables all others
4458 // that are activated. 'entries' is taken by value on purpose.
4459 void enable_vertex_attrib_array(std::unordered_set<GLint> entries);
4460
4461+ const Capabilities& capabilities() const {
4462+ return caps_;
4463+ }
4464+
4465+ void check_capabilities();
4466+
4467 private:
4468- std::unordered_map<GLenum, GLuint> target_to_texture_;
4469- std::unordered_map<GLuint, GLenum> texture_to_target_;
4470+ static const unsigned kMaxTextureTargets = 16;
4471+
4472+ std::vector<GLuint> target_to_texture_;
4473 std::unordered_set<GLint> enabled_attrib_arrays_;
4474 GLenum last_active_texture_;
4475 GLuint current_framebuffer_;
4476 GLuint current_framebuffer_texture_;
4477+ Capabilities caps_;
4478
4479 State();
4480
4481
4482=== modified file 'src/graphic/image_io.cc'
4483--- src/graphic/image_io.cc 2016-08-04 15:49:05 +0000
4484+++ src/graphic/image_io.cc 2017-01-07 12:36:16 +0000
4485@@ -124,7 +124,7 @@
4486 std::unique_ptr<png_byte[]> row(new png_byte[row_size]);
4487
4488 // Write each row
4489- texture->lock();
4490+ texture->lock(Texture::Lock_Preserve);
4491
4492 // Write each row
4493 RGBAColor color;
4494
4495=== added file 'src/graphic/minimap_layer.h'
4496--- src/graphic/minimap_layer.h 1970-01-01 00:00:00 +0000
4497+++ src/graphic/minimap_layer.h 2017-01-07 12:36:16 +0000
4498@@ -0,0 +1,45 @@
4499+/*
4500+ * Copyright (C) 2010-2016 by the Widelands Development Team
4501+ *
4502+ * This program is free software; you can redistribute it and/or
4503+ * modify it under the terms of the GNU General Public License
4504+ * as published by the Free Software Foundation; either version 2
4505+ * of the License, or (at your option) any later version.
4506+ *
4507+ * This program is distributed in the hope that it will be useful,
4508+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4509+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4510+ * GNU General Public License for more details.
4511+ *
4512+ * You should have received a copy of the GNU General Public License
4513+ * along with this program; if not, write to the Free Software
4514+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
4515+ *
4516+ */
4517+
4518+#ifndef WL_GRAPHIC_MINIMAP_LAYER_H
4519+#define WL_GRAPHIC_MINIMAP_LAYER_H
4520+
4521+// Layers for selecting what do display on the minimap.
4522+enum class MiniMapLayer {
4523+ Terrain = 1,
4524+ Owner = 2,
4525+ Flag = 4,
4526+ Road = 8,
4527+ Building = 16,
4528+ Zoom2 = 32,
4529+ ViewWindow = 64,
4530+};
4531+
4532+// A bunch of operators that turn MiniMapLayer into a bitwise combinable flag class.
4533+inline MiniMapLayer operator|(MiniMapLayer left, MiniMapLayer right) {
4534+ return MiniMapLayer(static_cast<int>(left) | static_cast<int>(right));
4535+}
4536+inline int operator&(MiniMapLayer left, MiniMapLayer right) {
4537+ return static_cast<int>(left) & static_cast<int>(right);
4538+}
4539+inline MiniMapLayer operator^(MiniMapLayer left, MiniMapLayer right) {
4540+ return MiniMapLayer(static_cast<int>(left) ^ static_cast<int>(right));
4541+}
4542+
4543+#endif // end of include guard: WL_GRAPHIC_MINIMAP_LAYER_H
4544
4545=== modified file 'src/graphic/minimap_renderer.cc'
4546--- src/graphic/minimap_renderer.cc 2016-12-03 13:32:28 +0000
4547+++ src/graphic/minimap_renderer.cc 2017-01-07 12:36:16 +0000
4548@@ -24,7 +24,13 @@
4549 #include "base/macros.h"
4550 #include "economy/flag.h"
4551 #include "economy/road.h"
4552+#include "graphic/game_renderer_gl4.h"
4553+#include "graphic/gl/terrain_program_gl4.h"
4554 #include "graphic/graphic.h"
4555+#include "graphic/image_io.h"
4556+#include "graphic/render_queue.h"
4557+#include "graphic/rendertarget.h"
4558+#include "graphic/texture.h"
4559 #include "logic/field.h"
4560 #include "logic/map_objects/world/terrain_description.h"
4561 #include "logic/map_objects/world/world.h"
4562@@ -189,13 +195,118 @@
4563 owner = field.owner;
4564 }
4565
4566- if (vision > 0) {
4567- texture->set_pixel(x, y, calc_minimap_color(egbase, f, layers, owner, vision > 1));
4568- }
4569+ RGBColor color;
4570+ if (vision > 0)
4571+ color = calc_minimap_color(egbase, f, layers, owner, vision > 1);
4572+ texture->set_pixel(x, y, color);
4573 }
4574 }
4575 }
4576
4577+/**
4578+ * Mini-map renderer implementation that generates a mini-map texture in
4579+ * software and blits it.
4580+ */
4581+class MiniMapRendererSoftware : public MiniMapRenderer {
4582+public:
4583+ MiniMapRendererSoftware(const Widelands::EditorGameBase& egbase,
4584+ const Widelands::Player* player)
4585+ : MiniMapRenderer(egbase, player) {
4586+ }
4587+
4588+ void draw(RenderTarget& dst,
4589+ const Rectf& viewarea,
4590+ MiniMapType type,
4591+ MiniMapLayer layers) override {
4592+ texture_ = draw_minimap(egbase(), player(), viewarea, type, layers);
4593+ dst.blit(Vector2f(), texture_.get());
4594+ }
4595+
4596+private:
4597+ std::unique_ptr<Texture> texture_;
4598+};
4599+
4600+/**
4601+ * Mini-map renderer that delegates to the GL4 programs for mini-map drawing
4602+ * in a pixel shader.
4603+ */
4604+class MiniMapRendererGl4 : public MiniMapRenderer {
4605+public:
4606+ MiniMapRendererGl4(const Widelands::EditorGameBase& egbase,
4607+ const Widelands::Player* player)
4608+ : MiniMapRenderer(egbase, player) {
4609+ args_.terrain = TerrainInformationGl4::get(egbase, player);
4610+ }
4611+
4612+ void draw(RenderTarget& dst,
4613+ const Rectf& view_area,
4614+ MiniMapType minimap_type,
4615+ MiniMapLayer layers) override {
4616+ Surface* surface = dst.get_surface();
4617+ if (!surface)
4618+ return;
4619+
4620+ args_.terrain->update_minimap();
4621+
4622+ const Recti& bounding_rect = dst.get_rect();
4623+
4624+ // Determine the field shown at the top-left of the minimap.
4625+ const bool zoom = layers & MiniMapLayer::Zoom2;
4626+ Vector2f top_left =
4627+ minimap_pixel_to_mappixel(egbase().map(), Vector2i(0, 0), view_area, minimap_type, zoom);
4628+ const Coords node =
4629+ MapviewPixelFunctions::calc_node_and_triangle(egbase().map(), top_left.x, top_left.y).node;
4630+
4631+ args_.minimap_tl_fx = node.x;
4632+ args_.minimap_tl_fy = node.y;
4633+ args_.minimap_layers = layers;
4634+
4635+ // Calculate frame coordinates
4636+ int frame_width = view_area.w / kTriangleWidth;
4637+ int frame_height = view_area.h / kTriangleHeight;
4638+
4639+ Vector2i center_field;
4640+ switch (minimap_type) {
4641+ case MiniMapType::kStaticViewWindow:
4642+ center_field.x = node.x + egbase().map().get_width() / 2;
4643+ center_field.y = node.y + egbase().map().get_height() / 2;
4644+ break;
4645+
4646+ case MiniMapType::kStaticMap: {
4647+ Vector2f origin = view_area.center();
4648+ MapviewPixelFunctions::normalize_pix(egbase().map(), &origin);
4649+ center_field.x = origin.x / kTriangleWidth;
4650+ center_field.y = origin.y / kTriangleHeight;
4651+ break;
4652+ }
4653+ }
4654+
4655+ args_.minfx = center_field.x - frame_width / 2;
4656+ args_.minfy = center_field.y - frame_height / 2;
4657+ args_.maxfx = center_field.x + frame_width / 2;
4658+ args_.maxfy = center_field.y + frame_height / 2;
4659+
4660+ args_.surface_offset = (bounding_rect.origin() + dst.get_offset()).cast<float>();
4661+ args_.surface_width = surface->width();
4662+ args_.surface_height = surface->height();
4663+
4664+ // Enqueue the drawing.
4665+ RenderQueue::Item i;
4666+ i.program_id = RenderQueue::Program::kMiniMapGl4;
4667+ i.blend_mode = BlendMode::Copy;
4668+ i.terrain_arguments.destination_rect =
4669+ Rectf(bounding_rect.x, args_.surface_height - bounding_rect.y - bounding_rect.h,
4670+ bounding_rect.w, bounding_rect.h);
4671+ i.terrain_arguments.renderbuffer_width = args_.surface_width;
4672+ i.terrain_arguments.renderbuffer_height = args_.surface_height;
4673+ i.terrain_gl4_arguments = &args_;
4674+ RenderQueue::instance().enqueue(i);
4675+ }
4676+
4677+private:
4678+ TerrainGl4Arguments args_;
4679+};
4680+
4681 } // namespace
4682
4683 Vector2f minimap_pixel_to_mappixel(const Widelands::Map& map,
4684@@ -235,10 +346,6 @@
4685 const int16_t map_w = (layers & MiniMapLayer::Zoom2) ? map.get_width() * 2 : map.get_width();
4686 const int16_t map_h = (layers & MiniMapLayer::Zoom2) ? map.get_height() * 2 : map.get_height();
4687
4688- std::unique_ptr<Texture> texture(new Texture(map_w, map_h));
4689-
4690- texture->fill_rect(Rectf(0, 0, texture->width(), texture->height()), RGBAColor(0, 0, 0, 255));
4691-
4692 // Center the view on the middle of the 'view_area'.
4693 const bool zoom = layers & MiniMapLayer::Zoom2;
4694 Vector2f top_left =
4695@@ -246,7 +353,8 @@
4696 const Coords node =
4697 MapviewPixelFunctions::calc_node_and_triangle(map, top_left.x, top_left.y).node;
4698
4699- texture->lock();
4700+ std::unique_ptr<Texture> texture(new Texture(map_w, map_h));
4701+ texture->lock(Texture::Lock_Discard);
4702 do_draw_minimap(texture.get(), egbase, player, Vector2i(node.x, node.y), layers);
4703
4704 if (layers & MiniMapLayer::ViewWindow) {
4705@@ -256,3 +364,16 @@
4706
4707 return texture;
4708 }
4709+
4710+MiniMapRenderer::MiniMapRenderer(const Widelands::EditorGameBase& egbase,
4711+ const Widelands::Player* player)
4712+ : egbase_(egbase), player_(player) {
4713+}
4714+
4715+std::unique_ptr<MiniMapRenderer>
4716+MiniMapRenderer::create(const Widelands::EditorGameBase& egbase,
4717+ const Widelands::Player* player) {
4718+ if (TerrainProgramGl4::supported())
4719+ return std::unique_ptr<MiniMapRenderer>(new MiniMapRendererGl4(egbase, player));
4720+ return std::unique_ptr<MiniMapRenderer>(new MiniMapRendererSoftware(egbase, player));
4721+}
4722
4723=== modified file 'src/graphic/minimap_renderer.h'
4724--- src/graphic/minimap_renderer.h 2016-10-24 20:07:22 +0000
4725+++ src/graphic/minimap_renderer.h 2017-01-07 12:36:16 +0000
4726@@ -24,31 +24,16 @@
4727
4728 #include "base/rect.h"
4729 #include "base/vector.h"
4730-#include "graphic/texture.h"
4731-#include "logic/editor_game_base.h"
4732-#include "logic/map.h"
4733-#include "logic/player.h"
4734-
4735-// Layers for selecting what do display on the minimap.
4736-enum class MiniMapLayer {
4737- Terrain = 1,
4738- Owner = 2,
4739- Flag = 4,
4740- Road = 8,
4741- Building = 16,
4742- Zoom2 = 32,
4743- ViewWindow = 64,
4744-};
4745-
4746-// A bunch of operators that turn MiniMapLayer into a bitwise combinable flag class.
4747-inline MiniMapLayer operator|(MiniMapLayer left, MiniMapLayer right) {
4748- return MiniMapLayer(static_cast<int>(left) | static_cast<int>(right));
4749-}
4750-inline int operator&(MiniMapLayer left, MiniMapLayer right) {
4751- return static_cast<int>(left) & static_cast<int>(right);
4752-}
4753-inline MiniMapLayer operator^(MiniMapLayer left, MiniMapLayer right) {
4754- return MiniMapLayer(static_cast<int>(left) ^ static_cast<int>(right));
4755+#include "graphic/minimap_layer.h"
4756+
4757+class RenderTarget;
4758+class StreamWrite;
4759+class Texture;
4760+
4761+namespace Widelands {
4762+class Map;
4763+class Player;
4764+class EditorGameBase;
4765 }
4766
4767 enum class MiniMapType {
4768@@ -59,6 +44,45 @@
4769 kStaticMap,
4770 };
4771
4772+/**
4773+ * Virtual base class for mini-map renderers. Each mini-map view should own
4774+ * an instance of this class, which must be kept alive during rendering (due
4775+ * to commands sent to the RenderQueue).
4776+ */
4777+class MiniMapRenderer {
4778+public:
4779+ static std::unique_ptr<MiniMapRenderer>
4780+ create(const Widelands::EditorGameBase& egbase,
4781+ const Widelands::Player* player);
4782+
4783+ virtual ~MiniMapRenderer() {}
4784+
4785+ const Widelands::EditorGameBase& egbase() const {
4786+ return egbase_;
4787+ }
4788+
4789+ const Widelands::Player* player() const {
4790+ return player_;
4791+ }
4792+
4793+ /**
4794+ * Draw the minimap into the given destination. The @p viewpoint is the
4795+ * map field shown in the top-left corner of the minimap.
4796+ */
4797+ virtual void draw(RenderTarget& dst,
4798+ const Rectf& viewarea,
4799+ MiniMapType type,
4800+ MiniMapLayer layers) = 0;
4801+
4802+protected:
4803+ MiniMapRenderer(const Widelands::EditorGameBase& egbase,
4804+ const Widelands::Player* player);
4805+
4806+private:
4807+ const Widelands::EditorGameBase& egbase_;
4808+ const Widelands::Player* player_;
4809+};
4810+
4811 // Converts between minimap pixel and map pixel.
4812 // Remember to call 'normalize_pix' after applying the transformation.
4813 Vector2f minimap_pixel_to_mappixel(const Widelands::Map& map,
4814
4815=== modified file 'src/graphic/render_queue.cc'
4816--- src/graphic/render_queue.cc 2016-10-22 18:19:22 +0000
4817+++ src/graphic/render_queue.cc 2017-01-07 12:36:16 +0000
4818@@ -30,6 +30,7 @@
4819 #include "graphic/gl/fill_rect_program.h"
4820 #include "graphic/gl/road_program.h"
4821 #include "graphic/gl/terrain_program.h"
4822+#include "graphic/gl/terrain_program_gl4.h"
4823
4824 namespace {
4825
4826@@ -47,7 +48,7 @@
4827 // - we batch up by program to have maximal batching.
4828 // - and we want to render frontmost objects first, so that we do not render
4829 // any pixel more than once.
4830-static_assert(RenderQueue::Program::kHighestProgramId <= 8,
4831+static_assert(RenderQueue::Program::kHighestProgramId <= 16,
4832 "Need to change sorting keys."); // 4 bits.
4833
4834 uint64_t
4835@@ -139,10 +140,14 @@
4836 } // namespace
4837
4838 RenderQueue::RenderQueue()
4839- : next_z_(1),
4840- terrain_program_(new TerrainProgram()),
4841- dither_program_(new DitherProgram()),
4842- road_program_(new RoadProgram()) {
4843+ : next_z_(1) {
4844+ if (!TerrainProgramGl4::supported()) {
4845+ terrain_program_.reset(new TerrainProgram);
4846+ dither_program_.reset(new DitherProgram);
4847+ road_program_.reset(new RoadProgram);
4848+ } else {
4849+ terrain_program_gl4_.reset(new TerrainProgramGl4);
4850+ }
4851 }
4852
4853 // static
4854@@ -165,6 +170,9 @@
4855 case Program::kTerrainBase:
4856 case Program::kTerrainDither:
4857 case Program::kTerrainRoad:
4858+ case Program::kTerrainGl4:
4859+ case Program::kTerrainRoadGl4:
4860+ case Program::kMiniMapGl4:
4861 /* all fallthroughs intended */
4862 break;
4863
4864@@ -191,6 +199,8 @@
4865 throw wexception("Too many drawn layers. Ran out of z-values.");
4866 }
4867
4868+ TerrainInformationGl4::prepare_frame();
4869+
4870 Gl::State::instance().bind_framebuffer(0, 0);
4871 glViewport(0, 0, screen_width, screen_height);
4872
4873@@ -256,6 +266,27 @@
4874 ++i;
4875 } break;
4876
4877+ case Program::kTerrainGl4: {
4878+ ScopedScissor scoped_scissor(item.terrain_arguments.destination_rect);
4879+ terrain_program_gl4_->draw(item.terrain_gl4_arguments,
4880+ item.z_value);
4881+ ++i;
4882+ } break;
4883+
4884+ case Program::kTerrainRoadGl4: {
4885+ ScopedScissor scoped_scissor(item.terrain_arguments.destination_rect);
4886+ terrain_program_gl4_->draw_roads(item.terrain_gl4_arguments,
4887+ item.z_value);
4888+ ++i;
4889+ } break;
4890+
4891+ case Program::kMiniMapGl4: {
4892+ ScopedScissor scoped_scissor(item.terrain_arguments.destination_rect);
4893+ terrain_program_gl4_->draw_minimap(item.terrain_gl4_arguments,
4894+ item.z_value);
4895+ ++i;
4896+ } break;
4897+
4898 default:
4899 throw wexception("Unknown item.program_id: %d", item.program_id);
4900 }
4901
4902=== modified file 'src/graphic/render_queue.h'
4903--- src/graphic/render_queue.h 2016-10-22 11:20:33 +0000
4904+++ src/graphic/render_queue.h 2017-01-07 12:36:16 +0000
4905@@ -38,6 +38,8 @@
4906 class DitherProgram;
4907 class RoadProgram;
4908 class TerrainProgram;
4909+class TerrainProgramGl4;
4910+struct TerrainGl4Arguments;
4911
4912 // The RenderQueue is a singleton implementing the concept of deferred
4913 // rendering: Every rendering call that pretends to draw onto the screen will
4914@@ -83,6 +85,9 @@
4915 kTerrainBase,
4916 kTerrainDither,
4917 kTerrainRoad,
4918+ kTerrainGl4,
4919+ kTerrainRoadGl4,
4920+ kMiniMapGl4,
4921 kBlit,
4922 kRect,
4923 kLine,
4924@@ -120,7 +125,7 @@
4925 int renderbuffer_width;
4926 int renderbuffer_height;
4927 const DescriptionMaintainer<Widelands::TerrainDescription>* terrains;
4928- FieldsToDraw* fields_to_draw;
4929+ FieldsToDrawGl2* fields_to_draw;
4930 float scale;
4931 Rectf destination_rect;
4932 };
4933@@ -156,6 +161,7 @@
4934 TerrainArguments terrain_arguments;
4935 RectArguments rect_arguments;
4936 LineArguments line_arguments;
4937+ TerrainGl4Arguments* terrain_gl4_arguments;
4938 };
4939
4940 static RenderQueue& instance();
4941@@ -179,6 +185,7 @@
4942 int next_z_;
4943
4944 std::unique_ptr<TerrainProgram> terrain_program_;
4945+ std::unique_ptr<TerrainProgramGl4> terrain_program_gl4_;
4946 std::unique_ptr<DitherProgram> dither_program_;
4947 std::unique_ptr<RoadProgram> road_program_;
4948
4949
4950=== modified file 'src/graphic/texture.cc'
4951--- src/graphic/texture.cc 2016-10-24 14:07:28 +0000
4952+++ src/graphic/texture.cc 2017-01-07 12:36:16 +0000
4953@@ -189,7 +189,7 @@
4954 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, static_cast<GLint>(GL_LINEAR));
4955 }
4956
4957-void Texture::lock() {
4958+void Texture::lock(LockMode mode) {
4959 if (blit_data_.texture_id == 0) {
4960 return;
4961 }
4962@@ -203,8 +203,10 @@
4963
4964 pixels_.reset(new uint8_t[width() * height() * 4]);
4965
4966- Gl::State::instance().bind(GL_TEXTURE0, blit_data_.texture_id);
4967- glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels_.get());
4968+ if (mode == Lock_Preserve) {
4969+ Gl::State::instance().bind(GL_TEXTURE0, blit_data_.texture_id);
4970+ glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels_.get());
4971+ }
4972 }
4973
4974 void Texture::unlock(UnlockMode mode) {
4975
4976=== modified file 'src/graphic/texture.h'
4977--- src/graphic/texture.h 2016-11-03 07:20:57 +0000
4978+++ src/graphic/texture.h 2017-01-07 12:36:16 +0000
4979@@ -50,6 +50,25 @@
4980 // Implements Image.
4981 const BlitData& blit_data() const override;
4982
4983+ enum LockMode {
4984+ /**
4985+ * Previously existing pixel data will be discarded.
4986+ *
4987+ * The contents of the texture will be undefined unless all pixels
4988+ * values are explicitly set and \ref unlock is called in Unlock_Update
4989+ * mode.
4990+ */
4991+ Lock_Discard = 0,
4992+
4993+ /**
4994+ * The existing data in the texture will be preserved.
4995+ *
4996+ * Avoid this when possible, since the texture may have to be
4997+ * re-downloaded from the GPU which involves a graphics pipeline stall.
4998+ */
4999+ Lock_Preserve
5000+ };
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to status/vote changes: