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
=== modified file 'cmake/codecheck/rules/correct_include_order'
--- cmake/codecheck/rules/correct_include_order 2015-04-04 15:03:14 +0000
+++ cmake/codecheck/rules/correct_include_order 2017-01-07 12:36:16 +0000
@@ -45,9 +45,10 @@
45 delims = set(entry[1] for entry in block)45 delims = set(entry[1] for entry in block)
46 if len(delims) != 1:46 if len(delims) != 1:
47 errors.append((fn, block[0][0], """Use either '"' or '<' for all includes in a block."""))47 errors.append((fn, block[0][0], """Use either '"' or '<' for all includes in a block."""))
48 if sorted(includes) != includes:48 for include_sorted, include_actual in zip(sorted(includes), includes):
49 errors.append((fn, block[0][0], "Include block is not sorted alphabetically."))49 if include_sorted != include_actual:
50 return errors50 errors.append((fn, block[0][0], "Include block is not sorted alphabetically: '%s' must be included before '%s'" % (include_sorted, include_actual)))
51 return errors
5152
52 if ".cc" in fn:53 if ".cc" in fn:
53 base_file = os.path.basename(fn)[:-3]54 base_file = os.path.basename(fn)[:-3]
5455
=== modified file 'data/shaders/blit.fp'
--- data/shaders/blit.fp 2016-02-01 10:24:34 +0000
+++ data/shaders/blit.fp 2017-01-07 12:36:16 +0000
@@ -3,13 +3,12 @@
3uniform sampler2D u_texture;3uniform sampler2D u_texture;
4uniform sampler2D u_mask;4uniform sampler2D u_mask;
55
6varying vec2 out_mask_texture_coordinate;6varying vec4 out_texture_coordinates;
7varying vec2 out_texture_coordinate;
8varying vec4 out_blend;7varying vec4 out_blend;
9varying float out_program_flavor;8varying float out_program_flavor;
109
11void main() {10void main() {
12 vec4 texture_color = texture2D(u_texture, out_texture_coordinate);11 vec4 texture_color = texture2D(u_texture, out_texture_coordinates.xy);
1312
14 // See http://en.wikipedia.org/wiki/YUV.13 // See http://en.wikipedia.org/wiki/YUV.
15 float luminance = dot(vec3(0.299, 0.587, 0.114), texture_color.rgb);14 float luminance = dot(vec3(0.299, 0.587, 0.114), texture_color.rgb);
@@ -19,7 +18,7 @@
19 } else if (out_program_flavor == 1.) {18 } else if (out_program_flavor == 1.) {
20 gl_FragColor = vec4(vec3(luminance) * out_blend.rgb, out_blend.a * texture_color.a);19 gl_FragColor = vec4(vec3(luminance) * out_blend.rgb, out_blend.a * texture_color.a);
21 } else {20 } else {
22 vec4 mask_color = texture2D(u_mask, out_mask_texture_coordinate);21 vec4 mask_color = texture2D(u_mask, out_texture_coordinates.zw);
23 float blend_influence = mask_color.r * mask_color.a;22 float blend_influence = mask_color.r * mask_color.a;
24 gl_FragColor = vec4(23 gl_FragColor = vec4(
25 mix(texture_color.rgb, out_blend.rgb * luminance, blend_influence),24 mix(texture_color.rgb, out_blend.rgb * luminance, blend_influence),
2625
=== modified file 'data/shaders/blit.vp'
--- data/shaders/blit.vp 2016-02-01 10:24:34 +0000
+++ data/shaders/blit.vp 2017-01-07 12:36:16 +0000
@@ -7,14 +7,15 @@
7attribute vec4 attr_blend;7attribute vec4 attr_blend;
8attribute float attr_program_flavor;8attribute float attr_program_flavor;
99
10varying vec2 out_mask_texture_coordinate;10// xy = texture_coordinate; zw = mask_texture_coordinate
11varying vec2 out_texture_coordinate;11// Sharing a single varying in this way can save hardware resources.
12varying vec4 out_texture_coordinates;
12varying vec4 out_blend;13varying vec4 out_blend;
13varying float out_program_flavor;14varying float out_program_flavor;
1415
15void main() {16void main() {
16 out_mask_texture_coordinate = attr_mask_texture_position;17 out_texture_coordinates.zw = attr_mask_texture_position;
17 out_texture_coordinate = attr_texture_position;18 out_texture_coordinates.xy = attr_texture_position;
18 out_blend = attr_blend;19 out_blend = attr_blend;
19 out_program_flavor = attr_program_flavor;20 out_program_flavor = attr_program_flavor;
20 gl_Position = vec4(attr_position, 1.);21 gl_Position = vec4(attr_position, 1.);
2122
=== added file 'data/shaders/blit_gl4.vp'
--- data/shaders/blit_gl4.vp 1970-01-01 00:00:00 +0000
+++ data/shaders/blit_gl4.vp 2017-01-07 12:36:16 +0000
@@ -0,0 +1,70 @@
1#version 130
2#extension GL_ARB_separate_shader_objects : enable
3#extension GL_ARB_shader_storage_buffer_object : enable
4#extension GL_ARB_uniform_buffer_object : enable
5
6// Varyings.
7out vec4 out_texture_coordinates;
8out vec4 out_blend;
9out float out_program_flavor;
10
11struct Rect {
12 float dst_x, dst_y, dst_width, dst_height;
13 uint src_x, src_y, src_width, src_height;
14 uint src_parent_width, src_parent_height;
15 uint mask_x, mask_y, mask_width, mask_height;
16 uint mask_parent_width, mask_parent_height;
17 uint blend_r, blend_g, blend_b, blend_a;
18 float program_flavor, z;
19};
20
21layout(std140, binding=0) buffer ssbo_rects {
22 Rect rects[];
23};
24
25void main() {
26 // Determine rect and vertex relative to rect
27 uint rect_idx = uint(gl_VertexID) >> 2;
28 uint vertex_idx = uint(gl_VertexID) & 3u;
29 Rect r = rects[rect_idx];
30
31 out_program_flavor = r.program_flavor;
32
33 // Position
34 gl_Position = vec4(r.dst_x, r.dst_y, r.z, 1.);
35 if ((vertex_idx & 1u) != 0u)
36 gl_Position.x += r.dst_width;
37 if ((vertex_idx & 2u) != 0u)
38 gl_Position.y += r.dst_height;
39
40 // Texture coordinate
41 uint tx = r.src_x;
42 uint ty = r.src_y;
43 if ((vertex_idx & 1u) != 0u)
44 tx += r.src_width;
45 if ((vertex_idx & 2u) == 0u)
46 ty += r.src_height;
47 out_texture_coordinates.x = tx * (1. / r.src_parent_width);
48 out_texture_coordinates.y = 1.0 - ty * (1. / r.src_parent_height);
49
50 // Blending
51 out_blend.a = r.blend_a * (1. / 255.);
52
53// if (out_program_flavor >= 1.) {
54 out_blend.r = r.blend_r * (1. / 255.);
55 out_blend.g = r.blend_g * (1. / 255.);
56 out_blend.b = r.blend_b * (1. / 255.);
57
58// if (out_program_flavor >= 2.) {
59 // Mask texture coordinate
60 tx = r.mask_x;
61 ty = r.mask_y;
62 if ((vertex_idx & 1u) != 0u)
63 tx += r.mask_width;
64 if ((vertex_idx & 2u) == 0u)
65 ty += r.mask_height;
66 out_texture_coordinates.z = tx * (1. / r.mask_parent_width);
67 out_texture_coordinates.w = 1.0 - ty * (1. / r.mask_parent_height);
68// }
69// }
70}
071
=== added file 'data/shaders/minimap_gl4.fp'
--- data/shaders/minimap_gl4.fp 1970-01-01 00:00:00 +0000
+++ data/shaders/minimap_gl4.fp 2017-01-07 12:36:16 +0000
@@ -0,0 +1,108 @@
1#version 130
2#extension GL_ARB_uniform_buffer_object: enable
3
4// Varyings
5in vec2 var_field;
6
7// Uniforms
8uniform bool u_layer_terrain;
9uniform bool u_layer_owner;
10uniform uint u_layer_details;
11
12uniform vec2 u_frame_topleft;
13uniform vec2 u_frame_bottomright;
14
15// Textures (map data).
16uniform usampler2D u_terrain_base;
17uniform sampler2D u_player_brightness;
18uniform usampler2D u_minimap_extra;
19uniform sampler2D u_terrain_color;
20uniform sampler2D u_player_color;
21
22float calc_node_brightness(uint node_ubrightness) {
23 // Brightness is really an 8-bit signed value, but it's stored in an
24 // GL_RGBA8UI texture, so here we use signed (arithmetic) shifts to do
25 // the conversion.
26 int node_brightness = int(node_ubrightness << 24) >> 24;
27 float brightness = 144. / 255. + node_brightness * (1. / 255.);
28 brightness = min(1., brightness * (255. / 160.));
29 return brightness;
30}
31
32// Return true if a and b are closer than threshold modulo 1.
33bool wrap_close(float a, float b, float threshold) {
34 float dist = a - b;
35 dist -= floor(dist + 0.5);
36 return abs(dist) < threshold;
37}
38
39void main() {
40 float player_brightness = texture(u_player_brightness, var_field).r;
41
42 // Determine whether we're on the frame
43 bool on_frame = false;
44 float dfdx = abs(dFdx(var_field.x)) * 0.5;
45 float dfdy = abs(dFdy(var_field.y)) * 0.5;
46 float low, high, pix, width;
47
48 if (wrap_close(var_field.x, u_frame_topleft.x, dfdx) ||
49 wrap_close(var_field.x, u_frame_bottomright.x, dfdx)) {
50 on_frame = true;
51 low = u_frame_topleft.y;
52 high = u_frame_bottomright.y;
53 pix = var_field.y;
54 width = dfdy;
55 } else if (wrap_close(var_field.y, u_frame_topleft.y, dfdy) ||
56 wrap_close(var_field.y, u_frame_bottomright.y, dfdy)) {
57 on_frame = true;
58 low = u_frame_topleft.x;
59 high = u_frame_bottomright.x;
60 pix = var_field.x;
61 width = dfdx;
62 }
63
64 if (on_frame) {
65 pix -= floor(pix - low); // Normalize to range [low, low + 1)
66 if (pix <= high &&
67 (int((pix - low) / (2 * width)) & 1) == 0) {
68 gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
69 return;
70 }
71 }
72
73 // Determine minimap color
74 vec3 color = vec3(0.0, 0.0, 0.0);
75 if (player_brightness > 0.0) {
76 if (u_layer_terrain) {
77 uvec4 node = texture(u_terrain_base, var_field);
78 float brightness = calc_node_brightness(node.w);
79
80 color = texelFetch(u_terrain_color, ivec2(node.y, 0), 0).rgb;
81 color *= brightness;
82 }
83
84 if (u_layer_owner || u_layer_details != 0u) {
85 uint extra = texture(u_minimap_extra, var_field).r;
86
87 if (u_layer_owner) {
88 uint owner = extra & 0x3fu;
89 if (owner > 0u) {
90 vec3 player_color = texelFetch(u_player_color, ivec2(owner - 1u, 0), 0).rgb;
91 color = mix(color, player_color, 0.5);
92 }
93 }
94
95 uint detail = extra >> 6u;
96 if ((u_layer_details & 1u) != 0u && detail == 1u) {
97 // Road
98 color = mix(color, vec3(1, 1, 1), 0.5);
99 } else if (detail != 0u && (u_layer_details & (1u << (detail - 1u))) != 0u) {
100 // Flag or building
101 color = vec3(1, 1, 1);
102 }
103 }
104 }
105
106 gl_FragColor.rgb = color;
107 gl_FragColor.w = 1.0;
108}
0109
=== added file 'data/shaders/minimap_gl4.vp'
--- data/shaders/minimap_gl4.vp 1970-01-01 00:00:00 +0000
+++ data/shaders/minimap_gl4.vp 2017-01-07 12:36:16 +0000
@@ -0,0 +1,11 @@
1#version 130
2
3in vec3 in_position;
4in vec2 in_field; // Fractional field
5
6out vec2 var_field;
7
8void main() {
9 gl_Position = vec4(in_position, 1.);
10 var_field = in_field;
11}
012
=== added file 'data/shaders/road_gl4.vp'
--- data/shaders/road_gl4.vp 1970-01-01 00:00:00 +0000
+++ data/shaders/road_gl4.vp 2017-01-07 12:36:16 +0000
@@ -0,0 +1,89 @@
1#version 130
2#extension GL_ARB_separate_shader_objects : enable
3#extension GL_ARB_shader_storage_buffer_object : enable
4#extension GL_ARB_uniform_buffer_object : enable
5
6// From terrain_common_gl4:
7void init_common();
8uvec4 get_field_base(ivec2 coord);
9float calc_node_brightness(uint node_ubrightness);
10float calc_brightness(ivec2 coord, uint node_ubrightness);
11void calc_basepix(ivec2 coord, out vec2 basepix, out float heightpix, out uint node_brightness);
12void calc_pix(ivec2 coord, out vec2 pix, out uint node_brightness);
13
14// Attributes.
15uniform vec2 u_position_scale;
16uniform vec2 u_position_offset;
17uniform float u_z_value;
18
19// Road data.
20struct Road {
21 ivec2 start;
22 uint direction; // WALK_E = 2, WALK_SE = 3, WALK_SW = 4
23 uint texture;
24};
25
26layout(std140, binding=0) buffer ssbo_roads {
27 Road u_roads[];
28};
29
30// Road texture information.
31struct Texture {
32 float x, y, w, h;
33};
34
35layout(std140) uniform block_textures {
36 Texture u_textures[128];
37};
38
39// Outputs.
40varying vec2 out_texture_position;
41varying float out_brightness;
42
43// Constants
44const float kRoadThicknessInPixels = 5.;
45const float kRoadElongationFraction = .1;
46
47void main() {
48 init_common();
49
50 Road road = u_roads[gl_VertexID >> 2];
51 bool is_end = (gl_VertexID & 2) != 0;
52 bool is_right = (gl_VertexID & 1) != 0;
53
54 // Calculate end coordinate.
55 ivec2 end = road.start;
56 end.x += 3 - int(road.direction);
57 if (road.direction >= 3u) {
58 end.y += 1;
59 end.x += (road.start.y & 1) != 0 ? 1 : 0;
60 }
61
62 // Map coordinates
63 vec2 start_pix, end_pix;
64 uint start_node_brightness, end_node_brightness;
65 calc_pix(road.start, start_pix, start_node_brightness);
66 calc_pix(end, end_pix, end_node_brightness);
67
68 vec2 delta_pix = end_pix - start_pix;
69 vec2 road_overshoot = delta_pix * kRoadElongationFraction;
70 vec2 road_thickness = vec2(-delta_pix.y, delta_pix.x) * (kRoadThicknessInPixels / length(delta_pix));
71
72 vec2 pix =
73 (is_end ? end_pix + road_overshoot : start_pix - road_overshoot) +
74 (is_right ? -road_thickness : road_thickness);
75 gl_Position = vec4(pix * u_position_scale + u_position_offset, u_z_value, 1.);
76
77 // Brightness
78 out_brightness = calc_brightness(
79 is_end ? end : road.start,
80 is_end ? end_node_brightness : start_node_brightness);
81
82 // Texture coordinates.
83 Texture tex = u_textures[road.texture];
84 out_texture_position = vec2(tex.x, tex.y);
85 if (is_end)
86 out_texture_position.x += tex.w;
87 if (!is_right)
88 out_texture_position.y += tex.h;
89}
090
=== added file 'data/shaders/terrain_common_gl4.vp'
--- data/shaders/terrain_common_gl4.vp 1970-01-01 00:00:00 +0000
+++ data/shaders/terrain_common_gl4.vp 2017-01-07 12:36:16 +0000
@@ -0,0 +1,59 @@
1// Helper functions that are shared by the vertex shaders for terrain and
2// roads.
3#version 130
4#extension GL_ARB_uniform_buffer_object: enable
5
6// Textures (map data).
7uniform usampler2D u_terrain_base;
8uniform sampler2D u_player_brightness;
9
10// Constants
11const float kTriangleWidth = 64;
12const float kTriangleHeight = 32;
13const float kHeightFactor = 5;
14
15vec2 map_size;
16vec2 map_inv_size;
17
18void init_common() {
19 map_size = vec2(textureSize(u_terrain_base, 0));
20 map_inv_size = 1. / map_size;
21}
22
23uvec4 get_field_base(ivec2 coord) {
24 return texture(u_terrain_base, (coord + 0.5) * map_inv_size);
25}
26
27float calc_node_brightness(uint node_ubrightness) {
28 // Brightness is really an 8-bit signed value, but it's stored in an
29 // GL_RGBA8UI texture, so here we use signed (arithmetic) shifts to do
30 // the conversion.
31 int node_brightness = int(node_ubrightness << 24) >> 24;
32 float brightness = 144. / 255. + node_brightness * (1. / 255.);
33 brightness = min(1., brightness * (255. / 160.));
34 return brightness;
35}
36
37float calc_brightness(ivec2 coord, uint node_ubrightness) {
38 float brightness = calc_node_brightness(node_ubrightness);
39
40 brightness *= texture(u_player_brightness, (coord + 0.5) * map_inv_size).r;
41
42 return brightness;
43}
44
45void calc_basepix(ivec2 coord, out vec2 basepix, out float heightpix, out uint node_brightness) {
46 uvec4 field = get_field_base(coord);
47 basepix.x = coord.x * kTriangleWidth;
48 if ((coord.y & 1) != 0)
49 basepix.x += kTriangleWidth / 2;
50 basepix.y = coord.y * kTriangleHeight;
51 heightpix = field.z * kHeightFactor;
52 node_brightness = field.w;
53}
54
55void calc_pix(ivec2 coord, out vec2 pix, out uint node_brightness) {
56 float heightpix;
57 calc_basepix(coord, pix, heightpix, node_brightness);
58 pix.y -= heightpix;
59}
060
=== added file 'data/shaders/terrain_gl4.fp'
--- data/shaders/terrain_gl4.fp 1970-01-01 00:00:00 +0000
+++ data/shaders/terrain_gl4.fp 2017-01-07 12:36:16 +0000
@@ -0,0 +1,58 @@
1#version 130
2
3uniform sampler2D u_terrain_texture;
4uniform sampler2D u_dither_texture;
5
6uniform vec2 u_texture_dimensions;
7
8in vec4 var_texture;
9in vec4 var_dither[3];
10in float var_brightness;
11
12// TODO(sirver): This is a hack to make sure we are sampling inside of the
13// terrain texture. This is a common problem with OpenGL and texture atlases.
14#define MARGIN 1e-2
15
16vec3 sample_terrain(vec2 offset, vec2 texcoord) {
17 // The arbitrary multiplication by 0.99 makes sure that we never sample
18 // outside of the texture in the texture atlas - this means non-perfect
19 // pixel mapping of textures to the screen, but we are pretty meh about that
20 // here.
21 vec2 texture_fract = clamp(
22 fract(texcoord),
23 vec2(MARGIN, MARGIN),
24 vec2(1. - MARGIN, 1. - MARGIN));
25 return texture2D(u_terrain_texture, offset + u_texture_dimensions * texture_fract).rgb;
26}
27
28vec3 apply_dither(vec3 color, vec2 texcoord, vec4 dither) {
29 vec2 offset = dither.xy;
30 vec2 dither_tc = dither.zw;
31
32 // Cut-off value to avoid unnecessary texture samples. The cut-off value
33 // is chosen based on the dither mask, which happens to be 1 in 3/4 of the
34 // texture.
35 //
36 // Note that cuting off in this way would be slightly incorrect if mipmaps
37 // were used, because derivatives become undefined under non-uniform
38 // control flow.
39 if (dither_tc.y < 0.75)
40 return color;
41
42 float dither_factor = texture2D(u_dither_texture, dither_tc).a;
43 vec3 dither_color = sample_terrain(offset, texcoord);
44
45 // dither_factor is 0 when the other texture replace the base texture
46 // entirely, and 1 when the base texture is not replaced at all.
47 return mix(dither_color, color, dither_factor);
48}
49
50void main() {
51 vec2 texcoord = var_texture.zw;
52 vec3 clr = sample_terrain(var_texture.xy, texcoord);
53 clr = apply_dither(clr, texcoord, var_dither[0]);
54 clr = apply_dither(clr, texcoord, var_dither[1]);
55 clr = apply_dither(clr, texcoord, var_dither[2]);
56 clr *= var_brightness;
57 gl_FragColor = vec4(clr, 1.);
58}
059
=== added file 'data/shaders/terrain_gl4.vp'
--- data/shaders/terrain_gl4.vp 1970-01-01 00:00:00 +0000
+++ data/shaders/terrain_gl4.vp 2017-01-07 12:36:16 +0000
@@ -0,0 +1,157 @@
1#version 130
2#extension GL_ARB_uniform_buffer_object: enable
3
4// From terrain_common_gl4:
5void init_common();
6uvec4 get_field_base(ivec2 coord);
7float calc_brightness(ivec2 coord, uint node_ubrightness);
8void calc_basepix(ivec2 coord, out vec2 basepix, out float heightpix, out uint node_brightness);
9void calc_pix(ivec2 coord, out vec2 pix, out uint node_brightness);
10
11// Per-vertex Attributes.
12// X,Y: relative vertex map coordinate
13// Z,W: relative triangle map coordinate
14// low bit of Z: 0 for d-triangle, 1 for r-triangle
15// bits 1&2 of Z: vertex ID for dithering
16in ivec4 in_vertex_coordinate;
17
18// Per-instance/patch Attributes.
19in ivec2 in_patch_coordinate;
20
21// Primary Constants.
22uniform vec2 u_position_scale;
23uniform vec2 u_position_offset;
24uniform float u_z_value;
25
26// Terrain information (secondary constants in a uniform buffer).
27struct Terrain {
28 vec2 offset;
29 int dither_layer;
30};
31
32layout(std140) uniform block_terrains {
33 Terrain u_terrains[128];
34};
35
36// Varyings: Output of vertex shader.
37
38// X,Y: offset into texture atlas
39// Z,W: texture coordinate
40out vec4 var_texture;
41
42// X,Y: offset into texture atlas
43// Z,W: dither texture coordinate
44out vec4 var_dither[3];
45
46out float var_brightness;
47
48// Constants
49const float kTextureSideLength = 64;
50
51void main() {
52 init_common();
53
54 // Compute coordinate of field that owns the triangle to which this
55 // vertex belongs.
56 ivec2 field_coord;
57 field_coord.x = in_patch_coordinate.x + (in_vertex_coordinate.z >> 3);
58 field_coord.y = in_patch_coordinate.y + in_vertex_coordinate.w;
59
60 uvec4 field = get_field_base(field_coord);
61 bool is_down = (in_vertex_coordinate.z & 1) == 0;
62 uint dither_vid = uint((in_vertex_coordinate.z >> 1) & 3);
63 uint terrain = is_down ? field.y : field.x;
64
65 ivec2 node_coord = in_patch_coordinate.xy + in_vertex_coordinate.xy;
66 uint node_brightness;
67 float node_heightpix;
68 vec2 vertex_basepix;
69
70 calc_basepix(node_coord, vertex_basepix, node_heightpix, node_brightness);
71 // On-screen position
72 gl_Position.x = vertex_basepix.x;
73 gl_Position.y = vertex_basepix.y - node_heightpix;
74 gl_Position.xy = gl_Position.xy * u_position_scale + u_position_offset;
75 gl_Position.z = u_z_value;
76 gl_Position.w = 1.;
77
78 // Brightness
79 var_brightness = calc_brightness(node_coord, node_brightness);
80
81 // Texture coordinates
82 vec2 vertex_texcoord;
83 vertex_texcoord.x = vertex_basepix.x * (1. / kTextureSideLength);
84 vertex_texcoord.y = -vertex_basepix.y * (1. / kTextureSideLength);
85
86 // Base texture information
87 var_texture.xy = u_terrains[terrain].offset;
88 var_texture.zw = vertex_texcoord;
89
90 // Dithering. Vertices are labeled by their dither_vid. Note that the VIDs
91 // are different depending on whether the vertex is part of the r- or d-
92 // triangle.
93 //
94 // 2-1---d---0
95 // / \ /
96 // r r\d d
97 // / \ /
98 // 0---r--1-2
99 //
100 // Channel 0: opposite triangle of the same field
101 // Channel 1: x-major outside neighbor
102 // Channel 2: y-major outside neighbor
103 int dither_layer = u_terrains[terrain].dither_layer;
104 {
105 uint other_terrain = is_down ? field.x : field.y;
106
107 var_dither[0].zw = vec2(0.5, 0);
108
109 if (other_terrain != terrain) {
110 int other_dither_layer = u_terrains[other_terrain].dither_layer;
111
112 if (other_dither_layer > dither_layer) {
113 var_dither[0].xy = u_terrains[other_terrain].offset;
114 if (dither_vid != 0u)
115 var_dither[0].zw = vec2(2u - dither_vid, 1);
116 }
117 }
118 }
119 {
120 ivec2 other_coord = field_coord;
121 other_coord.x += is_down ? -1 : 1;
122 uvec4 other_field = get_field_base(other_coord);
123 uint other_terrain = is_down ? other_field.x : other_field.y;
124
125 var_dither[1].zw = vec2(0.5, 0);
126
127 if (other_terrain != terrain) {
128 int other_dither_layer = u_terrains[other_terrain].dither_layer;
129
130 if (other_dither_layer > dither_layer) {
131 var_dither[1].xy = u_terrains[other_terrain].offset;
132 if (dither_vid != 1u)
133 var_dither[1].zw = vec2(dither_vid / 2u, 1);
134 }
135 }
136 }
137 {
138 ivec2 other_coord = field_coord;
139 other_coord.y += is_down ? 1 : -1;
140 other_coord.x += is_down ? -1 : 0;
141 other_coord.x += ((field_coord.y & 1) != 0) ? 1 : 0;
142 uvec4 other_field = get_field_base(other_coord);
143 uint other_terrain = is_down ? other_field.x : other_field.y;
144
145 var_dither[2].zw = vec2(0.5, 0);
146
147 if (other_terrain != terrain) {
148 int other_dither_layer = u_terrains[other_terrain].dither_layer;
149
150 if (other_dither_layer > dither_layer) {
151 var_dither[2].xy = u_terrains[other_terrain].offset;
152 if (dither_vid != 2u)
153 var_dither[2].zw = vec2(1u - dither_vid, 1);
154 }
155 }
156 }
157}
0158
=== modified file 'src/graphic/CMakeLists.txt'
--- src/graphic/CMakeLists.txt 2016-10-25 07:07:14 +0000
+++ src/graphic/CMakeLists.txt 2017-01-07 12:36:16 +0000
@@ -120,6 +120,8 @@
120 gl/road_program.h120 gl/road_program.h
121 gl/terrain_program.cc121 gl/terrain_program.cc
122 gl/terrain_program.h122 gl/terrain_program.h
123 gl/terrain_program_gl4.cc
124 gl/terrain_program_gl4.h
123 gl/dither_program.cc125 gl/dither_program.cc
124 gl/dither_program.h126 gl/dither_program.h
125 DEPENDS127 DEPENDS
@@ -195,6 +197,8 @@
195 SRCS197 SRCS
196 game_renderer.cc198 game_renderer.cc
197 game_renderer.h199 game_renderer.h
200 game_renderer_gl4.cc
201 game_renderer_gl4.h
198 DEPENDS202 DEPENDS
199 base_geometry203 base_geometry
200 base_macros204 base_macros
201205
=== modified file 'src/graphic/game_renderer.cc'
--- src/graphic/game_renderer.cc 2016-11-17 18:33:09 +0000
+++ src/graphic/game_renderer.cc 2017-01-07 12:36:16 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2010-2013 by the Widelands Development Team2 * Copyright (C) 2010-2016 by the Widelands Development Team
3 *3 *
4 * This program is free software; you can redistribute it and/or4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License5 * modify it under the terms of the GNU General Public License
@@ -21,7 +21,9 @@
2121
22#include <memory>22#include <memory>
2323
24#include "graphic/game_renderer_gl4.h"
24#include "graphic/gl/coordinate_conversion.h"25#include "graphic/gl/coordinate_conversion.h"
26#include "graphic/gl/fields_to_draw.h"
25#include "graphic/graphic.h"27#include "graphic/graphic.h"
26#include "graphic/render_queue.h"28#include "graphic/render_queue.h"
27#include "graphic/rendertarget.h"29#include "graphic/rendertarget.h"
@@ -71,6 +73,23 @@
7173
72using namespace Widelands;74using namespace Widelands;
7375
76class GameRendererGl2 : public GameRenderer {
77public:
78 // Draw the map for the given parameters (see rendermap). 'player'
79 // can be nullptr in which case the whole map is drawn.
80 void draw(const Widelands::EditorGameBase& egbase,
81 const Vector2f& view_offset,
82 const float scale,
83 const TextToDraw draw_text,
84 const Widelands::Player* player,
85 RenderTarget* dst) override;
86
87private:
88 // This is owned and handled by us, but handed to the RenderQueue, so we
89 // basically promise that this stays valid for one frame.
90 FieldsToDrawGl2 fields_to_draw_;
91};
92
74// Returns the brightness value in [0, 1.] for 'fcoords' at 'gametime' for93// Returns the brightness value in [0, 1.] for 'fcoords' at 'gametime' for
75// 'player' (which can be nullptr).94// 'player' (which can be nullptr).
76float field_brightness(const FCoords& fcoords,95float field_brightness(const FCoords& fcoords,
@@ -98,7 +117,7 @@
98}117}
99118
100void draw_objects_for_visible_field(const EditorGameBase& egbase,119void draw_objects_for_visible_field(const EditorGameBase& egbase,
101 const FieldsToDraw::Field& field,120 const FieldToDrawBase& field,
102 const float zoom,121 const float zoom,
103 const TextToDraw draw_text,122 const TextToDraw draw_text,
104 const Player* player,123 const Player* player,
@@ -128,7 +147,7 @@
128 }147 }
129}148}
130149
131void draw_objets_for_formerly_visible_field(const FieldsToDraw::Field& field,150void draw_objets_for_formerly_visible_field(const FieldToDrawBase& field,
132 const Player::Field& player_field,151 const Player::Field& player_field,
133 const float zoom,152 const float zoom,
134 RenderTarget* dst) {153 RenderTarget* dst) {
@@ -204,49 +223,51 @@
204 }223 }
205}224}
206225
226} // namespace
227
207// Draws the objects (animations & overlays).228// Draws the objects (animations & overlays).
208void draw_objects(const EditorGameBase& egbase,229void GameRenderer::draw_objects(const EditorGameBase& egbase,
209 const float zoom,230 const float scale,
210 const FieldsToDraw& fields_to_draw,231 const FieldsToDrawRefBase& fields_to_draw,
211 const Player* player,232 const Player* player,
212 const TextToDraw draw_text,233 const TextToDraw draw_text,
213 RenderTarget* dst) {234 RenderTarget* dst) {
214 std::vector<FieldOverlayManager::OverlayInfo> overlay_info;235 std::vector<FieldOverlayManager::OverlayInfo> overlay_info;
215 for (size_t current_index = 0; current_index < fields_to_draw.size(); ++current_index) {236 for (auto cursor = fields_to_draw.cursor(); cursor.valid(); cursor.next()) {
216 const FieldsToDraw::Field& field = fields_to_draw.at(current_index);237 const FieldToDrawBase& field = cursor.field();
217 if (!field.all_neighbors_valid()) {238 if (!cursor.all_neighbors_valid()) {
218 continue;239 continue;
219 }240 }
220241
221 const FieldsToDraw::Field& rn = fields_to_draw.at(field.rn_index);242 const FieldToDrawBase& rn = cursor.rn();
222 const FieldsToDraw::Field& bln = fields_to_draw.at(field.bln_index);243 const FieldToDrawBase& bln = cursor.bln();
223 const FieldsToDraw::Field& brn = fields_to_draw.at(field.brn_index);244 const FieldToDrawBase& brn = cursor.brn();
224245
225 if (field.is_border) {246 if (field.is_border) {
226 assert(field.owner != nullptr);247 assert(field.owner != nullptr);
227 uint32_t const anim_idx = field.owner->tribe().frontier_animation();248 uint32_t const anim_idx = field.owner->tribe().frontier_animation();
228 if (field.vision) {249 if (field.vision) {
229 dst->blit_animation(250 dst->blit_animation(
230 field.rendertarget_pixel, zoom, anim_idx, 0, field.owner->get_playercolor());251 field.rendertarget_pixel, scale, anim_idx, 0, field.owner->get_playercolor());
231 }252 }
232 for (const auto& nf : {rn, bln, brn}) {253 for (const auto& nf : {rn, bln, brn}) {
233 if ((field.vision || nf.vision) && nf.is_border &&254 if ((field.vision || nf.vision) && nf.is_border &&
234 (field.owner == nf.owner || nf.owner == nullptr)) {255 (field.owner == nf.owner || nf.owner == nullptr)) {
235 dst->blit_animation(middle(field.rendertarget_pixel, nf.rendertarget_pixel), zoom,256 dst->blit_animation(middle(field.rendertarget_pixel, nf.rendertarget_pixel), scale,
236 anim_idx, 0, field.owner->get_playercolor());257 anim_idx, 0, field.owner->get_playercolor());
237 }258 }
238 }259 }
239 }260 }
240261
241 if (1 < field.vision) { // Render stuff that belongs to the node.262 if (1 < field.vision) { // Render stuff that belongs to the node.
242 draw_objects_for_visible_field(egbase, field, zoom, draw_text, player, dst);263 draw_objects_for_visible_field(egbase, field, scale, draw_text, player, dst);
243 } else if (field.vision == 1) {264 } else if (field.vision == 1) {
244 // We never show census or statistics for objects in the fog.265 // We never show census or statistics for objects in the fog.
245 assert(player != nullptr);266 assert(player != nullptr);
246 const Map& map = egbase.map();267 const Map& map = egbase.map();
247 const Player::Field& player_field =268 const Player::Field& player_field =
248 player->fields()[map.get_index(field.fcoords, map.get_width())];269 player->fields()[map.get_index(field.fcoords, map.get_width())];
249 draw_objets_for_formerly_visible_field(field, player_field, zoom, dst);270 draw_objets_for_formerly_visible_field(field, player_field, scale, dst);
250 }271 }
251272
252 const FieldOverlayManager& overlay_manager = egbase.get_ibase()->field_overlay_manager();273 const FieldOverlayManager& overlay_manager = egbase.get_ibase()->field_overlay_manager();
@@ -255,8 +276,8 @@
255 overlay_manager.get_overlays(field.fcoords, &overlay_info);276 overlay_manager.get_overlays(field.fcoords, &overlay_info);
256 for (const auto& overlay : overlay_info) {277 for (const auto& overlay : overlay_info) {
257 dst->blitrect_scale(278 dst->blitrect_scale(
258 Rectf(field.rendertarget_pixel - overlay.hotspot.cast<float>() * zoom,279 Rectf(field.rendertarget_pixel - overlay.hotspot.cast<float>() * scale,
259 overlay.pic->width() * zoom, overlay.pic->height() * zoom),280 overlay.pic->width() * scale, overlay.pic->height() * scale),
260 overlay.pic, Recti(0, 0, overlay.pic->width(), overlay.pic->height()), 1.f,281 overlay.pic, Recti(0, 0, overlay.pic->width(), overlay.pic->height()), 1.f,
261 BlendMode::UseAlpha);282 BlendMode::UseAlpha);
262 }283 }
@@ -272,8 +293,8 @@
272 (field.rendertarget_pixel.y + rn.rendertarget_pixel.y + brn.rendertarget_pixel.y) /293 (field.rendertarget_pixel.y + rn.rendertarget_pixel.y + brn.rendertarget_pixel.y) /
273 3.f);294 3.f);
274 for (const auto& overlay : overlay_info) {295 for (const auto& overlay : overlay_info) {
275 dst->blitrect_scale(Rectf(tripos - overlay.hotspot.cast<float>() * zoom,296 dst->blitrect_scale(Rectf(tripos - overlay.hotspot.cast<float>() * scale,
276 overlay.pic->width() * zoom, overlay.pic->height() * zoom),297 overlay.pic->width() * scale, overlay.pic->height() * scale),
277 overlay.pic,298 overlay.pic,
278 Recti(0, 0, overlay.pic->width(), overlay.pic->height()), 1.f,299 Recti(0, 0, overlay.pic->width(), overlay.pic->height()), 1.f,
279 BlendMode::UseAlpha);300 BlendMode::UseAlpha);
@@ -291,8 +312,8 @@
291 (field.rendertarget_pixel.y + bln.rendertarget_pixel.y + brn.rendertarget_pixel.y) /312 (field.rendertarget_pixel.y + bln.rendertarget_pixel.y + brn.rendertarget_pixel.y) /
292 3.f);313 3.f);
293 for (const auto& overlay : overlay_info) {314 for (const auto& overlay : overlay_info) {
294 dst->blitrect_scale(Rectf(tripos - overlay.hotspot.cast<float>() * zoom,315 dst->blitrect_scale(Rectf(tripos - overlay.hotspot.cast<float>() * scale,
295 overlay.pic->width() * zoom, overlay.pic->height() * zoom),316 overlay.pic->width() * scale, overlay.pic->height() * scale),
296 overlay.pic,317 overlay.pic,
297 Recti(0, 0, overlay.pic->width(), overlay.pic->height()), 1.f,318 Recti(0, 0, overlay.pic->width(), overlay.pic->height()), 1.f,
298 BlendMode::UseAlpha);319 BlendMode::UseAlpha);
@@ -301,37 +322,42 @@
301 }322 }
302}323}
303324
304} // namespace
305
306GameRenderer::GameRenderer() {325GameRenderer::GameRenderer() {
307}326}
308327
309GameRenderer::~GameRenderer() {328GameRenderer::~GameRenderer() {
310}329}
311330
331std::unique_ptr<GameRenderer>
332GameRenderer::create() {
333 if (GameRendererGl4::supported())
334 return std::unique_ptr<GameRenderer>(new GameRendererGl4);
335 return std::unique_ptr<GameRenderer>(new GameRendererGl2);
336}
337
312void GameRenderer::rendermap(const Widelands::EditorGameBase& egbase,338void GameRenderer::rendermap(const Widelands::EditorGameBase& egbase,
313 const Vector2f& viewpoint,339 const Vector2f& view_offset,
314 const float zoom,340 float zoom,
315 const Widelands::Player& player,341 const Widelands::Player& player,
316 const TextToDraw draw_text,342 TextToDraw draw_text,
317 RenderTarget* dst) {343 RenderTarget* dst) {
318 draw(egbase, viewpoint, zoom, draw_text, &player, dst);344 draw(egbase, view_offset, zoom, draw_text, &player, dst);
319}345}
320346
321void GameRenderer::rendermap(const Widelands::EditorGameBase& egbase,347void GameRenderer::rendermap(const Widelands::EditorGameBase& egbase,
322 const Vector2f& viewpoint,348 const Vector2f& view_offset,
323 const float zoom,349 float zoom,
324 const TextToDraw draw_text,350 TextToDraw draw_text,
325 RenderTarget* dst) {351 RenderTarget* dst) {
326 draw(egbase, viewpoint, zoom, draw_text, nullptr, dst);352 draw(egbase, view_offset, zoom, draw_text, nullptr, dst);
327}353}
328354
329void GameRenderer::draw(const EditorGameBase& egbase,355void GameRendererGl2::draw(const EditorGameBase& egbase,
330 const Vector2f& viewpoint,356 const Vector2f& viewpoint,
331 const float zoom,357 const float zoom,
332 const TextToDraw draw_text,358 const TextToDraw draw_text,
333 const Player* player,359 const Player* player,
334 RenderTarget* dst) {360 RenderTarget* dst) {
335 assert(viewpoint.x >= 0); // divisions involving negative numbers are bad361 assert(viewpoint.x >= 0); // divisions involving negative numbers are bad
336 assert(viewpoint.y >= 0);362 assert(viewpoint.y >= 0);
337 assert(dst->get_offset().x <= 0);363 assert(dst->get_offset().x <= 0);
@@ -372,59 +398,48 @@
372398
373 const float scale = 1.f / zoom;399 const float scale = 1.f / zoom;
374 fields_to_draw_.reset(minfx, maxfx, minfy, maxfy);400 fields_to_draw_.reset(minfx, maxfx, minfy, maxfy);
375 for (int32_t fy = minfy; fy <= maxfy; ++fy) {401 for (auto cursor = fields_to_draw_.cursor(); cursor.valid(); cursor.next()) {
376 for (int32_t fx = minfx; fx <= maxfx; ++fx) {402 FieldToDrawGl2& f = cursor.mutable_field();
377 FieldsToDraw::Field& f =403
378 *fields_to_draw_.mutable_field(fields_to_draw_.calculate_index(fx, fy));404 // Texture coordinates for pseudo random tiling of terrain and road
379405 // graphics. Since screen space X increases top-to-bottom and OpenGL
380 f.geometric_coords = Coords(fx, fy);406 // increases bottom-to-top we flip the y coordinate to not have
381407 // terrains and road graphics vertically mirrorerd.
382 f.ln_index = fields_to_draw_.calculate_index(fx - 1, fy);408 Vector2f map_pixel =
383 f.rn_index = fields_to_draw_.calculate_index(fx + 1, fy);409 MapviewPixelFunctions::to_map_pixel_ignoring_height(cursor.geometric_coords());
384 f.trn_index = fields_to_draw_.calculate_index(fx + (fy & 1), fy - 1);410 f.texture_coords.x = map_pixel.x / kTextureSideLength;
385 f.bln_index = fields_to_draw_.calculate_index(fx + (fy & 1) - 1, fy + 1);411 f.texture_coords.y = -map_pixel.y / kTextureSideLength;
386 f.brn_index = fields_to_draw_.calculate_index(fx + (fy & 1), fy + 1);412
387413 Coords normalized = cursor.geometric_coords();
388 // Texture coordinates for pseudo random tiling of terrain and road414 map.normalize_coords(normalized);
389 // graphics. Since screen space X increases top-to-bottom and OpenGL415 f.fcoords = map.get_fcoords(normalized);
390 // increases bottom-to-top we flip the y coordinate to not have416
391 // terrains and road graphics vertically mirrorerd.417 map_pixel.y -= f.fcoords.field->get_height() * kHeightFactor;
392 Vector2f map_pixel =418
393 MapviewPixelFunctions::to_map_pixel_ignoring_height(f.geometric_coords);419 f.rendertarget_pixel = MapviewPixelFunctions::map_to_panel(viewpoint, zoom, map_pixel);
394 f.texture_coords.x = map_pixel.x / kTextureSideLength;420 f.gl_position = f.surface_pixel = f.rendertarget_pixel +
395 f.texture_coords.y = -map_pixel.y / kTextureSideLength;421 dst->get_rect().origin().cast<float>() +
396422 dst->get_offset().cast<float>();
397 Coords normalized = f.geometric_coords;423 pixel_to_gl_renderbuffer(
398 map.normalize_coords(normalized);424 surface_width, surface_height, &f.gl_position.x, &f.gl_position.y);
399 f.fcoords = map.get_fcoords(normalized);425
400426 f.brightness = field_brightness(f.fcoords, gametime, map, player);
401 map_pixel.y -= f.fcoords.field->get_height() * kHeightFactor;427
402428 PlayerNumber owned_by = f.fcoords.field->get_owned_by();
403 f.rendertarget_pixel = MapviewPixelFunctions::map_to_panel(viewpoint, zoom, map_pixel);429 f.owner = owned_by != 0 ? &egbase.player(owned_by) : nullptr;
404 f.gl_position = f.surface_pixel = f.rendertarget_pixel +430 f.is_border = f.fcoords.field->is_border();
405 dst->get_rect().origin().cast<float>() +431 f.vision = 2;
406 dst->get_offset().cast<float>();432 f.roads = f.fcoords.field->get_roads();
407 pixel_to_gl_renderbuffer(433 if (player && !player->see_all()) {
408 surface_width, surface_height, &f.gl_position.x, &f.gl_position.y);434 const Player::Field& pf = player->fields()[map.get_index(f.fcoords, map.get_width())];
409435 f.roads = pf.roads;
410 f.brightness = field_brightness(f.fcoords, gametime, map, player);436 f.vision = pf.vision;
411437 if (pf.vision == 1) {
412 PlayerNumber owned_by = f.fcoords.field->get_owned_by();438 f.owner = pf.owner != 0 ? &egbase.player(owned_by) : nullptr;
413 f.owner = owned_by != 0 ? &egbase.player(owned_by) : nullptr;439 f.is_border = pf.border;
414 f.is_border = f.fcoords.field->is_border();
415 f.vision = 2;
416 f.roads = f.fcoords.field->get_roads();
417 if (player && !player->see_all()) {
418 const Player::Field& pf = player->fields()[map.get_index(f.fcoords, map.get_width())];
419 f.roads = pf.roads;
420 f.vision = pf.vision;
421 if (pf.vision == 1) {
422 f.owner = pf.owner != 0 ? &egbase.player(owned_by) : nullptr;
423 f.is_border = pf.border;
424 }
425 }440 }
426 f.roads |= edge_overlay_manager.get_overlay(f.fcoords);
427 }441 }
442 f.roads |= edge_overlay_manager.get_overlay(f.fcoords);
428 }443 }
429444
430 // Enqueue the drawing of the terrain.445 // Enqueue the drawing of the terrain.
431446
=== modified file 'src/graphic/game_renderer.h'
--- src/graphic/game_renderer.h 2016-10-26 19:43:40 +0000
+++ src/graphic/game_renderer.h 2017-01-07 12:36:16 +0000
@@ -34,11 +34,26 @@
3434
35class RenderTarget;35class RenderTarget;
3636
37// Renders the MapView on screen.37/**
38 * This abstract base class renders the main game view into an
39 * arbitrary @ref RenderTarget.
40 *
41 * Specializations exist for different OpenGL rendering paths.
42 *
43 * Users of this class should keep instances alive for as long as possible,
44 * so that target-specific optimizations (such as caching data) can
45 * be effective.
46 *
47 * Every instance can only perform one render operation per frame. When
48 * multiple views of the map are open, each needs its own instance of
49 * GameRenderer.
50 */
38class GameRenderer {51class GameRenderer {
39public:52public:
40 GameRenderer();53 virtual ~GameRenderer();
41 ~GameRenderer();54
55 // Create a game renderer instance.
56 static std::unique_ptr<GameRenderer> create();
4257
43 // Renders the map from a player's point of view into the given drawing58 // Renders the map from a player's point of view into the given drawing
44 // window. The 'viewpoint' is the top left screens pixel map pixel and59 // window. The 'viewpoint' is the top left screens pixel map pixel and
@@ -58,19 +73,23 @@
58 TextToDraw draw_text,73 TextToDraw draw_text,
59 RenderTarget* dst);74 RenderTarget* dst);
6075
61private:76protected:
62 // Draw the map for the given parameters (see rendermap). 'player'77 GameRenderer();
63 // can be nullptr in which case the whole map is drawn.78
64 void draw(const Widelands::EditorGameBase& egbase,79 virtual void draw(const Widelands::EditorGameBase& egbase,
65 const Vector2f& viewpoint,80 const Vector2f& view_offset,
66 float scale,81 const float zoom,
67 TextToDraw draw_text,82 const TextToDraw draw_text,
68 const Widelands::Player* player,83 const Widelands::Player* player,
69 RenderTarget* dst);84 RenderTarget* dst) = 0;
7085
71 // This is owned and handled by us, but handed to the RenderQueue, so we86 // Draws the objects (animations & overlays).
72 // basically promise that this stays valid for one frame.87 void draw_objects(const Widelands::EditorGameBase& egbase,
73 FieldsToDraw fields_to_draw_;88 const float scale,
89 const FieldsToDrawRefBase& fields_to_draw,
90 const Widelands::Player* player,
91 const TextToDraw draw_text,
92 RenderTarget* dst);
7493
75 DISALLOW_COPY_AND_ASSIGN(GameRenderer);94 DISALLOW_COPY_AND_ASSIGN(GameRenderer);
76};95};
7796
=== added file 'src/graphic/game_renderer_gl4.cc'
--- src/graphic/game_renderer_gl4.cc 1970-01-01 00:00:00 +0000
+++ src/graphic/game_renderer_gl4.cc 2017-01-07 12:36:16 +0000
@@ -0,0 +1,194 @@
1/*
2 * Copyright (C) 2010-2016 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20#include "graphic/game_renderer_gl4.h"
21
22#include "graphic/gl/terrain_program_gl4.h"
23#include "graphic/render_queue.h"
24#include "graphic/rendertarget.h"
25#include "graphic/surface.h"
26#include "logic/editor_game_base.h"
27#include "logic/map.h"
28#include "logic/map_objects/walkingdir.h"
29#include "logic/player.h"
30#include "logic/roadtype.h"
31#include "wui/edge_overlay_manager.h"
32#include "wui/interactive_base.h"
33#include "wui/mapviewpixelconstants.h"
34#include "wui/mapviewpixelfunctions.h"
35
36using namespace Widelands;
37
38/**
39 * This is the front-end of the GL4 rendering path. See game_renderer.cc for a
40 * general overview of terrain rendering.
41 *
42 * Only terrain rendering (terrain textures, dithering, and roads) differs
43 * substantially from the GL2 rendering path. To avoid CPU work, a persistent
44 * TerrainInformationGl4 object maintains texture data and other information
45 * across frames.
46 *
47 * The GameRendererGl4 contains per-view information, but the underlying
48 * TerrainInformationGl4 instance is automatically shared between different
49 * views when possible.
50 */
51
52GameRendererGl4::GameRendererGl4() {
53}
54
55GameRendererGl4::~GameRendererGl4() {
56}
57
58bool GameRendererGl4::supported() {
59 return TerrainProgramGl4::supported();
60}
61
62void GameRendererGl4::draw(const EditorGameBase& egbase,
63 const Vector2f& view_offset,
64 const float zoom,
65 const TextToDraw draw_text,
66 const Player* player,
67 RenderTarget* dst) {
68 Surface* surface = dst->get_surface();
69 if (!surface)
70 return;
71
72 // Upload map changes.
73 if (!args_.terrain || &args_.terrain->egbase() != &egbase ||
74 args_.terrain->player() != player)
75 args_.terrain = TerrainInformationGl4::get(egbase, player);
76
77 // Determine the set of patches to draw.
78 float scale = 1.f / zoom;
79 Vector2f tl_map = view_offset - dst->get_offset().cast<float>() * zoom;
80
81 assert(tl_map.x >= 0); // divisions involving negative numbers are bad
82 assert(tl_map.y >= 0);
83
84 args_.minfx = tl_map.x / kTriangleWidth - 1;
85 args_.minfy = tl_map.y / kTriangleHeight - 1;
86 args_.maxfx = (tl_map.x + dst->get_rect().w * zoom + (kTriangleWidth / 2)) / kTriangleWidth;
87 args_.maxfy = (tl_map.y + dst->get_rect().h * zoom) / kTriangleHeight;
88
89 // Fudge for triangle boundary effects, for height differences, and for
90 // large immovables.
91 args_.minfx -= 1;
92 args_.minfy -= 1;
93 args_.maxfx += 3;
94 args_.maxfy += 10;
95
96 const Recti& bounding_rect = dst->get_rect();
97 const uint32_t gametime = egbase.get_gametime();
98
99 args_.scale = scale;
100 args_.surface_offset = (bounding_rect.origin() + dst->get_offset()).cast<float>() * zoom - view_offset;
101 args_.surface_width = surface->width();
102 args_.surface_height = surface->height();
103
104 scan_fields(view_offset);
105
106 args_.terrain->update(args_.minfx, args_.maxfx, args_.minfy, args_.maxfy);
107
108 // Enqueue the drawing of the terrain.
109 RenderQueue::Item i;
110 i.program_id = RenderQueue::Program::kTerrainGl4;
111 i.blend_mode = BlendMode::Copy;
112 i.terrain_arguments.destination_rect =
113 Rectf(bounding_rect.x, args_.surface_height - bounding_rect.y - bounding_rect.h,
114 bounding_rect.w, bounding_rect.h);
115 i.terrain_arguments.gametime = gametime;
116 i.terrain_arguments.renderbuffer_width = args_.surface_width;
117 i.terrain_arguments.renderbuffer_height = args_.surface_height;
118 i.terrain_gl4_arguments = &args_;
119 RenderQueue::instance().enqueue(i);
120
121 if (!args_.roads.empty()) {
122 i.program_id = RenderQueue::Program::kTerrainRoadGl4;
123 i.blend_mode = BlendMode::UseAlpha;
124 RenderQueue::instance().enqueue(i);
125 }
126
127 draw_objects(egbase, scale, fields_to_draw_, player, draw_text, dst);
128}
129
130void GameRendererGl4::scan_fields(const Vector2f& view_offset) {
131 const EditorGameBase& egbase = args_.terrain->egbase();
132 const Player* player = args_.terrain->player();
133 auto& map = egbase.map();
134 const EdgeOverlayManager& edge_overlay_manager = egbase.get_ibase()->edge_overlay_manager();
135
136 args_.roads.clear();
137 fields_to_draw_.reset(args_.minfx, args_.maxfx, args_.minfy, args_.maxfy);
138
139 for (auto cursor = fields_to_draw_.cursor(); cursor.valid(); cursor.next()) {
140 FieldToDrawBase& f = cursor.mutable_field();
141
142 Vector2f map_pixel =
143 MapviewPixelFunctions::to_map_pixel_ignoring_height(cursor.geometric_coords());
144
145 Coords normalized = cursor.geometric_coords();
146 map.normalize_coords(normalized);
147 f.fcoords = map.get_fcoords(normalized);
148
149 map_pixel.y -= f.fcoords.field->get_height() * kHeightFactor;
150
151 f.rendertarget_pixel = MapviewPixelFunctions::map_to_panel(view_offset, 1. / args_.scale, map_pixel);
152
153 PlayerNumber owned_by = f.fcoords.field->get_owned_by();
154 f.owner = owned_by != 0 ? &egbase.player(owned_by) : nullptr;
155 f.is_border = f.fcoords.field->is_border();
156 f.vision = 2;
157 if (player && !player->see_all()) {
158 const Player::Field& pf = player->fields()[map.get_index(f.fcoords, map.get_width())];
159 f.vision = pf.vision;
160 if (pf.vision == 1) {
161 f.owner = pf.owner != 0 ? &egbase.player(owned_by) : nullptr;
162 f.is_border = pf.border;
163 }
164 }
165
166 uint8_t roads;
167 if (!player || player->see_all()) {
168 roads = f.fcoords.field->get_roads();
169 } else {
170 const Player::Field& pf = player->fields()[map.get_index(f.fcoords, map.get_width())];
171 roads = pf.roads;
172 }
173 if (player)
174 roads |= edge_overlay_manager.get_overlay(f.fcoords);
175
176 uint8_t type = (roads >> RoadType::kEast) & RoadType::kMask;
177 if (type) {
178 args_.roads.emplace_back(cursor.geometric_coords(), type,
179 WalkingDir::WALK_E, f.fcoords.field->get_owned_by());
180 }
181
182 type = (roads >> RoadType::kSouthEast) & RoadType::kMask;
183 if (type) {
184 args_.roads.emplace_back(cursor.geometric_coords(), type,
185 WalkingDir::WALK_SE, f.fcoords.field->get_owned_by());
186 }
187
188 type = (roads >> RoadType::kSouthWest) & RoadType::kMask;
189 if (type) {
190 args_.roads.emplace_back(cursor.geometric_coords(), type,
191 WalkingDir::WALK_SW, f.fcoords.field->get_owned_by());
192 }
193 }
194}
0195
=== added file 'src/graphic/game_renderer_gl4.h'
--- src/graphic/game_renderer_gl4.h 1970-01-01 00:00:00 +0000
+++ src/graphic/game_renderer_gl4.h 2017-01-07 12:36:16 +0000
@@ -0,0 +1,92 @@
1/*
2 * Copyright (C) 2010-2016 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20#ifndef WL_GRAPHIC_GAME_RENDERER_GL4_H
21#define WL_GRAPHIC_GAME_RENDERER_GL4_H
22
23#include <vector>
24
25#include "graphic/game_renderer.h"
26#include "graphic/minimap_layer.h"
27#include "logic/widelands_geometry.h"
28
29class TerrainInformationGl4;
30
31/**
32 * This structure is used for the @ref RenderQueue by terrain rendering, road
33 * rendering, and minimap rendering. Each use only uses a subset of memers.
34 */
35struct TerrainGl4Arguments {
36 struct Road {
37 Widelands::Coords coord;
38
39 // One of the RoadTypes
40 uint8_t type;
41
42 // One of the WalkingDirs
43 uint8_t direction;
44
45 // Player number
46 uint8_t owner;
47
48 Road(Widelands::Coords coord_, uint8_t type_, uint8_t direction_, uint8_t owner_)
49 : coord(coord_), type(type_), direction(direction_), owner(owner_) {
50 }
51 };
52 static_assert(sizeof(Road) == 8, "bad alignment");
53
54 std::shared_ptr<TerrainInformationGl4> terrain;
55 float scale;
56 Vector2f surface_offset;
57 int surface_width;
58 int surface_height;
59 int minfx, minfy, maxfx, maxfy;
60 std::vector<Road> roads;
61
62 int minimap_tl_fx, minimap_tl_fy;
63 MiniMapLayer minimap_layers;
64};
65
66/**
67 * This is the front-end of the GL4 rendering path game renderer.
68 */
69class GameRendererGl4 : public GameRenderer {
70public:
71 GameRendererGl4();
72 virtual ~GameRendererGl4();
73
74 static bool supported();
75
76 void draw(const Widelands::EditorGameBase& egbase,
77 const Vector2f& view_offset,
78 const float zoom,
79 const TextToDraw draw_text,
80 const Widelands::Player* player,
81 RenderTarget* dst) override;
82
83private:
84 void scan_fields(const Vector2f& view_offset);
85
86 TerrainGl4Arguments args_;
87 FieldsToDrawBase fields_to_draw_;
88
89 DISALLOW_COPY_AND_ASSIGN(GameRendererGl4);
90};
91
92#endif // end of include guard: WL_GRAPHIC_GAME_RENDERER_H
093
=== modified file 'src/graphic/gl/blit_program.cc'
--- src/graphic/gl/blit_program.cc 2016-12-03 13:32:28 +0000
+++ src/graphic/gl/blit_program.cc 2017-01-07 12:36:16 +0000
@@ -19,13 +19,16 @@
1919
20#include "graphic/gl/blit_program.h"20#include "graphic/gl/blit_program.h"
2121
22#include <memory>
22#include <vector>23#include <vector>
2324
24#include "base/log.h"25#include "base/log.h"
25#include "graphic/blit_mode.h"26#include "graphic/blit_mode.h"
26#include "graphic/gl/blit_data.h"27#include "graphic/gl/blit_data.h"
27#include "graphic/gl/coordinate_conversion.h"28#include "graphic/gl/coordinate_conversion.h"
29#include "graphic/gl/streaming_buffer.h"
28#include "graphic/gl/utils.h"30#include "graphic/gl/utils.h"
31#include "profile/profile.h"
2932
30namespace {33namespace {
3134
@@ -41,9 +44,120 @@
41 BlendMode blend_mode;44 BlendMode blend_mode;
42};45};
4346
47class BlitProgramGl2 : public BlitProgram {
48public:
49 BlitProgramGl2();
50
51 void draw(const std::vector<Arguments>& arguments) override;
52
53private:
54 struct PerVertexData {
55 PerVertexData(float init_gl_x,
56 float init_gl_y,
57 float init_gl_z,
58 float init_texture_x,
59 float init_texture_y,
60 float init_mask_texture_x,
61 float init_mask_texture_y,
62 float init_blend_r,
63 float init_blend_g,
64 float init_blend_b,
65 float init_blend_a,
66 float init_program_flavor)
67 : gl_x(init_gl_x),
68 gl_y(init_gl_y),
69 gl_z(init_gl_z),
70 texture_x(init_texture_x),
71 texture_y(init_texture_y),
72 mask_texture_x(init_mask_texture_x),
73 mask_texture_y(init_mask_texture_y),
74 blend_r(init_blend_r),
75 blend_g(init_blend_g),
76 blend_b(init_blend_b),
77 blend_a(init_blend_a),
78 program_flavor(init_program_flavor) {
79 }
80
81 float gl_x, gl_y, gl_z;
82 float texture_x, texture_y;
83 float mask_texture_x, mask_texture_y;
84 float blend_r, blend_g, blend_b, blend_a;
85 float program_flavor;
86 };
87 static_assert(sizeof(PerVertexData) == 48, "Wrong padding.");
88
89 // The buffer that will contain the quad for rendering.
90 Gl::Buffer<PerVertexData> gl_array_buffer_;
91
92 // The program.
93 Gl::Program gl_program_;
94
95 // Attributes.
96 GLint attr_blend_;
97 GLint attr_mask_texture_position_;
98 GLint attr_position_;
99 GLint attr_texture_position_;
100 GLint attr_program_flavor_;
101
102 // Uniforms.
103 GLint u_texture_;
104 GLint u_mask_;
105
106 // Cached for efficiency.
107 std::vector<PerVertexData> vertices_;
108};
109
110class BlitProgramGl4 : public BlitProgram {
111public:
112 BlitProgramGl4();
113
114 static bool supported();
115
116 void draw(const std::vector<Arguments>& arguments) override;
117
118private:
119 struct PerRectData {
120 float dst_x, dst_y, dst_width, dst_height;
121 uint32_t src_x, src_y, src_width, src_height;
122 uint32_t src_parent_width, src_parent_height;
123 uint32_t mask_x, mask_y, mask_width, mask_height;
124 uint32_t mask_parent_width, mask_parent_height;
125 uint32_t blend_r, blend_g, blend_b, blend_a;
126 float program_flavor, z;
127
128 // Standard OpenGL packing aligns arrays to a multiple of 16 bytes.
129 float padding[2];
130 };
131 static_assert(sizeof(PerRectData) == 96, "Wrong padding.");
132
133 void setup_index_buffer(unsigned num_rects);
134
135 // The index buffer.
136 Gl::Buffer<uint16_t> gl_index_buffer_;
137 unsigned num_index_rects_;
138
139 // The per-rect data buffer.
140 Gl::StreamingBuffer<PerRectData> gl_rects_buffer_;
141
142 // The program.
143 Gl::Program gl_program_;
144
145 // Uniform locations.
146 GLint u_texture_;
147 GLint u_mask_;
148};
149
44} // namespace150} // namespace
45151
46BlitProgram::BlitProgram() {152BlitProgram::BlitProgram() {
153}
154
155BlitProgram::~BlitProgram() {
156}
157
158BlitProgramGl2::BlitProgramGl2() {
159 log("Using GL2 blit path\n");
160
47 gl_program_.build("blit");161 gl_program_.build("blit");
48162
49 attr_blend_ = glGetAttribLocation(gl_program_.object(), "attr_blend");163 attr_blend_ = glGetAttribLocation(gl_program_.object(), "attr_blend");
@@ -57,10 +171,7 @@
57 u_mask_ = glGetUniformLocation(gl_program_.object(), "u_mask");171 u_mask_ = glGetUniformLocation(gl_program_.object(), "u_mask");
58}172}
59173
60BlitProgram::~BlitProgram() {174void BlitProgramGl2::draw(const std::vector<Arguments>& arguments) {
61}
62
63void BlitProgram::draw(const std::vector<Arguments>& arguments) {
64 glUseProgram(gl_program_.object());175 glUseProgram(gl_program_.object());
65176
66 auto& gl_state = Gl::State::instance();177 auto& gl_state = Gl::State::instance();
@@ -176,6 +287,165 @@
176 }287 }
177}288}
178289
290BlitProgramGl4::BlitProgramGl4()
291 : gl_rects_buffer_(GL_ARRAY_BUFFER) {
292 log("Using GL4 blit path\n");
293
294 gl_program_.build_vp_fp({"blit_gl4"}, {"blit"});
295
296 u_texture_ = glGetUniformLocation(gl_program_.object(), "u_texture");
297 u_mask_ = glGetUniformLocation(gl_program_.object(), "u_mask");
298
299 num_index_rects_ = 0;
300}
301
302bool BlitProgramGl4::supported() {
303 const auto& caps = Gl::State::instance().capabilities();
304
305 if (caps.glsl_version < 130)
306 return false;
307
308 if (!caps.ARB_separate_shader_objects ||
309 !caps.ARB_shader_storage_buffer_object ||
310 !caps.ARB_uniform_buffer_object)
311 return false;
312
313 return !g_options.pull_section("global").get_bool("disable_gl4", false);
314}
315
316void BlitProgramGl4::draw(const std::vector<Arguments>& arguments) {
317 glUseProgram(gl_program_.object());
318
319 auto& gl_state = Gl::State::instance();
320
321 gl_state.enable_vertex_attrib_array({});
322
323 glUniform1i(u_texture_, 0);
324 glUniform1i(u_mask_, 1);
325
326 // Prepare the buffer for many draw calls.
327 std::vector<DrawBatch> draw_batches;
328 auto rects = gl_rects_buffer_.stream(arguments.size());
329
330 size_t i = 0;
331 while (i < arguments.size()) {
332 const auto& template_args = arguments[i];
333 const int start = i;
334
335 // Batch common blit operations up.
336 while (i < arguments.size()) {
337 const auto& current_args = arguments[i];
338 if (current_args.blend_mode != template_args.blend_mode ||
339 current_args.texture.texture_id != template_args.texture.texture_id ||
340 (current_args.mask.texture_id != 0 &&
341 current_args.mask.texture_id != template_args.mask.texture_id)) {
342 break;
343 }
344
345 rects.emplace_back();
346 auto& rect = rects.back();
347 rect.dst_x = current_args.destination_rect.x;
348 rect.dst_y = current_args.destination_rect.y;
349 rect.dst_width = current_args.destination_rect.w;
350 rect.dst_height = current_args.destination_rect.h;
351
352 rect.src_x = current_args.texture.rect.x;
353 rect.src_y = current_args.texture.rect.y;
354 rect.src_width = current_args.texture.rect.w;
355 rect.src_height = current_args.texture.rect.h;
356 rect.src_parent_width = current_args.texture.parent_width;
357 rect.src_parent_height = current_args.texture.parent_height;
358
359 rect.mask_x = current_args.mask.rect.x;
360 rect.mask_y = current_args.mask.rect.y;
361 rect.mask_width = current_args.mask.rect.w;
362 rect.mask_height = current_args.mask.rect.h;
363 rect.mask_parent_width = current_args.mask.parent_width;
364 rect.mask_parent_height = current_args.mask.parent_height;
365
366 rect.blend_r = current_args.blend.r;
367 rect.blend_g = current_args.blend.g;
368 rect.blend_b = current_args.blend.b;
369 rect.blend_a = current_args.blend.a;
370
371 switch (current_args.blit_mode) {
372 case BlitMode::kDirect:
373 rect.program_flavor = 0.;
374 break;
375
376 case BlitMode::kMonochrome:
377 rect.program_flavor = 1.;
378 break;
379
380 case BlitMode::kBlendedWithMask:
381 rect.program_flavor = 2.;
382 break;
383 }
384
385 rect.z = current_args.z_value;
386
387 ++i;
388 }
389
390 draw_batches.emplace_back(DrawBatch{int(start), int(i - start),
391 template_args.texture.texture_id,
392 template_args.mask.texture_id, template_args.blend_mode});
393 }
394
395 glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, gl_rects_buffer_.object(),
396 rects.unmap(), i * sizeof(PerRectData));
397
398 setup_index_buffer(i);
399 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl_index_buffer_.object());
400
401 // Now do the draw calls.
402 for (const auto& draw_arg : draw_batches) {
403 gl_state.bind(GL_TEXTURE0, draw_arg.texture);
404 gl_state.bind(GL_TEXTURE1, draw_arg.mask);
405
406 if (draw_arg.blend_mode == BlendMode::Copy) {
407 glBlendFunc(GL_ONE, GL_ZERO);
408 }
409
410 glDrawElements(GL_TRIANGLES, 6 * draw_arg.count, GL_UNSIGNED_SHORT,
411 static_cast<uint16_t*>(nullptr) + 6 * draw_arg.offset);
412
413 if (draw_arg.blend_mode == BlendMode::Copy) {
414 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
415 }
416 }
417
418 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
419}
420
421void BlitProgramGl4::setup_index_buffer(unsigned num_rects)
422{
423 if (num_rects <= num_index_rects_)
424 return;
425
426 if (num_rects > 65536 / 4)
427 throw wexception("Too many rectangles for 16-bit indices");
428
429 std::vector<uint16_t> indices;
430 indices.reserve(num_rects * 6);
431
432 for (unsigned i = 0; i < num_rects; ++i) {
433 indices.push_back(4 * i);
434 indices.push_back(4 * i + 1);
435 indices.push_back(4 * i + 2);
436
437 indices.push_back(4 * i + 2);
438 indices.push_back(4 * i + 1);
439 indices.push_back(4 * i + 3);
440 }
441
442 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl_index_buffer_.object());
443 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint16_t) * indices.size(),
444 indices.data(), GL_STATIC_DRAW);
445 num_index_rects_ = num_rects;
446}
447
448
179void BlitProgram::draw(const Rectf& gl_dest_rect,449void BlitProgram::draw(const Rectf& gl_dest_rect,
180 const float z_value,450 const float z_value,
181 const BlitData& texture,451 const BlitData& texture,
@@ -194,8 +464,29 @@
194 BlendMode::UseAlpha, BlitMode::kMonochrome}});464 BlendMode::UseAlpha, BlitMode::kMonochrome}});
195}465}
196466
467namespace {
468
469class BlitProgramHolder {
470public:
471 BlitProgramHolder() {
472 if (BlitProgramGl4::supported())
473 program_.reset(new BlitProgramGl4);
474 else
475 program_.reset(new BlitProgramGl2);
476 }
477
478 BlitProgram& program() {
479 return *program_;
480 }
481
482private:
483 std::unique_ptr<BlitProgram> program_;
484};
485
486} // namespace
487
197// static488// static
198BlitProgram& BlitProgram::instance() {489BlitProgram& BlitProgram::instance() {
199 static BlitProgram blit_program;490 static BlitProgramHolder holder;
200 return blit_program;491 return holder.program();
201}492}
202493
=== modified file 'src/graphic/gl/blit_program.h'
--- src/graphic/gl/blit_program.h 2016-10-16 09:50:48 +0000
+++ src/graphic/gl/blit_program.h 2017-01-07 12:36:16 +0000
@@ -47,7 +47,7 @@
4747
48 // Returns the (singleton) instance of this class.48 // Returns the (singleton) instance of this class.
49 static BlitProgram& instance();49 static BlitProgram& instance();
50 ~BlitProgram();50 virtual ~BlitProgram();
5151
52 // Draws the rectangle 'gl_src_rect' from the texture with the name52 // Draws the rectangle 'gl_src_rect' from the texture with the name
53 // 'gl_texture_image' to 'gl_dest_rect' in the currently bound framebuffer. All53 // 'gl_texture_image' to 'gl_dest_rect' in the currently bound framebuffer. All
@@ -70,66 +70,12 @@
70 const RGBAColor& blend);70 const RGBAColor& blend);
7171
72 // Draws a bunch of items at once.72 // Draws a bunch of items at once.
73 void draw(const std::vector<Arguments>& arguments);73 virtual void draw(const std::vector<Arguments>& arguments) = 0;
74
75protected:
76 BlitProgram();
7477
75private:78private:
76 BlitProgram();
77
78 struct PerVertexData {
79 PerVertexData(float init_gl_x,
80 float init_gl_y,
81 float init_gl_z,
82 float init_texture_x,
83 float init_texture_y,
84 float init_mask_texture_x,
85 float init_mask_texture_y,
86 float init_blend_r,
87 float init_blend_g,
88 float init_blend_b,
89 float init_blend_a,
90 float init_program_flavor)
91 : gl_x(init_gl_x),
92 gl_y(init_gl_y),
93 gl_z(init_gl_z),
94 texture_x(init_texture_x),
95 texture_y(init_texture_y),
96 mask_texture_x(init_mask_texture_x),
97 mask_texture_y(init_mask_texture_y),
98 blend_r(init_blend_r),
99 blend_g(init_blend_g),
100 blend_b(init_blend_b),
101 blend_a(init_blend_a),
102 program_flavor(init_program_flavor) {
103 }
104
105 float gl_x, gl_y, gl_z;
106 float texture_x, texture_y;
107 float mask_texture_x, mask_texture_y;
108 float blend_r, blend_g, blend_b, blend_a;
109 float program_flavor;
110 };
111 static_assert(sizeof(PerVertexData) == 48, "Wrong padding.");
112
113 // The buffer that will contain the quad for rendering.
114 Gl::Buffer<PerVertexData> gl_array_buffer_;
115
116 // The program.
117 Gl::Program gl_program_;
118
119 // Attributes.
120 GLint attr_blend_;
121 GLint attr_mask_texture_position_;
122 GLint attr_position_;
123 GLint attr_texture_position_;
124 GLint attr_program_flavor_;
125
126 // Uniforms.
127 GLint u_texture_;
128 GLint u_mask_;
129
130 // Cached for efficiency.
131 std::vector<PerVertexData> vertices_;
132
133 DISALLOW_COPY_AND_ASSIGN(BlitProgram);79 DISALLOW_COPY_AND_ASSIGN(BlitProgram);
134};80};
13581
13682
=== modified file 'src/graphic/gl/dither_program.cc'
--- src/graphic/gl/dither_program.cc 2016-12-03 13:32:28 +0000
+++ src/graphic/gl/dither_program.cc 2017-01-07 12:36:16 +0000
@@ -54,7 +54,7 @@
54DitherProgram::~DitherProgram() {54DitherProgram::~DitherProgram() {
55}55}
5656
57void DitherProgram::add_vertex(const FieldsToDraw::Field& field,57void DitherProgram::add_vertex(const FieldToDrawGl2& field,
58 const TrianglePoint triangle_point,58 const TrianglePoint triangle_point,
59 const Vector2f& texture_offset) {59 const Vector2f& texture_offset) {
60 vertices_.emplace_back();60 vertices_.emplace_back();
@@ -87,10 +87,9 @@
87void DitherProgram::maybe_add_dithering_triangle(87void DitherProgram::maybe_add_dithering_triangle(
88 const uint32_t gametime,88 const uint32_t gametime,
89 const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,89 const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
90 const FieldsToDraw& fields_to_draw,90 const FieldToDrawGl2& f1,
91 const int idx1,91 const FieldToDrawGl2& f2,
92 const int idx2,92 const FieldToDrawGl2& f3,
93 const int idx3,
94 const int my_terrain,93 const int my_terrain,
95 const int other_terrain) {94 const int other_terrain) {
96 if (my_terrain == other_terrain) {95 if (my_terrain == other_terrain) {
@@ -100,9 +99,9 @@
100 if (terrains.get(my_terrain).dither_layer() < other_terrain_description.dither_layer()) {99 if (terrains.get(my_terrain).dither_layer() < other_terrain_description.dither_layer()) {
101 const Vector2f texture_offset =100 const Vector2f texture_offset =
102 to_gl_texture(other_terrain_description.get_texture(gametime).blit_data()).origin();101 to_gl_texture(other_terrain_description.get_texture(gametime).blit_data()).origin();
103 add_vertex(fields_to_draw.at(idx1), TrianglePoint::kTopRight, texture_offset);102 add_vertex(f1, TrianglePoint::kTopRight, texture_offset);
104 add_vertex(fields_to_draw.at(idx2), TrianglePoint::kTopLeft, texture_offset);103 add_vertex(f2, TrianglePoint::kTopLeft, texture_offset);
105 add_vertex(fields_to_draw.at(idx3), TrianglePoint::kBottomMiddle, texture_offset);104 add_vertex(f3, TrianglePoint::kBottomMiddle, texture_offset);
106 }105 }
107}106}
108107
@@ -141,7 +140,7 @@
141140
142void DitherProgram::draw(const uint32_t gametime,141void DitherProgram::draw(const uint32_t gametime,
143 const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,142 const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
144 const FieldsToDraw& fields_to_draw,143 const FieldsToDrawGl2& fields_to_draw,
145 const float z_value) {144 const float z_value) {
146 // This method expects that all terrains have the same dimensions and that145 // This method expects that all terrains have the same dimensions and that
147 // all are packed into the same texture atlas, i.e. all are in the same GL146 // all are packed into the same texture atlas, i.e. all are in the same GL
@@ -150,49 +149,49 @@
150 vertices_.clear();149 vertices_.clear();
151 vertices_.reserve(fields_to_draw.size() * 3);150 vertices_.reserve(fields_to_draw.size() * 3);
152151
153 for (size_t current_index = 0; current_index < fields_to_draw.size(); ++current_index) {152 for (auto cursor = fields_to_draw.cursor(); cursor.valid(); cursor.next()) {
154 const FieldsToDraw::Field& field = fields_to_draw.at(current_index);153 const FieldToDrawGl2& field = cursor.field();
155154
156 // The bottom right neighbor fields_to_draw is needed for both triangles155 // The bottom right neighbor fields_to_draw is needed for both triangles
157 // associated with this field. If it is not in fields_to_draw, there is no need to156 // associated with this field. If it is not in fields_to_draw, there is no need to
158 // draw any triangles.157 // draw any triangles.
159 if (field.brn_index == FieldsToDraw::kInvalidIndex) {158 if (!cursor.brn_valid()) {
160 continue;159 continue;
161 }160 }
162161
163 // Dithering triangles for Down triangle.162 // Dithering triangles for Down triangle.
164 if (field.bln_index != FieldsToDraw::kInvalidIndex) {163 if (cursor.bln_valid()) {
165 maybe_add_dithering_triangle(164 maybe_add_dithering_triangle(
166 gametime, terrains, fields_to_draw, field.brn_index, current_index, field.bln_index,165 gametime, terrains, cursor.brn(), cursor.field(), cursor.bln(),
167 field.fcoords.field->terrain_d(), field.fcoords.field->terrain_r());166 field.fcoords.field->terrain_d(), field.fcoords.field->terrain_r());
168167
169 const int terrain_dd = fields_to_draw.at(field.bln_index).fcoords.field->terrain_r();168 const int terrain_dd = cursor.bln().fcoords.field->terrain_r();
170 maybe_add_dithering_triangle(gametime, terrains, fields_to_draw, field.bln_index,169 maybe_add_dithering_triangle(gametime, terrains, cursor.bln(),
171 field.brn_index, current_index,170 cursor.brn(), cursor.field(),
172 field.fcoords.field->terrain_d(), terrain_dd);171 field.fcoords.field->terrain_d(), terrain_dd);
173172
174 if (field.ln_index != FieldsToDraw::kInvalidIndex) {173 if (cursor.ln_valid()) {
175 const int terrain_l = fields_to_draw.at(field.ln_index).fcoords.field->terrain_r();174 const int terrain_l = cursor.ln().fcoords.field->terrain_r();
176 maybe_add_dithering_triangle(gametime, terrains, fields_to_draw, current_index,175 maybe_add_dithering_triangle(gametime, terrains, cursor.field(),
177 field.bln_index, field.brn_index,176 cursor.bln(), cursor.brn(),
178 field.fcoords.field->terrain_d(), terrain_l);177 field.fcoords.field->terrain_d(), terrain_l);
179 }178 }
180 }179 }
181180
182 // Dithering for right triangle.181 // Dithering for right triangle.
183 if (field.rn_index != FieldsToDraw::kInvalidIndex) {182 if (cursor.rn_valid()) {
184 maybe_add_dithering_triangle(183 maybe_add_dithering_triangle(
185 gametime, terrains, fields_to_draw, current_index, field.brn_index, field.rn_index,184 gametime, terrains, cursor.field(), cursor.brn(), cursor.rn(),
186 field.fcoords.field->terrain_r(), field.fcoords.field->terrain_d());185 field.fcoords.field->terrain_r(), field.fcoords.field->terrain_d());
187 int terrain_rr = fields_to_draw.at(field.rn_index).fcoords.field->terrain_d();186 int terrain_rr = cursor.rn().fcoords.field->terrain_d();
188 maybe_add_dithering_triangle(gametime, terrains, fields_to_draw, field.brn_index,187 maybe_add_dithering_triangle(gametime, terrains, cursor.brn(),
189 field.rn_index, current_index,188 cursor.rn(), cursor.field(),
190 field.fcoords.field->terrain_r(), terrain_rr);189 field.fcoords.field->terrain_r(), terrain_rr);
191190
192 if (field.trn_index != FieldsToDraw::kInvalidIndex) {191 if (cursor.trn_valid()) {
193 const int terrain_u = fields_to_draw.at(field.trn_index).fcoords.field->terrain_d();192 const int terrain_u = cursor.trn().fcoords.field->terrain_d();
194 maybe_add_dithering_triangle(gametime, terrains, fields_to_draw, field.rn_index,193 maybe_add_dithering_triangle(gametime, terrains, cursor.rn(),
195 current_index, field.brn_index,194 cursor.field(), cursor.brn(),
196 field.fcoords.field->terrain_r(), terrain_u);195 field.fcoords.field->terrain_r(), terrain_u);
197 }196 }
198 }197 }
199198
=== modified file 'src/graphic/gl/dither_program.h'
--- src/graphic/gl/dither_program.h 2016-10-16 09:31:42 +0000
+++ src/graphic/gl/dither_program.h 2017-01-07 12:36:16 +0000
@@ -38,7 +38,7 @@
38 // Draws the terrain.38 // Draws the terrain.
39 void draw(uint32_t gametime,39 void draw(uint32_t gametime,
40 const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,40 const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
41 const FieldsToDraw& fields_to_draw,41 const FieldsToDrawGl2& fields_to_draw,
42 float z_value);42 float z_value);
4343
44private:44private:
@@ -54,17 +54,16 @@
54 void maybe_add_dithering_triangle(54 void maybe_add_dithering_triangle(
55 uint32_t gametime,55 uint32_t gametime,
56 const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,56 const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
57 const FieldsToDraw& fields_to_draw,57 const FieldToDrawGl2& f1,
58 int idx1,58 const FieldToDrawGl2& f2,
59 int idx2,59 const FieldToDrawGl2& f3,
60 int idx3,
61 int my_terrain,60 int my_terrain,
62 int other_terrain);61 int other_terrain);
6362
64 // Adds the 'field' as an vertex to the 'vertices_'. The 'order_index'63 // Adds the 'field' as an vertex to the 'vertices_'. The 'order_index'
65 // defines which texture position in the dithering texture will be used for64 // defines which texture position in the dithering texture will be used for
66 // this vertex.65 // this vertex.
67 void add_vertex(const FieldsToDraw::Field& field,66 void add_vertex(const FieldToDrawGl2& field,
68 TrianglePoint triangle_point,67 TrianglePoint triangle_point,
69 const Vector2f& texture_offset);68 const Vector2f& texture_offset);
7069
7170
=== modified file 'src/graphic/gl/fields_to_draw.h'
--- src/graphic/gl/fields_to_draw.h 2016-10-24 20:07:22 +0000
+++ src/graphic/gl/fields_to_draw.h 2017-01-07 12:36:16 +0000
@@ -33,51 +33,47 @@
33#include "logic/widelands.h"33#include "logic/widelands.h"
34#include "logic/widelands_geometry.h"34#include "logic/widelands_geometry.h"
3535
36template<typename FTD>
37class FieldsToDrawCursor;
38
39template<typename F>
40class FieldsToDrawRef;
41
42struct FieldToDrawBase {
43 Widelands::FCoords fcoords; // The normalized coords and the field this is refering to.
44 Vector2f rendertarget_pixel;
45
46 // The next values are not necessarily the true data of this field, but
47 // what the player should see. For example in fog of war we always draw
48 // what we saw last.
49 //
50 // Note: we put the roads member variable here for better alignment even
51 // though it isn't needed in the Gl4 path.
52 Widelands::Player* owner; // can be nullptr.
53 uint8_t roads; // Bitmask of roads to render, see logic/roadtype.h.
54 bool is_border;
55 Widelands::Vision vision;
56};
57
58struct FieldToDrawGl2 : FieldToDrawBase {
59 Vector2f gl_position; // GL Position of this field.
60
61 // Surface pixel this will be plotted on.
62 Vector2f surface_pixel;
63
64 // Rendertarget pixel this will be plotted on. This is only different by
65 // the Rendertarget::get_rect().origin() of the view window.
66 Vector2f texture_coords; // Texture coordinates.
67 float brightness; // brightness of the pixel
68};
69
36// Helper struct that contains the data needed for drawing all fields. All70// Helper struct that contains the data needed for drawing all fields. All
37// methods are inlined for performance reasons.71// methods are inlined for performance reasons.
38class FieldsToDraw {72class FieldsToDrawImplBase {
39public:73public:
40 static constexpr int kInvalidIndex = std::numeric_limits<int>::min();74 static constexpr int kInvalidIndex = std::numeric_limits<int>::min();
4175
42 struct Field {76 FieldsToDrawImplBase() {
43 Widelands::Coords geometric_coords; // geometric coordinates (i.e. map coordinates that can
44 // be out of bounds).
45 Widelands::FCoords fcoords; // The normalized coords and the field this is refering to.
46 Vector2f gl_position; // GL Position of this field.
47
48 // Surface pixel this will be plotted on.
49 Vector2f surface_pixel;
50
51 // Rendertarget pixel this will be plotted on. This is only different by
52 // the Rendertarget::get_rect().origin() of the view window.
53 Vector2f rendertarget_pixel;
54 Vector2f texture_coords; // Texture coordinates.
55 float brightness; // brightness of the pixel
56
57 // The next values are not necessarily the true data of this field, but
58 // what the player should see. For example in fog of war we always draw
59 // what we saw last.
60 uint8_t roads; // Bitmask of roads to render, see logic/roadtype.h.
61 bool is_border;
62 Widelands::Vision vision;
63 Widelands::Player* owner; // can be nullptr.
64
65 // Index of neighbors in this 'FieldsToDraw'. kInvalidIndex if this
66 // neighbor is not contained.
67 int ln_index;
68 int rn_index;
69 int trn_index;
70 int bln_index;
71 int brn_index;
72
73 inline bool all_neighbors_valid() const {
74 return ln_index != kInvalidIndex && rn_index != kInvalidIndex &&
75 trn_index != kInvalidIndex && bln_index != kInvalidIndex &&
76 brn_index != kInvalidIndex;
77 }
78 };
79
80 FieldsToDraw() {
81 }77 }
8278
83 // Resize this fields to draw for reuse.79 // Resize this fields to draw for reuse.
@@ -87,55 +83,277 @@
87 min_fy_ = minfy;83 min_fy_ = minfy;
88 max_fy_ = maxfy;84 max_fy_ = maxfy;
89 w_ = max_fx_ - min_fx_ + 1;85 w_ = max_fx_ - min_fx_ + 1;
90 h_ = max_fy_ - min_fy_ + 1;86 }
91 const size_t dimension = w_ * h_;87
92 if (fields_.size() != dimension) {88 int min_fx() const {
93 fields_.resize(dimension);89 return min_fx_;
94 }90 }
95 }91
9692 int max_fx() const {
97 // Calculates the index of the given field with ('fx', 'fy') being geometric93 return max_fx_;
98 // coordinates in the map. Returns kInvalidIndex if this field is not in the94 }
99 // fields_to_draw.95
100 inline int calculate_index(int fx, int fy) const {96 int min_fy() const {
101 uint16_t xidx = fx - min_fx_;97 return min_fy_;
102 if (xidx >= w_) {98 }
103 return kInvalidIndex;99
104 }100 int max_fy() const {
105 uint16_t yidx = fy - min_fy_;101 return max_fy_;
106 if (yidx >= h_) {102 }
107 return kInvalidIndex;103
108 }104 int get_w() const {
109 return yidx * w_ + xidx;105 return w_;
110 }106 }
111107
112 // The number of fields to draw.108protected:
113 inline size_t size() const {
114 return fields_.size();
115 }
116
117 // Get the field at 'index' which must be in bound.
118 inline const Field& at(const int index) const {
119 return fields_.at(index);
120 }
121
122 // Returns a mutable field at 'index' which must be in bound.
123 inline Field* mutable_field(const int index) {
124 return &fields_[index];
125 }
126
127private:
128 // Minimum and maximum field coordinates (geometric) to render. Can be negative.109 // Minimum and maximum field coordinates (geometric) to render. Can be negative.
129 int min_fx_;110 int min_fx_;
130 int max_fx_;111 int max_fx_;
131 int min_fy_;112 int min_fy_;
132 int max_fy_;113 int max_fy_;
133114
134 // Width and height in number of fields.115 // Width in number of fields.
135 int w_;116 int w_;
136 int h_;117};
118
119// Helper struct that contains the data needed for drawing all fields. All
120// methods are inlined for performance reasons.
121template<typename Field_>
122class FieldsToDraw : public FieldsToDrawImplBase {
123public:
124 using Field = Field_;
125
126 FieldsToDraw() {
127 }
128
129 // Resize this fields to draw for reuse.
130 void reset(int minfx, int maxfx, int minfy, int maxfy) {
131 FieldsToDrawImplBase::reset(minfx, maxfx, minfy, maxfy);
132
133 int h = max_fy_ - min_fy_ + 1;
134 const size_t dimension = FieldsToDrawImplBase::get_w() * h;
135 if (fields_.size() != dimension) {
136 fields_.resize(dimension);
137 }
138 }
139
140 // The number of fields to draw.
141 inline size_t size() const {
142 return fields_.size();
143 }
144
145 Field& operator[](int index) {
146 return fields_[index];
147 }
148
149 const Field& operator[](int index) const {
150 return fields_[index];
151 }
152
153 FieldsToDrawCursor<FieldsToDraw> cursor();
154 FieldsToDrawCursor<const FieldsToDraw> cursor() const;
155
156private:
157 template<typename F>
158 friend class FieldsToDrawRef;
159
160 const Field* get_fields() const {
161 return &fields_[0];
162 }
163
164 intptr_t get_fields_stride() const {
165 return sizeof(fields_[0]);
166 }
137167
138 std::vector<Field> fields_;168 std::vector<Field> fields_;
139};169};
140170
171template<typename Field_>
172class FieldsToDrawRef : public FieldsToDrawImplBase {
173public:
174 using Field = Field_;
175
176 template<typename FTD>
177 FieldsToDrawRef(const FTD& fields_to_draw) {
178 reset(fields_to_draw.min_fx(), fields_to_draw.max_fx(),
179 fields_to_draw.min_fy(), fields_to_draw.max_fy());
180
181 // First assign to type Field* for type-checking, before reinterpreting
182 // to the char* that will be used for pointer arithmetic.
183 const Field* fields = fields_to_draw.get_fields();
184
185 fields_ = reinterpret_cast<const char*>(fields);
186 stride_ = fields_to_draw.get_fields_stride();
187 }
188
189 const Field& operator[](int index) const {
190 const char* base = fields_ + index * stride_;
191 return *reinterpret_cast<const Field*>(base);
192 }
193
194 FieldsToDrawCursor<const FieldsToDrawRef> cursor() const;
195
196private:
197 const char* fields_;
198 intptr_t stride_;
199};
200
201using FieldsToDrawBase = FieldsToDraw<FieldToDrawBase>;
202using FieldsToDrawGl2 = FieldsToDraw<FieldToDrawGl2>;
203using FieldsToDrawRefBase = FieldsToDrawRef<FieldToDrawBase>;
204
205// For iteration over fields.
206//
207// Template for const-correctness.
208template<typename FTD>
209class FieldsToDrawCursor {
210public:
211 using Field = typename FTD::Field;
212
213 FieldsToDrawCursor(FTD& fields_to_draw)
214 : fields_(fields_to_draw) {
215 type_check(fields_to_draw);
216
217 geometric_coords_.x = fields_.min_fx();
218 geometric_coords_.y = fields_.min_fy();
219 geometric_tblx_shift_ = (fields_.min_fy() & 1) - 1;
220
221 index_ = 0;
222 }
223
224 bool valid() const {
225 return index_ >= 0;
226 }
227
228 void next() {
229 assert(valid());
230
231 index_++;
232 geometric_coords_.x++;
233 if (geometric_coords_.x > fields_.max_fx()) {
234 geometric_coords_.x = fields_.min_fx();
235 geometric_tblx_shift_ = -1 - geometric_tblx_shift_;
236 geometric_coords_.y++;
237 if (geometric_coords_.y > fields_.max_fy())
238 index_ = -1;
239 }
240 }
241
242 const Field& field() const {
243 assert(valid());
244 return fields_[index_];
245 }
246
247 Field& mutable_field() {
248 assert(valid());
249 return fields_[index_];
250 }
251
252 // Return current geometric coordinates (i.e. map coordinates that can
253 // be out of bounds).
254 Widelands::Coords geometric_coords() const {
255 assert(valid());
256 return geometric_coords_;
257 }
258
259 bool tln_valid() const {
260 assert(valid());
261 return (geometric_coords_.y > fields_.min_fy()) &&
262 (geometric_coords_.x + geometric_tblx_shift_ >= fields_.min_fx());
263 }
264
265 const Field& tln() const {
266 assert(tln_valid());
267 return fields_[index_ + geometric_tblx_shift_ - fields_.get_w()];
268 }
269
270 bool trn_valid() const {
271 assert(valid());
272 return (geometric_coords_.y > fields_.min_fy()) &&
273 (geometric_coords_.x + geometric_tblx_shift_ + 1 <= fields_.max_fx());
274 }
275
276 const Field& trn() const {
277 assert(trn_valid());
278 return fields_[index_ + geometric_tblx_shift_ + 1 - fields_.get_w()];
279 }
280
281 bool ln_valid() const {
282 assert(valid());
283 return (geometric_coords_.x - 1 >= fields_.min_fx());
284 }
285
286 const Field& ln() const {
287 assert(ln_valid());
288 return fields_[index_ - 1];
289 }
290
291 bool rn_valid() const {
292 assert(valid());
293 return (geometric_coords_.x + 1 <= fields_.max_fx());
294 }
295
296 const Field& rn() const {
297 assert(rn_valid());
298 return fields_[index_ + 1];
299 }
300
301 bool bln_valid() const {
302 assert(valid());
303 return (geometric_coords_.y < fields_.max_fy()) &&
304 (geometric_coords_.x + geometric_tblx_shift_ >= fields_.min_fx());
305 }
306
307 const Field& bln() const {
308 assert(bln_valid());
309 return fields_[index_ + geometric_tblx_shift_ + fields_.get_w()];
310 }
311
312 bool brn_valid() const {
313 assert(valid());
314 return (geometric_coords_.y < fields_.max_fy()) &&
315 (geometric_coords_.x + geometric_tblx_shift_ + 1 <= fields_.max_fx());
316 }
317
318 const Field& brn() const {
319 assert(brn_valid());
320 return fields_[index_ + geometric_tblx_shift_ + 1 + fields_.get_w()];
321 }
322
323 bool all_neighbors_valid() const {
324 assert(valid());
325 return (geometric_coords_.y > fields_.min_fy() &&
326 geometric_coords_.y < fields_.max_fy() &&
327 geometric_coords_.x > fields_.min_fx() &&
328 geometric_coords_.x < fields_.max_fx());
329 }
330
331private:
332 template<typename F>
333 void type_check(const FieldsToDraw<F>&) {}
334 template<typename F>
335 void type_check(const FieldsToDrawRef<F>&) {}
336
337 FTD& fields_;
338
339 Widelands::Coords geometric_coords_;
340 int geometric_tblx_shift_; // top/bottom left neighbor geometric x-coordinate offset
341 int index_;
342};
343
344template<typename F>
345inline FieldsToDrawCursor<FieldsToDraw<F>> FieldsToDraw<F>::cursor() {
346 return {*this};
347}
348
349template<typename F>
350inline FieldsToDrawCursor<const FieldsToDraw<F>> FieldsToDraw<F>::cursor() const {
351 return {*this};
352}
353
354template<typename F>
355inline FieldsToDrawCursor<const FieldsToDrawRef<F>> FieldsToDrawRef<F>::cursor() const {
356 return {*this};
357}
358
141#endif // end of include guard: WL_GRAPHIC_GL_FIELDS_TO_DRAW_H359#endif // end of include guard: WL_GRAPHIC_GL_FIELDS_TO_DRAW_H
142360
=== modified file 'src/graphic/gl/initialize.cc'
--- src/graphic/gl/initialize.cc 2016-08-04 15:49:05 +0000
+++ src/graphic/gl/initialize.cc 2017-01-07 12:36:16 +0000
@@ -135,6 +135,8 @@
135 log("Graphics: OpenGL: ShadingLanguage: \"%s\"\n",135 log("Graphics: OpenGL: ShadingLanguage: \"%s\"\n",
136 reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION)));136 reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION)));
137137
138 Gl::State::instance().check_capabilities();
139
138 glDrawBuffer(GL_BACK);140 glDrawBuffer(GL_BACK);
139141
140 glDisable(GL_DEPTH_TEST);142 glDisable(GL_DEPTH_TEST);
141143
=== modified file 'src/graphic/gl/road_program.cc'
--- src/graphic/gl/road_program.cc 2016-12-01 17:35:34 +0000
+++ src/graphic/gl/road_program.cc 2017-01-07 12:36:16 +0000
@@ -48,8 +48,9 @@
4848
49void RoadProgram::add_road(const int renderbuffer_width,49void RoadProgram::add_road(const int renderbuffer_width,
50 const int renderbuffer_height,50 const int renderbuffer_height,
51 const FieldsToDraw::Field& start,51 Widelands::Coords geometric_coords,
52 const FieldsToDraw::Field& end,52 const FieldToDrawGl2& start,
53 const FieldToDrawGl2& end,
53 const float scale,54 const float scale,
54 const Widelands::RoadType road_type,55 const Widelands::RoadType road_type,
55 const Direction direction,56 const Direction direction,
@@ -82,8 +83,8 @@
82 const Image& texture =83 const Image& texture =
83 road_type == Widelands::RoadType::kNormal ?84 road_type == Widelands::RoadType::kNormal ?
84 visible_owner->tribe().road_textures().get_normal_texture(85 visible_owner->tribe().road_textures().get_normal_texture(
85 start.geometric_coords, direction) :86 geometric_coords, direction) :
86 visible_owner->tribe().road_textures().get_busy_texture(start.geometric_coords, direction);87 visible_owner->tribe().road_textures().get_busy_texture(geometric_coords, direction);
87 if (*gl_texture == 0) {88 if (*gl_texture == 0) {
88 *gl_texture = texture.blit_data().texture_id;89 *gl_texture = texture.blit_data().texture_id;
89 }90 }
@@ -135,42 +136,42 @@
135136
136void RoadProgram::draw(const int renderbuffer_width,137void RoadProgram::draw(const int renderbuffer_width,
137 const int renderbuffer_height,138 const int renderbuffer_height,
138 const FieldsToDraw& fields_to_draw,139 const FieldsToDrawGl2& fields_to_draw,
139 const float scale,140 const float scale,
140 const float z_value) {141 const float z_value) {
141 vertices_.clear();142 vertices_.clear();
142143
143 uint32_t gl_texture = 0;144 uint32_t gl_texture = 0;
144 for (size_t current_index = 0; current_index < fields_to_draw.size(); ++current_index) {145 for (auto cursor = fields_to_draw.cursor(); cursor.valid(); cursor.next()) {
145 const FieldsToDraw::Field& field = fields_to_draw.at(current_index);146 const FieldToDrawGl2& field = cursor.field();
146147
147 // Road to right neighbor.148 // Road to right neighbor.
148 if (field.rn_index != FieldsToDraw::kInvalidIndex) {149 if (cursor.rn_valid()) {
149 const Widelands::RoadType road =150 const Widelands::RoadType road =
150 static_cast<Widelands::RoadType>(field.roads & Widelands::RoadType::kMask);151 static_cast<Widelands::RoadType>(field.roads & Widelands::RoadType::kMask);
151 if (road != Widelands::RoadType::kNone) {152 if (road != Widelands::RoadType::kNone) {
152 add_road(renderbuffer_width, renderbuffer_height, field,153 add_road(renderbuffer_width, renderbuffer_height, cursor.geometric_coords(), field,
153 fields_to_draw.at(field.rn_index), scale, road, kEast, &gl_texture);154 cursor.rn(), scale, road, kEast, &gl_texture);
154 }155 }
155 }156 }
156157
157 // Road to bottom right neighbor.158 // Road to bottom right neighbor.
158 if (field.brn_index != FieldsToDraw::kInvalidIndex) {159 if (cursor.brn_valid()) {
159 const Widelands::RoadType road =160 const Widelands::RoadType road =
160 static_cast<Widelands::RoadType>((field.roads >> 2) & Widelands::RoadType::kMask);161 static_cast<Widelands::RoadType>((field.roads >> 2) & Widelands::RoadType::kMask);
161 if (road != Widelands::RoadType::kNone) {162 if (road != Widelands::RoadType::kNone) {
162 add_road(renderbuffer_width, renderbuffer_height, field,163 add_road(renderbuffer_width, renderbuffer_height, cursor.geometric_coords(), field,
163 fields_to_draw.at(field.brn_index), scale, road, kSouthEast, &gl_texture);164 cursor.brn(), scale, road, kSouthEast, &gl_texture);
164 }165 }
165 }166 }
166167
167 // Road to bottom right neighbor.168 // Road to bottom right neighbor.
168 if (field.bln_index != FieldsToDraw::kInvalidIndex) {169 if (cursor.bln_valid()) {
169 const Widelands::RoadType road =170 const Widelands::RoadType road =
170 static_cast<Widelands::RoadType>((field.roads >> 4) & Widelands::RoadType::kMask);171 static_cast<Widelands::RoadType>((field.roads >> 4) & Widelands::RoadType::kMask);
171 if (road != Widelands::RoadType::kNone) {172 if (road != Widelands::RoadType::kNone) {
172 add_road(renderbuffer_width, renderbuffer_height, field,173 add_road(renderbuffer_width, renderbuffer_height, cursor.geometric_coords(), field,
173 fields_to_draw.at(field.bln_index), scale, road, kSouthWest, &gl_texture);174 cursor.bln(), scale, road, kSouthWest, &gl_texture);
174 }175 }
175 }176 }
176 }177 }
177178
=== modified file 'src/graphic/gl/road_program.h'
--- src/graphic/gl/road_program.h 2016-11-03 07:20:57 +0000
+++ src/graphic/gl/road_program.h 2017-01-07 12:36:16 +0000
@@ -41,7 +41,7 @@
41 // space.41 // space.
42 void draw(int renderbuffer_width,42 void draw(int renderbuffer_width,
43 int renderbuffer_height,43 int renderbuffer_height,
44 const FieldsToDraw& fields_to_draw,44 const FieldsToDrawGl2& fields_to_draw,
45 float scale,45 float scale,
46 float z_value);46 float z_value);
4747
@@ -60,8 +60,9 @@
60 enum Direction { kEast, kSouthEast, kSouthWest };60 enum Direction { kEast, kSouthEast, kSouthWest };
61 void add_road(int renderbuffer_width,61 void add_road(int renderbuffer_width,
62 int renderbuffer_height,62 int renderbuffer_height,
63 const FieldsToDraw::Field& start,63 Widelands::Coords geometric_coords,
64 const FieldsToDraw::Field& end,64 const FieldToDrawGl2& start,
65 const FieldToDrawGl2& end,
65 float scale,66 float scale,
66 const Widelands::RoadType road_type,67 const Widelands::RoadType road_type,
67 const Direction direction,68 const Direction direction,
6869
=== added file 'src/graphic/gl/streaming_buffer.h'
--- src/graphic/gl/streaming_buffer.h 1970-01-01 00:00:00 +0000
+++ src/graphic/gl/streaming_buffer.h 2017-01-07 12:36:16 +0000
@@ -0,0 +1,173 @@
1/*
2 * Copyright 2016 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19#ifndef WL_GRAPHIC_GL_STREAMING_BUFFER_H
20#define WL_GRAPHIC_GL_STREAMING_BUFFER_H
21
22#include <cassert>
23
24#include "base/macros.h"
25#include "base/wexception.h"
26#include "graphic/gl/system_headers.h"
27
28namespace Gl {
29
30// Wrapper around an OpenGL buffer object that is intended for streaming use.
31//
32// Requires GL_ARB_direct_state_access.
33template <typename T> class StreamingBuffer {
34public:
35 class Inserter {
36 public:
37 Inserter(Inserter&& o)
38 : buffer_(o.buffer_), map_(o.map_), count_(o.count_), max_(o.max_) {
39 o.buffer_ = nullptr;
40 o.map_ = nullptr;
41 }
42
43 ~Inserter() {
44 assert(!buffer_ || buffer_->inserting_);
45 if (buffer_)
46 buffer_->inserting_ = false;
47
48 if (map_) {
49 buffer_->bind();
50 glUnmapBuffer(buffer_->target());
51 }
52 }
53
54 // The number of elements that have already been inserted.
55 size_t count() const {
56 return count_;
57 }
58
59 // The maximum number of elements that can be inserted.
60 size_t max() const {
61 return max_;
62 }
63
64 // Finish inserting. Return the buffer offset for the beginning of
65 // the inserted range, as must be passed to OpenGL.
66 GLintptr unmap() {
67 assert(map_);
68 buffer_->bind();
69 glUnmapBuffer(buffer_->target());
70 map_ = nullptr;
71 return 0;
72 }
73
74 // Append an item to the buffer.
75 template<typename... Args>
76 void emplace_back(Args&&... args) {
77 assert(map_);
78 assert(count_ < max_);
79
80 new(&map_[count_]) T(std::forward<Args>(args)...);
81 count_++;
82 }
83
84 // Append space for count items, return a pointer to the first one.
85 T* add(size_t count) {
86 assert(count <= max_ && count_ <= max_ - count);
87 T* ret = &map_[count_];
88 count_ += count;
89 return ret;
90 }
91
92 T& back() {
93 assert(map_ && count_ >= 1);
94 return map_[count_ - 1];
95 }
96
97 private:
98 friend class StreamingBuffer;
99
100 Inserter(StreamingBuffer& buffer, size_t max)
101 : buffer_(&buffer), map_(nullptr), count_(0), max_(max)
102 {
103 assert(!buffer_->inserting_);
104 buffer_->inserting_ = true;
105
106 buffer_->bind();
107 map_ = reinterpret_cast<T*>(glMapBuffer(buffer_->target(), GL_WRITE_ONLY));
108 if (!map_)
109 throw wexception("Could not map GL buffer.");
110 }
111
112 StreamingBuffer* buffer_;
113 T* map_;
114 size_t count_;
115 size_t max_;
116
117 DISALLOW_COPY_AND_ASSIGN(Inserter);
118 };
119
120 StreamingBuffer(GLenum target) {
121 target_ = target;
122 glGenBuffers(1, &object_);
123 if (!object_) {
124 throw wexception("Could not create GL buffer.");
125 }
126 }
127
128 ~StreamingBuffer() {
129 assert(!inserting_);
130
131 if (object_) {
132 glDeleteBuffers(1, &object_);
133 }
134 }
135
136 // Returns the OpenGL object for direct use.
137 GLuint object() const {
138 return object_;
139 }
140
141 GLenum target() const {
142 return target_;
143 }
144
145 void bind() const {
146 glBindBuffer(target_, object_);
147 }
148
149 // Set the buffer up for streaming up to the given number of elements.
150 //
151 // Previous contents of the buffer are discarded (this does not affect
152 // OpenGL functions that have been called previously).
153 Inserter stream(size_t max) {
154 // Always re-allocate the buffer. We rely on fast swap-out by the
155 // driver. If backing store were to be shared globally, it might make
156 // sense to consider an alternative scheme using unsynchronized maps
157 // and explicit flushing.
158 glBindBuffer(target_, object_);
159 glBufferData(target_, sizeof(T) * max, NULL, GL_STREAM_DRAW);
160 return Inserter(*this, max);
161 }
162
163private:
164 GLenum target_;
165 GLuint object_;
166 bool inserting_ = false;
167
168 DISALLOW_COPY_AND_ASSIGN(StreamingBuffer);
169};
170
171} // namespace Gl
172
173#endif // end of include guard: WL_GRAPHIC_GL_STREAMING_BUFFER_H
0174
=== modified file 'src/graphic/gl/terrain_program.cc'
--- src/graphic/gl/terrain_program.cc 2016-11-03 07:20:57 +0000
+++ src/graphic/gl/terrain_program.cc 2017-01-07 12:36:16 +0000
@@ -30,6 +30,8 @@
30// http://www.opengl.org/registry/doc/GLSLangSpec.Full.1.20.8.pdf30// http://www.opengl.org/registry/doc/GLSLangSpec.Full.1.20.8.pdf
31// We target OpenGL 2.1 for the desktop here.31// We target OpenGL 2.1 for the desktop here.
32TerrainProgram::TerrainProgram() {32TerrainProgram::TerrainProgram() {
33 log("Using GL2 terrain rendering path\n");
34
33 gl_program_.build("terrain");35 gl_program_.build("terrain");
3436
35 attr_brightness_ = glGetAttribLocation(gl_program_.object(), "attr_brightness");37 attr_brightness_ = glGetAttribLocation(gl_program_.object(), "attr_brightness");
@@ -70,7 +72,7 @@
70 glDrawArrays(GL_TRIANGLES, 0, vertices_.size());72 glDrawArrays(GL_TRIANGLES, 0, vertices_.size());
71}73}
7274
73void TerrainProgram::add_vertex(const FieldsToDraw::Field& field, const Vector2f& texture_offset) {75void TerrainProgram::add_vertex(const FieldToDrawGl2& field, const Vector2f& texture_offset) {
74 vertices_.emplace_back();76 vertices_.emplace_back();
75 PerVertexData& back = vertices_.back();77 PerVertexData& back = vertices_.back();
7678
@@ -85,7 +87,7 @@
8587
86void TerrainProgram::draw(uint32_t gametime,88void TerrainProgram::draw(uint32_t gametime,
87 const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,89 const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
88 const FieldsToDraw& fields_to_draw,90 const FieldsToDrawGl2& fields_to_draw,
89 float z_value) {91 float z_value) {
90 // This method expects that all terrains have the same dimensions and that92 // This method expects that all terrains have the same dimensions and that
91 // all are packed into the same texture atlas, i.e. all are in the same GL93 // all are packed into the same texture atlas, i.e. all are in the same GL
@@ -94,36 +96,36 @@
94 vertices_.clear();96 vertices_.clear();
95 vertices_.reserve(fields_to_draw.size() * 3);97 vertices_.reserve(fields_to_draw.size() * 3);
9698
97 for (size_t current_index = 0; current_index < fields_to_draw.size(); ++current_index) {99 for (auto cursor = fields_to_draw.cursor(); cursor.valid(); cursor.next()) {
98 const FieldsToDraw::Field& field = fields_to_draw.at(current_index);100 const FieldToDrawGl2& field = cursor.field();
99101
100 // The bottom right neighbor fields_to_draw is needed for both triangles102 // The bottom right neighbor fields_to_draw is needed for both triangles
101 // associated with this field. If it is not in fields_to_draw, there is no need to103 // associated with this field. If it is not in fields_to_draw, there is no need to
102 // draw any triangles.104 // draw any triangles.
103 if (field.brn_index == FieldsToDraw::kInvalidIndex) {105 if (!cursor.brn_valid()) {
104 continue;106 continue;
105 }107 }
106108
107 // Down triangle.109 // Down triangle.
108 if (field.bln_index != FieldsToDraw::kInvalidIndex) {110 if (cursor.bln_valid()) {
109 const Vector2f texture_offset =111 const Vector2f texture_offset =
110 to_gl_texture(112 to_gl_texture(
111 terrains.get(field.fcoords.field->terrain_d()).get_texture(gametime).blit_data())113 terrains.get(field.fcoords.field->terrain_d()).get_texture(gametime).blit_data())
112 .origin();114 .origin();
113 add_vertex(fields_to_draw.at(current_index), texture_offset);115 add_vertex(field, texture_offset);
114 add_vertex(fields_to_draw.at(field.bln_index), texture_offset);116 add_vertex(cursor.bln(), texture_offset);
115 add_vertex(fields_to_draw.at(field.brn_index), texture_offset);117 add_vertex(cursor.brn(), texture_offset);
116 }118 }
117119
118 // Right triangle.120 // Right triangle.
119 if (field.rn_index != FieldsToDraw::kInvalidIndex) {121 if (cursor.rn_valid()) {
120 const Vector2f texture_offset =122 const Vector2f texture_offset =
121 to_gl_texture(123 to_gl_texture(
122 terrains.get(field.fcoords.field->terrain_r()).get_texture(gametime).blit_data())124 terrains.get(field.fcoords.field->terrain_r()).get_texture(gametime).blit_data())
123 .origin();125 .origin();
124 add_vertex(fields_to_draw.at(current_index), texture_offset);126 add_vertex(field, texture_offset);
125 add_vertex(fields_to_draw.at(field.brn_index), texture_offset);127 add_vertex(cursor.brn(), texture_offset);
126 add_vertex(fields_to_draw.at(field.rn_index), texture_offset);128 add_vertex(cursor.rn(), texture_offset);
127 }129 }
128 }130 }
129131
130132
=== modified file 'src/graphic/gl/terrain_program.h'
--- src/graphic/gl/terrain_program.h 2016-10-16 09:31:42 +0000
+++ src/graphic/gl/terrain_program.h 2017-01-07 12:36:16 +0000
@@ -36,7 +36,7 @@
36 // Draws the terrain.36 // Draws the terrain.
37 void draw(uint32_t gametime,37 void draw(uint32_t gametime,
38 const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,38 const DescriptionMaintainer<Widelands::TerrainDescription>& terrains,
39 const FieldsToDraw& fields_to_draw,39 const FieldsToDrawGl2& fields_to_draw,
40 float z_value);40 float z_value);
4141
42private:42private:
@@ -54,7 +54,7 @@
54 void gl_draw(int gl_texture, float texture_w, float texture_h, float z_value);54 void gl_draw(int gl_texture, float texture_w, float texture_h, float z_value);
5555
56 // Adds a vertex to the end of vertices with data from 'field' and 'texture_coordinates'.56 // Adds a vertex to the end of vertices with data from 'field' and 'texture_coordinates'.
57 void add_vertex(const FieldsToDraw::Field& field, const Vector2f& texture_coordinates);57 void add_vertex(const FieldToDrawGl2& field, const Vector2f& texture_coordinates);
5858
59 // The program used for drawing the terrain.59 // The program used for drawing the terrain.
60 Gl::Program gl_program_;60 Gl::Program gl_program_;
6161
=== added file 'src/graphic/gl/terrain_program_gl4.cc'
--- src/graphic/gl/terrain_program_gl4.cc 1970-01-01 00:00:00 +0000
+++ src/graphic/gl/terrain_program_gl4.cc 2017-01-07 12:36:16 +0000
@@ -0,0 +1,1065 @@
1/*
2 * Copyright (C) 2006-2016 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 *
18 */
19
20#include "graphic/gl/terrain_program_gl4.h"
21
22#include "graphic/game_renderer_gl4.h"
23#include "graphic/gl/coordinate_conversion.h"
24#include "graphic/gl/utils.h"
25#include "graphic/image_io.h"
26#include "io/filesystem/layered_filesystem.h"
27#include "logic/editor_game_base.h"
28#include "logic/map.h"
29#include "logic/map_objects/tribes/tribe_descr.h"
30#include "logic/map_objects/world/terrain_description.h"
31#include "logic/map_objects/world/world.h"
32#include "profile/profile.h"
33#include "wui/mapviewpixelfunctions.h"
34
35using namespace Widelands;
36
37/**
38 * This is the back-end of the GL4 rendering path.
39 *
40 * Per-field data is uploaded directly into integer-valued textures that span
41 * the entire map, and vertex shaders do most of the heavy lifting. The
42 * following textures are used:
43 *
44 * Fields texture: data that is usually constant throughout a game, GL_RGBA8UI:
45 * R = r-triangle texture index
46 * G = d-triangle texture index
47 * B = height
48 * A = brightness
49 * This information can be modified in the editor and in scenarios. Note that
50 * the triangle textures depend on the player perspective.
51 *
52 * Player brightness texture:
53 * R = player-perspective-dependent brightness modulation
54 * This information is re-uploaded every frame.
55 *
56 * Semi-permanent information (GL_R8UI):
57 * R = bits 0..5: field ownership (player number)
58 * bits 6..7: road/flag/building data:
59 * 0: nothing, 1: road, 2: flag, 3: building
60 * This information is only needed for the minimap, and re-uploaded every frame
61 * when it is shown.
62 *
63 * Terrain is rendered in patches of a fixed structure, and many patches are
64 * rendered in one call via instancing. Per-instance data is processed in a
65 * vertex shader.
66 *
67 * Each patch consists of the triangles associated to a WxH "rectangle" of
68 * fields, where the top-left field must be at an even y-coordinate, and H is
69 * even, e.g. a 2x4-patch:
70 *
71 * (0,0)
72 * O-------O-------*
73 * / \ / \ /
74 * / \ / \ /
75 * / \ / \ /
76 * *-------O-------O-------*
77 * / \ / \ /
78 * / \ / \ /
79 * / \ / \ /
80 * O-------O-------*
81 * / \ / \ /
82 * / \ / \ /
83 * / \ / \ /
84 * *-------O-------O-------*
85 * / \ / \ /
86 * / \ / \ /
87 * / \ / \ /
88 * *-------*-------*
89 *
90 * OpenGL vertices of triangles are not shared; this allows separate textures
91 * and dithering in a single pass.
92 *
93 * Road rendering is also handled here. Roads are rendered as two triangles per
94 * segment. Only per-road data is uploaded; the vertex shader sources data
95 * from the per-road buffer based on the vertex ID, and an index buffer
96 * (element array buffer in OpenGL terms) is used to share two vertices between
97 * the triangles that make up each segment.
98 */
99
100TerrainInformationGl4::GlobalMap TerrainInformationGl4::global_map_;
101
102std::shared_ptr<TerrainInformationGl4>
103TerrainInformationGl4::get(const Widelands::EditorGameBase& egbase,
104 const Widelands::Player* player) {
105 GlobalKey key(&egbase, player);
106 auto it = global_map_.find(key);
107 if (it != global_map_.end())
108 return it->second.lock();
109
110 std::shared_ptr<TerrainInformationGl4> instance(
111 new TerrainInformationGl4(egbase, player));
112 global_map_[key] = instance;
113 return instance;
114}
115
116TerrainInformationGl4::TerrainInformationGl4(const Widelands::EditorGameBase& egbase,
117 const Widelands::Player* player)
118 : egbase_(egbase), player_(player), uploads_(GL_PIXEL_UNPACK_BUFFER) {
119 glGenTextures(1, &brightness_texture_);
120 glGenTextures(1, &fields_texture_);
121 glGenTextures(1, &minimap_texture_);
122
123 const Map& map = egbase.map();
124 auto& gl = Gl::State::instance();
125 gl.bind(GL_TEXTURE0, fields_texture_);
126 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
127 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
128 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
129 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
130
131 gl.bind(GL_TEXTURE0, brightness_texture_);
132 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
133 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
134 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
135 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
136 glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, map.get_width(), map.get_height(), 0,
137 GL_RED, GL_UNSIGNED_BYTE, NULL);
138 brightness_see_all_ = false;
139
140 gl.bind(GL_TEXTURE0, minimap_texture_);
141 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
142 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
143 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
144 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
145 glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, map.get_width(), map.get_height(), 0,
146 GL_RED_INTEGER, GL_UNSIGNED_BYTE, NULL);
147
148 fields_update();
149 upload_road_textures();
150 upload_constant_textures();
151
152 updated_minimap_ = false;
153 need_update_minimap_ = false;
154 minimap_update_next_ = 0;
155}
156
157TerrainInformationGl4::~TerrainInformationGl4() {
158 if (brightness_texture_)
159 glDeleteTextures(1, &brightness_texture_);
160
161 if (fields_texture_)
162 glDeleteTextures(1, &fields_texture_);
163
164 if (minimap_texture_)
165 glDeleteTextures(1, &minimap_texture_);
166
167 if (terrain_color_texture_)
168 glDeleteTextures(1, &terrain_color_texture_);
169
170 global_map_.erase(GlobalKey(&egbase_, player_));
171}
172
173/// Add @p rect to @p rects, merging it with any overlapping or touching
174/// pre-existing rects (where merging means that the rectangles are replaced by
175/// a single rectangle that contains their union). The order of rectangles in
176/// @p rects is not preserved.
177///
178/// Rectangles are interpreted as half-open.
179static void add_rect(std::vector<Recti>& rects, const Recti& rect) {
180 Recti new_rect = rect;
181
182 for (size_t i = 0; i < rects.size(); ++i) {
183 // Merge rectangles even if they only touch instead of fully overlapping.
184 // The rationale is that reducing the number of uploads is often a
185 // benefit even when the total size of uploads becomes larger.
186 if (new_rect.x + new_rect.w < rects[i].x ||
187 rects[i].x + rects[i].w < new_rect.x ||
188 new_rect.y + new_rect.h < rects[i].y ||
189 rects[i].y + rects[i].h < new_rect.y)
190 continue;
191
192 rects[i] = rects.back();
193 rects.pop_back();
194 i--;
195 }
196
197 rects.push_back(new_rect);
198}
199
200void TerrainInformationGl4::update(int minfx, int maxfx, int minfy, int maxfy) {
201 const Map& map = egbase().map();
202 int width = map.get_width();
203 int height = map.get_height();
204
205 auto normalize = [](int& min, int& max, int size) {
206 while (min < 0) {
207 min += size;
208 max += size;
209 }
210 while (min >= size) {
211 min -= size;
212 max -= size;
213 }
214 };
215
216 normalize(minfx, maxfx, width);
217 normalize(minfy, maxfy, height);
218
219 // Ensure proper row alignment during texture uploads.
220 minfx = (minfx / 4) * 4;
221 maxfx = ((maxfx + 4) / 4) * 4 - 1;
222
223 auto add = [&](int startx, int endx) {
224 add_rect(update_, Recti(startx, minfy, endx - startx, std::min(maxfy + 1, height) - minfy));
225 if (maxfy >= height)
226 add_rect(update_, Recti(startx, 0, endx - startx, std::min(maxfy + 1 - height, height)));
227 };
228
229 add(minfx, std::min(maxfx + 1, width));
230 if (maxfx >= width)
231 add(0, std::min(maxfx + 1 - width, width));
232}
233
234void TerrainInformationGl4::update_minimap() {
235 need_update_minimap_ = true;
236}
237
238void TerrainInformationGl4::do_prepare_frame() {
239 const Map& map = egbase().map();
240
241 upload_terrain_data();
242
243 if (need_update_minimap_) {
244 if (!updated_minimap_) {
245 // Need a full update when the minimap is drawn for the first
246 // time.
247 update_.clear();
248 update_.emplace_back(0, 0, map.get_width(), map.get_height());
249 } else {
250 // For the minimap, we want to do rolling texture updates of
251 // stripes that cover the whole width or height of the map,
252 // depending on which is smaller. For consistency and simplicity,
253 // expand all other dirty rectangles to full strips as well.
254 //
255 // Furthermore, use stripes of a size that is a multiple of a small
256 // power of two, since that likely has bandwidth benefits due to
257 // how textures are laid out in memory. This also avoids confusion
258 // due to pixel (un)packing row alignments.
259 unsigned width = map.get_width();
260 unsigned height = map.get_height();
261 bool horiz = width <= height;
262 std::vector<std::pair<unsigned, unsigned>> stripes;
263
264 // Massage existing stripes, effectively a form of insertion sort.
265 stripes.reserve(update_.size() + 1);
266 for (size_t i = 0; i < update_.size(); ++i) {
267 unsigned min = horiz ? update_[i].y : update_[i].x;
268 unsigned max = min + (horiz ? update_[i].h : update_[i].w);
269
270 min = (min / 8) * 8;
271 max = ((max + 7) / 8) * 8;
272
273 size_t j;
274 for (j = 0; j < stripes.size(); ++j) {
275 if (max < stripes[j].first) {
276 stripes.insert(stripes.begin() + j, std::make_pair(min, max));
277 break;
278 }
279
280 if (min <= stripes[j].second) {
281 stripes[j].first = std::min(stripes[j].first, min);
282 stripes[j].second = std::max(stripes[j].second, max);
283
284 size_t k;
285 for (k = j + 1; k < stripes.size(); ++k) {
286 if (stripes[j].second < stripes[k].first)
287 break;
288
289 stripes[j].second = std::max(stripes[j].second, stripes[k].second);
290 }
291
292 stripes.erase(stripes.begin() + j + 1, stripes.begin() + k);
293 break;
294 }
295 }
296 if (j >= stripes.size())
297 stripes.emplace_back(min, max);
298 }
299
300 if (stripes.empty() || stripes[0].first != 0 ||
301 stripes[0].second < (horiz ? height : width)) {
302 // Add a stripe (or expand an existing one) for the rolling minimap
303 // update.
304 if (minimap_update_next_ >= (horiz ? height : width))
305 minimap_update_next_ = 0;
306
307 unsigned min = minimap_update_next_;
308 size_t j;
309 for (j = 0; j < stripes.size(); ++j) {
310 unsigned max = min + 8;
311 if (max < stripes[j].first) {
312 stripes.insert(stripes.begin() + j, std::make_pair(min, max));
313 break;
314 }
315
316 if (min <= stripes[j].second) {
317 if (min < stripes[j].first) {
318 assert(max == stripes[j].first); // due to multiples of 8
319 stripes[j].first = min;
320 break;
321 }
322
323 min = minimap_update_next_ = stripes[j].second;
324 if (min >= (horiz ? height : width)) {
325 min = 0;
326 j = 0;
327 continue;
328 }
329 max = std::min(min + 8, horiz ? height : width);
330 stripes[j].second = max;
331
332 size_t k;
333 for (k = j + 1; k < stripes.size(); ++k) {
334 if (stripes[j].second < stripes[k].first)
335 break;
336
337 stripes[j].second = std::max(stripes[j].second, stripes[k].second);
338 }
339 stripes.erase(stripes.begin() + j + 1, stripes.begin() + k);
340 break;
341 }
342 }
343 if (j >= stripes.size())
344 stripes.emplace_back(min, min + 8);
345
346 minimap_update_next_ += 8;
347 }
348
349 // Convert stripes back to update rectangles.
350 update_.resize(stripes.size());
351 for (size_t i = 0; i < stripes.size(); ++i) {
352 if (horiz) {
353 update_[i].x = 0;
354 update_[i].w = width;
355 update_[i].y = stripes[i].first;
356 update_[i].h = stripes[i].second - stripes[i].first;
357 } else {
358 update_[i].y = 0;
359 update_[i].h = height;
360 update_[i].x = stripes[i].first;
361 update_[i].w = stripes[i].second - stripes[i].first;
362 }
363 }
364 }
365 }
366
367 // Fields data updates are guarded by version numbers instead of
368 // rectangles.
369 if (fields_base_version_ != map.get_fields_base_version() ||
370 (player() && terrain_vision_version_ != player()->get_terrain_vision_version()))
371 fields_update();
372
373 brightness_update();
374
375 if (need_update_minimap_)
376 do_update_minimap();
377
378 update_.clear();
379 updated_minimap_ = need_update_minimap_;
380 need_update_minimap_ = false;
381}
382
383void TerrainInformationGl4::prepare_frame() {
384 for (auto& entries : global_map_) {
385 std::shared_ptr<TerrainInformationGl4> ti = entries.second.lock();
386
387 ti->do_prepare_frame();
388 }
389}
390
391void TerrainInformationGl4::do_update_minimap() {
392 // Re-upload minimap data.
393 auto& gl = Gl::State::instance();
394 const Map& map = egbase().map();
395 unsigned width = map.get_width();
396 std::vector<uint8_t> data;
397 const bool see_all = !player() || player()->see_all();
398
399 auto detail_bits = [&](const Widelands::BaseImmovable* imm) -> uint8_t {
400 if (imm) {
401 Widelands::MapObjectType type = imm->descr().type();
402 if (type == Widelands::MapObjectType::ROAD)
403 return 1u << 6;
404 if (type == Widelands::MapObjectType::FLAG)
405 return 2u << 6;
406 if (type >= Widelands::MapObjectType::BUILDING)
407 return 3u << 6;
408 }
409 return 0;
410 };
411
412 gl.bind(GL_TEXTURE0, minimap_texture_);
413
414 for (const Recti& rect : update_) {
415 data.resize(rect.w * rect.h);
416 if (see_all) {
417 unsigned i = 0;
418 for (unsigned y = 0; y < unsigned(rect.h); ++y) {
419 unsigned idx = (rect.y + y) * width + rect.x;
420 for (unsigned x = 0; x < unsigned(rect.w); ++x, ++i, ++idx) {
421 const Field& f = map[idx];
422 data[i] = f.get_owned_by();
423 data[i] |= detail_bits(f.get_immovable());
424 }
425 }
426 } else {
427 unsigned i = 0;
428 for (unsigned y = 0; y < unsigned(rect.h); ++y) {
429 unsigned idx = (rect.y + y) * width + rect.x;
430 for (unsigned x = 0; x < unsigned(rect.w); ++x, ++i, ++idx) {
431 const Player::Field& pf = player()->fields()[idx];
432 data[i] = pf.owner;
433
434 if (pf.vision >= 2) {
435 const Field& f = map[idx];
436 data[i] |= detail_bits(f.get_immovable());
437 }
438 }
439 }
440 }
441
442 glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x, rect.y, rect.w, rect.h,
443 GL_RED_INTEGER, GL_UNSIGNED_BYTE, data.data());
444 }
445}
446
447void TerrainInformationGl4::fields_update() {
448 auto& gl = Gl::State::instance();
449 const Map& map = egbase().map();
450 auto stream = uploads_.stream(sizeof(PerFieldData) * uint(map.get_width()) * map.get_height());
451 PerFieldData* fd =
452 reinterpret_cast<PerFieldData*>
453 (stream.add(sizeof(PerFieldData) * uint(map.get_width()) * map.get_height()));
454 MapIndex max_index = map.max_index();
455 const bool see_all = !player() || player()->see_all();
456
457 if (see_all) {
458 for (MapIndex i = 0; i < max_index; ++i) {
459 const Field& f = map[i];
460 fd[i].terrain_r = f.terrain_r();
461 fd[i].terrain_d = f.terrain_d();
462 fd[i].height = f.get_height();
463 fd[i].brightness = f.get_brightness();
464 }
465 } else {
466 const Player::Field* player_fields = player()->fields();
467
468 for (MapIndex i = 0; i < max_index; ++i) {
469 const Field& f = map[i];
470 const Player::Field& pf = player_fields[i];
471 fd[i].terrain_r = pf.terrains.r;
472 fd[i].terrain_d = pf.terrains.d;
473 fd[i].height = f.get_height();
474 fd[i].brightness = f.get_brightness();
475 }
476 }
477
478 GLintptr offset = stream.unmap();
479 uploads_.bind();
480
481 gl.bind(GL_TEXTURE0, fields_texture_);
482 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8UI, map.get_width(), map.get_height(), 0,
483 GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, reinterpret_cast<void*>(offset));
484
485 glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
486
487 fields_base_version_ = map.get_fields_base_version();
488 if (player())
489 terrain_vision_version_ = player()->get_terrain_vision_version();
490}
491
492TerrainInformationGl4::PerRoadTextureData::PerRoadTextureData(const Rectf& rect)
493 : x(rect.x), y(rect.y), w(rect.w), h(rect.h) {
494}
495
496void TerrainInformationGl4::upload_road_textures() {
497 std::vector<PerRoadTextureData> roads;
498 std::map<const TribeDescr*, unsigned> tribe_map;
499 PlayerNumber const nr_players = egbase().map().get_nrplayers();
500
501 player_roads_.resize(nr_players + 1);
502
503 iterate_players_existing_const(p, nr_players, egbase(), player) {
504 const TribeDescr& tribe = player->tribe();
505 auto it = tribe_map.find(&tribe);
506 if (it != tribe_map.end()) {
507 player_roads_[p] = player_roads_[it->second];
508 } else {
509 const auto& normal_textures = tribe.road_textures().get_normal_textures();
510 player_roads_[p].normal_roads = roads.size();
511 player_roads_[p].num_normal_roads = normal_textures.size();
512 for (const Image* image : normal_textures) {
513 const BlitData& blit_data = image->blit_data();
514 roads.emplace_back(to_gl_texture(blit_data));
515 road_texture_object_ = blit_data.texture_id;
516 }
517
518 const auto& busy_textures = tribe.road_textures().get_busy_textures();
519 player_roads_[p].busy_roads = roads.size();
520 player_roads_[p].num_busy_roads = busy_textures.size();
521 for (const Image* image : busy_textures)
522 roads.emplace_back(to_gl_texture(image->blit_data()));
523
524 tribe_map[&tribe] = p;
525 }
526 }
527
528 road_textures_.bind();
529 road_textures_.update(roads);
530}
531
532unsigned TerrainInformationGl4::road_texture_idx(PlayerNumber owner,
533 RoadType road_type,
534 const Coords& coords,
535 WalkingDir direction) const {
536 const PlayerRoads& roads = player_roads_[owner];
537 unsigned base, count;
538
539 if (road_type == RoadType::kNormal) {
540 base = roads.normal_roads;
541 count = roads.num_normal_roads;
542 } else {
543 base = roads.busy_roads;
544 count = roads.num_busy_roads;
545 }
546
547 return base + unsigned(coords.x + coords.y + direction) % count;
548}
549
550// Upload the per-terrain texture data. This is done on every draw call because
551// it depends on the gametime.
552void TerrainInformationGl4::upload_terrain_data() {
553 uint32_t gametime = egbase().get_gametime();
554 const auto& terrains = egbase().world().terrains();
555 std::vector<PerTerrainData> data;
556
557 data.resize(terrains.size());
558
559 for (unsigned i = 0; i < terrains.size(); ++i) {
560 PerTerrainData& terrain = data[i];
561 const TerrainDescription& descr = terrains.get(i);
562 terrain.offset =
563 to_gl_texture(descr.get_texture(gametime).blit_data()).origin();
564 terrain.dither_layer = descr.dither_layer();
565 }
566
567 terrain_data_.bind();
568 terrain_data_.update(data);
569}
570
571void TerrainInformationGl4::brightness_update() {
572 auto& gl = Gl::State::instance();
573 bool see_all = !player_ || player_->see_all();
574
575 gl.bind(GL_TEXTURE0, brightness_texture_);
576
577 if (see_all) {
578 if (!brightness_see_all_) {
579 // Pixel unpacking has a per-row alignment of 4 bytes. Usually this
580 // is not a problem for us, because maps' widths are always multiples
581 // of 4, but in this particular case, OpenGL implementations disagree
582 // about whether the alignment should be considered for the bounds
583 // check in glTexImage2D. If we only allocate 1 byte, some
584 // implementations flag a GL_INVALID_OPERATION.
585 static const uint8_t data[4] = {255, 255, 255, 255};
586
587 glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, 1, 1, 0, GL_RED, GL_UNSIGNED_BYTE,
588 data);
589 brightness_see_all_ = true;
590 }
591 } else {
592 const Map& map = egbase().map();
593 int width = map.get_width();
594 int height = map.get_height();
595 uint32_t gametime = egbase().get_gametime();
596 std::vector<uint8_t> data;
597
598 if (brightness_see_all_) {
599 // Resize the texture when switching between see-all and not-see-all.
600 glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED,
601 GL_UNSIGNED_BYTE, NULL);
602 brightness_see_all_ = false;
603 }
604
605 for (const Recti& rect : update_) {
606 data.resize(rect.w * rect.h);
607
608 unsigned dst = 0;
609 for (unsigned y = 0; y < unsigned(rect.h); ++y) {
610 unsigned src = (rect.y + y) * width + rect.x;
611 for (unsigned x = 0; x < unsigned(rect.w); ++x, ++src, ++dst) {
612 const Player::Field& pf = player_->fields()[src];
613 if (pf.vision == 0) {
614 data[dst] = 0;
615 } else if (pf.vision == 1) {
616 static const uint32_t kDecayTimeInMs = 20000;
617 const Duration time_ago = gametime - pf.time_node_last_unseen;
618 if (time_ago < kDecayTimeInMs) {
619 data[dst] = 255 * (2 * kDecayTimeInMs - time_ago) / (2 * kDecayTimeInMs);
620 } else {
621 data[dst] = 128;
622 }
623 } else {
624 data[dst] = 255;
625 }
626 }
627 }
628
629 glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x, rect.y, rect.w, rect.h,
630 GL_RED, GL_UNSIGNED_BYTE, &data[0]);
631 }
632 }
633}
634
635void TerrainInformationGl4::upload_constant_textures() {
636 auto& gl = Gl::State::instance();
637 const auto& terrains = egbase().world().terrains();
638 std::vector<RGBColor> colors;
639
640 for (Widelands::DescriptionIndex i = 0; i < terrains.size(); ++i)
641 colors.push_back(terrains.get(i).get_minimap_color(0));
642
643 glGenTextures(1, &terrain_color_texture_);
644
645 gl.bind(GL_TEXTURE0, terrain_color_texture_);
646 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, colors.size(), 1, 0, GL_RGB, GL_UNSIGNED_BYTE,
647 colors.data());
648 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
649 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
650 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
651 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
652
653 colors.resize(kMaxPlayers);
654 for (int i = 1; i <= kMaxPlayers; ++i) {
655 const Widelands::Player* player = egbase().get_player(i);
656 if (player)
657 colors[i - 1] = player->get_playercolor();
658 }
659
660 glGenTextures(1, &player_color_texture_);
661
662 gl.bind(GL_TEXTURE0, player_color_texture_);
663 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, colors.size(), 1, 0, GL_RGB, GL_UNSIGNED_BYTE,
664 colors.data());
665 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
666 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
667 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
668 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
669}
670
671TerrainProgramGl4::Terrain::Terrain()
672 : instance_data(GL_ARRAY_BUFFER) {
673 // Initialize program.
674 gl_program.build_vp_fp({"terrain_gl4", "terrain_common_gl4"}, {"terrain_gl4"});
675
676 in_vertex_coordinate = glGetAttribLocation(gl_program.object(), "in_vertex_coordinate");
677 in_patch_coordinate = glGetAttribLocation(gl_program.object(), "in_patch_coordinate");
678
679 u_position_scale = glGetUniformLocation(gl_program.object(), "u_position_scale");
680 u_position_offset = glGetUniformLocation(gl_program.object(), "u_position_offset");
681 u_z_value = glGetUniformLocation(gl_program.object(), "u_z_value");
682 u_texture_dimensions = glGetUniformLocation(gl_program.object(), "u_texture_dimensions");
683
684 u_terrain_base = glGetUniformLocation(gl_program.object(), "u_terrain_base");
685 u_player_brightness = glGetUniformLocation(gl_program.object(), "u_player_brightness");
686 u_terrain_texture = glGetUniformLocation(gl_program.object(), "u_terrain_texture");
687 u_dither_texture = glGetUniformLocation(gl_program.object(), "u_dither_texture");
688
689 block_terrains_idx = glGetUniformBlockIndex(gl_program.object(), "block_terrains");
690}
691
692TerrainProgramGl4::Terrain::~Terrain() {
693}
694
695TerrainProgramGl4::Roads::Roads()
696 : road_data(GL_SHADER_STORAGE_BUFFER) {
697 num_index_roads = 0;
698
699 // Initialize program.
700 gl_program.build_vp_fp({"road_gl4", "terrain_common_gl4"}, {"road"});
701
702 u_position_scale = glGetUniformLocation(gl_program.object(), "u_position_scale");
703 u_position_offset = glGetUniformLocation(gl_program.object(), "u_position_offset");
704 u_z_value = glGetUniformLocation(gl_program.object(), "u_z_value");
705
706 u_terrain_base = glGetUniformLocation(gl_program.object(), "u_terrain_base");
707 u_player_brightness = glGetUniformLocation(gl_program.object(), "u_player_brightness");
708 u_texture = glGetUniformLocation(gl_program.object(), "u_texture");
709
710 block_textures_idx = glGetUniformBlockIndex(gl_program.object(), "block_textures");
711}
712
713TerrainProgramGl4::Roads::~Roads() {
714}
715
716TerrainProgramGl4::MiniMap::MiniMap()
717 : vertex_data(GL_ARRAY_BUFFER) {
718 gl_program.build_vp_fp({"minimap_gl4"}, {"minimap_gl4"});
719
720 in_position = glGetAttribLocation(gl_program.object(), "in_position");
721 in_field = glGetAttribLocation(gl_program.object(), "in_field");
722
723 u_layer_terrain = glGetUniformLocation(gl_program.object(), "u_layer_terrain");
724 u_layer_owner = glGetUniformLocation(gl_program.object(), "u_layer_owner");
725 u_layer_details = glGetUniformLocation(gl_program.object(), "u_layer_details");
726
727 u_frame_topleft = glGetUniformLocation(gl_program.object(), "u_frame_topleft");
728 u_frame_bottomright = glGetUniformLocation(gl_program.object(), "u_frame_bottomright");
729
730 u_terrain_base = glGetUniformLocation(gl_program.object(), "u_terrain_base");
731 u_player_brightness = glGetUniformLocation(gl_program.object(), "u_player_brightness");
732 u_minimap_extra = glGetUniformLocation(gl_program.object(), "u_minimap_extra");
733 u_terrain_color = glGetUniformLocation(gl_program.object(), "u_terrain_color");
734 u_player_color = glGetUniformLocation(gl_program.object(), "u_player_color");
735}
736
737TerrainProgramGl4::MiniMap::~MiniMap() {
738}
739
740TerrainProgramGl4::TerrainProgramGl4() {
741 log("Using GL4 terrain rendering path\n");
742
743 // Initialize vertex buffer (every instance/path has the same structure).
744 init_vertex_data();
745
746 // Load mask texture for dithering.
747 terrain_.dither_mask.reset(new Texture(load_image_as_sdl_surface("world/pics/edge.png", g_fs), true));
748
749 Gl::State::instance().bind(GL_TEXTURE0, terrain_.dither_mask->blit_data().texture_id);
750 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, static_cast<GLint>(GL_CLAMP_TO_EDGE));
751 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, static_cast<GLint>(GL_CLAMP_TO_EDGE));
752 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, static_cast<GLint>(GL_LINEAR));
753 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, static_cast<GLint>(GL_LINEAR));
754}
755
756TerrainProgramGl4::~TerrainProgramGl4() {
757}
758
759bool TerrainProgramGl4::supported() {
760 const auto& caps = Gl::State::instance().capabilities();
761
762 if (caps.glsl_version < 130)
763 return false;
764
765 if (!caps.ARB_separate_shader_objects ||
766 !caps.ARB_shader_storage_buffer_object ||
767 !caps.ARB_uniform_buffer_object)
768 return false;
769
770 return !g_options.pull_section("global").get_bool("disable_gl4", false);
771}
772
773void TerrainProgramGl4::draw(const TerrainGl4Arguments* args,
774 float z_value) {
775 auto& gl = Gl::State::instance();
776
777 // First, draw the terrain.
778 glUseProgram(terrain_.gl_program.object());
779
780 // Coordinate transform from map coordinates to GL coordinates.
781 float scale_x = 2.0 / args->surface_width * args->scale;
782 float scale_y = -2.0 / args->surface_height * args->scale;
783 float offset_x = args->surface_offset.x * scale_x - 1.0;
784 float offset_y = args->surface_offset.y * scale_y + 1.0;
785
786 // Texture size
787 const BlitData& blit_data = args->terrain->egbase().world().terrains().get(0).get_texture(0).blit_data();
788 const Rectf texture_coordinates = to_gl_texture(blit_data);
789
790 // Prepare uniforms.
791 glBindBufferBase(GL_UNIFORM_BUFFER, terrain_.block_terrains_idx,
792 args->terrain->terrain_data_buffer_object());
793
794 glUniform2f(terrain_.u_position_scale, scale_x, scale_y);
795 glUniform2f(terrain_.u_position_offset, offset_x, offset_y);
796 glUniform1f(terrain_.u_z_value, z_value);
797 glUniform2f(terrain_.u_texture_dimensions, texture_coordinates.w, texture_coordinates.h);
798
799 // Prepare textures & sampler uniforms.
800 glUniform1i(terrain_.u_terrain_base, 0);
801 gl.bind(GL_TEXTURE0, args->terrain->fields_texture());
802
803 glUniform1i(terrain_.u_player_brightness, 1);
804 gl.bind(GL_TEXTURE1, args->terrain->player_brightness_texture());
805
806 glUniform1i(terrain_.u_terrain_texture, 2);
807 gl.bind(GL_TEXTURE2, blit_data.texture_id);
808
809 glUniform1i(terrain_.u_dither_texture, 3);
810 gl.bind(GL_TEXTURE3, terrain_.dither_mask->blit_data().texture_id);
811
812 // Setup vertex and instance attribute data.
813 gl.enable_vertex_attrib_array(
814 {terrain_.in_vertex_coordinate, terrain_.in_patch_coordinate});
815
816 unsigned num_instances = upload_instance_data(args);
817
818 terrain_.vertex_data.bind();
819 glVertexAttribIPointer(terrain_.in_vertex_coordinate, 4, GL_INT, sizeof(PerVertexData), nullptr);
820
821 glDrawArraysInstanced(GL_TRIANGLES, 0, 6 * kPatchWidth * kPatchHeight, num_instances);
822
823 glVertexBindingDivisor(terrain_.in_patch_coordinate, 0);
824}
825
826void TerrainProgramGl4::draw_minimap(const TerrainGl4Arguments* args,
827 float z_value) {
828 auto& gl = Gl::State::instance();
829 const Widelands::Map& map = args->terrain->egbase().map();
830 float width = map.get_width();
831 float height = map.get_height();
832
833 glUseProgram(minimap_.gl_program.object());
834
835 // Prepare minimap setting uniforms
836 glUniform1i(minimap_.u_layer_terrain, (args->minimap_layers & MiniMapLayer::Terrain) ? 1 : 0);
837 glUniform1i(minimap_.u_layer_owner, (args->minimap_layers & MiniMapLayer::Owner) ? 1 : 0);
838
839 uint details = 0;
840 if (args->minimap_layers & MiniMapLayer::Road)
841 details |= 1;
842 if (args->minimap_layers & MiniMapLayer::Flag)
843 details |= 2;
844 if (args->minimap_layers & MiniMapLayer::Building)
845 details |= 4;
846
847 glUniform1ui(minimap_.u_layer_details, details);
848
849 // Prepare textures & sampler uniforms.
850 glUniform1i(minimap_.u_terrain_base, 0);
851 gl.bind(GL_TEXTURE0, args->terrain->fields_texture());
852
853 glUniform1i(minimap_.u_player_brightness, 1);
854 gl.bind(GL_TEXTURE1, args->terrain->player_brightness_texture());
855
856 glUniform1i(minimap_.u_minimap_extra, 2);
857 gl.bind(GL_TEXTURE2, args->terrain->minimap_texture());
858
859 glUniform1i(minimap_.u_terrain_color, 3);
860 gl.bind(GL_TEXTURE3, args->terrain->terrain_color_texture());
861
862 glUniform1i(minimap_.u_player_color, 4);
863 gl.bind(GL_TEXTURE4, args->terrain->player_color_texture());
864
865 glUniform2f(minimap_.u_frame_topleft, (args->minfx + 0.001) / width, (args->minfy + 0.001) / height);
866 glUniform2f(minimap_.u_frame_bottomright, (args->maxfx - 0.001) / width, (args->maxfy - 0.001) / height);
867
868 // Compute coordinates and upload vertex data.
869 if (args->minimap_layers & MiniMapLayer::Zoom2) {
870 width *= 2;
871 height *= 2;
872 }
873
874 float left = args->surface_offset.x;
875 float right = left + width;
876 float top = args->surface_offset.y;
877 float bottom = top + height;
878
879 pixel_to_gl_renderbuffer(args->surface_width, args->surface_height, &left, &top);
880 pixel_to_gl_renderbuffer(args->surface_width, args->surface_height, &right, &bottom);
881
882 float tx = args->minimap_tl_fx * (1.0 / map.get_width());
883 float ty = args->minimap_tl_fy * (1.0 / map.get_height());
884
885 auto stream = minimap_.vertex_data.stream(4);
886 MiniMap::VertexData* v = stream.add(4);
887
888 v[0].x = left;
889 v[0].y = top;
890 v[0].z = z_value;
891 v[0].tx = tx;
892 v[0].ty = ty;
893
894 v[1].x = left;
895 v[1].y = bottom;
896 v[1].z = z_value;
897 v[1].tx = tx;
898 v[1].ty = ty + 1.0;
899
900 v[2].x = right;
901 v[2].y = top;
902 v[2].z = z_value;
903 v[2].tx = tx + 1.0;
904 v[2].ty = ty;
905
906 v[3].x = right;
907 v[3].y = bottom;
908 v[3].z = z_value;
909 v[3].tx = tx + 1.0;
910 v[3].ty = ty + 1.0;
911
912 GLintptr offset = stream.unmap();
913
914 gl.enable_vertex_attrib_array({minimap_.in_position, minimap_.in_field});
915
916 minimap_.vertex_data.bind();
917 Gl::vertex_attrib_pointer(minimap_.in_position, 3, sizeof(MiniMap::VertexData), offset);
918 Gl::vertex_attrib_pointer(minimap_.in_field, 2, sizeof(MiniMap::VertexData),
919 offset + offsetof(MiniMap::VertexData, tx));
920
921 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
922}
923
924void TerrainProgramGl4::draw_roads(const TerrainGl4Arguments* args,
925 float z_value) {
926 auto& gl = Gl::State::instance();
927
928 // Coordinate transform from map coordinates to GL coordinates.
929 float scale_x = 2.0 / args->surface_width * args->scale;
930 float scale_y = -2.0 / args->surface_height * args->scale;
931 float offset_x = args->surface_offset.x * scale_x - 1.0;
932 float offset_y = args->surface_offset.y * scale_y + 1.0;
933
934 glUseProgram(roads_.gl_program.object());
935
936 setup_road_index_buffer(args->roads.size());
937
938 // Prepare uniforms.
939 glUniform2f(roads_.u_position_scale, scale_x, scale_y);
940 glUniform2f(roads_.u_position_offset, offset_x, offset_y);
941 glUniform1f(roads_.u_z_value, z_value);
942
943 // Prepare textures & sampler uniforms.
944 glUniform1i(roads_.u_terrain_base, 0);
945 gl.bind(GL_TEXTURE0, args->terrain->fields_texture());
946
947 glUniform1i(roads_.u_player_brightness, 1);
948 gl.bind(GL_TEXTURE1, args->terrain->player_brightness_texture());
949
950 glUniform1i(roads_.u_texture, 2);
951 gl.bind(GL_TEXTURE2, args->terrain->road_texture_object());
952
953 glBindBufferBase(GL_UNIFORM_BUFFER, roads_.block_textures_idx,
954 args->terrain->road_textures_buffer_object());
955
956 upload_road_data(args);
957
958 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, roads_.gl_index_buffer.object());
959 glDrawElements(GL_TRIANGLES, 6 * args->roads.size(), GL_UNSIGNED_SHORT, nullptr);
960 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
961}
962
963void TerrainProgramGl4::init_vertex_data() {
964 std::vector<PerVertexData> vertices;
965
966 for (int y = 0; y < int(kPatchHeight); ++y) {
967 for (int x = 0; x < int(kPatchWidth); ++x) {
968 int blx = (y & 1) ? x : (x - 1);
969
970 // Down triangle.
971 vertices.emplace_back(x, y, x, y, false, 2);
972 vertices.emplace_back(blx, y + 1, x, y, false, 0);
973 vertices.emplace_back(blx + 1, y + 1, x, y, false, 1);
974
975 // Right triangle.
976 vertices.emplace_back(x, y, x, y, true, 1);
977 vertices.emplace_back(blx + 1, y + 1, x, y, true, 2);
978 vertices.emplace_back(x + 1, y, x, y, true, 0);
979 }
980 }
981
982 assert(vertices.size() == 6 * kPatchWidth * kPatchHeight);
983
984 terrain_.vertex_data.bind();
985 terrain_.vertex_data.update(vertices);
986}
987
988// Determine which instances/patches to draw, upload the data and set up the
989// vertex attributes.
990unsigned TerrainProgramGl4::upload_instance_data(const TerrainGl4Arguments* args) {
991 int minfx = args->minfx;
992 int minfy = args->minfy;
993 int maxfx = args->maxfx;
994 int maxfy = args->maxfy;
995 if (minfy & 1)
996 minfy--;
997
998 int ph = (maxfy - minfy + kPatchHeight) / kPatchHeight;
999 int pw = (maxfx - minfx + kPatchWidth) / kPatchWidth;
1000 int num_patches = pw * ph;
1001
1002 auto stream = terrain_.instance_data.stream(num_patches);
1003 for (int py = 0; py < ph; ++py) {
1004 for (int px = 0; px < pw; ++px) {
1005 const int fx = minfx + px * kPatchWidth;
1006 const int fy = minfy + py * kPatchHeight;
1007
1008 stream.emplace_back();
1009 PerInstanceData& i = stream.back();
1010 i.coordinate.x = fx;
1011 i.coordinate.y = fy;
1012 }
1013 }
1014
1015 GLintptr offset = stream.unmap();
1016
1017 glVertexAttribIPointer(terrain_.in_patch_coordinate, 2, GL_INT, sizeof(PerInstanceData),
1018 reinterpret_cast<void*>(offset + offsetof(PerInstanceData, coordinate)));
1019
1020 glVertexBindingDivisor(terrain_.in_patch_coordinate, 1);
1021
1022 return num_patches;
1023}
1024
1025void TerrainProgramGl4::setup_road_index_buffer(unsigned num_roads) {
1026 if (num_roads <= roads_.num_index_roads)
1027 return;
1028
1029 if (num_roads > 65536 / 4)
1030 throw wexception("Too many roads for 16-bit indices");
1031
1032 std::vector<uint16_t> indices;
1033 indices.reserve(num_roads * 6);
1034
1035 for (unsigned i = 0; i < num_roads; ++i) {
1036 indices.push_back(4 * i);
1037 indices.push_back(4 * i + 1);
1038 indices.push_back(4 * i + 2);
1039
1040 indices.push_back(4 * i + 2);
1041 indices.push_back(4 * i + 1);
1042 indices.push_back(4 * i + 3);
1043 }
1044
1045 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, roads_.gl_index_buffer.object());
1046 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint16_t) * indices.size(),
1047 indices.data(), GL_STATIC_DRAW);
1048 roads_.num_index_roads = num_roads;
1049}
1050
1051void TerrainProgramGl4::upload_road_data(const TerrainGl4Arguments* args) {
1052 assert(!args->roads.empty());
1053
1054 auto stream = roads_.road_data.stream(args->roads.size());
1055
1056 for (const TerrainGl4Arguments::Road& road : args->roads) {
1057 stream.emplace_back(
1058 Vector2i(road.coord.x, road.coord.y), road.direction,
1059 args->terrain->road_texture_idx(
1060 road.owner, RoadType(road.type), road.coord, WalkingDir(road.direction)));
1061 }
1062
1063 glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, roads_.road_data.object(),
1064 stream.unmap(), args->roads.size() * sizeof(PerRoadData));
1065}
01066
=== added file 'src/graphic/gl/terrain_program_gl4.h'
--- src/graphic/gl/terrain_program_gl4.h 1970-01-01 00:00:00 +0000
+++ src/graphic/gl/terrain_program_gl4.h 2017-01-07 12:36:16 +0000
@@ -0,0 +1,352 @@
1/*
2 * Copyright (C) 2006-2016 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 *
18 */
19
20#ifndef WL_GRAPHIC_GL_TERRAIN_PROGRAM_GL4_H
21#define WL_GRAPHIC_GL_TERRAIN_PROGRAM_GL4_H
22
23#include <map>
24#include <memory>
25#include <unordered_map>
26
27#include "base/rect.h"
28#include "graphic/gl/streaming_buffer.h"
29#include "graphic/gl/utils.h"
30#include "logic/map_objects/walkingdir.h"
31#include "logic/roadtype.h"
32#include "logic/widelands.h"
33
34namespace Widelands {
35struct Coords;
36class EditorGameBase;
37class Player;
38}
39
40struct TerrainGl4Arguments;
41class Texture;
42
43/**
44 * This class maintains the terrain information textures.
45 */
46class TerrainInformationGl4 {
47public:
48 ~TerrainInformationGl4();
49
50 // Get the global instance associated to the given editor/game instance
51 // and player perspective. If player is nullptr, an omniscient perspective
52 // is returned.
53 static std::shared_ptr<TerrainInformationGl4>
54 get(const Widelands::EditorGameBase& egbase,
55 const Widelands::Player* player = nullptr);
56
57 static void prepare_frame();
58
59 const Widelands::EditorGameBase& egbase() const {
60 return egbase_;
61 }
62
63 const Widelands::Player* player() const {
64 return player_;
65 }
66
67 GLuint fields_texture() const {
68 return fields_texture_;
69 }
70
71 GLuint player_brightness_texture() const {
72 return brightness_texture_;
73 }
74
75 GLint road_texture_object() const {
76 return road_texture_object_;
77 }
78
79 GLuint road_textures_buffer_object() const {
80 return road_textures_.object();
81 }
82
83 GLuint terrain_data_buffer_object() const {
84 return terrain_data_.object();
85 }
86
87 GLuint minimap_texture() const {
88 return minimap_texture_;
89 }
90
91 GLuint terrain_color_texture() const {
92 return terrain_color_texture_;
93 }
94
95 GLuint player_color_texture() const {
96 return player_color_texture_;
97 }
98
99 // Get the index into the road textures array stored in the road textures
100 // buffer.
101 unsigned road_texture_idx(Widelands::PlayerNumber owner,
102 Widelands::RoadType road_type,
103 const Widelands::Coords& coords,
104 Widelands::WalkingDir direction) const;
105
106 // Mark regions/types of information that need to be updated.
107 void update(int minfx, int maxfx, int minfy, int maxfy);
108 void update_minimap();
109
110private:
111 TerrainInformationGl4(const Widelands::EditorGameBase& egbase,
112 const Widelands::Player* player);
113
114 using GlobalKey = std::pair<const Widelands::EditorGameBase*, const Widelands::Player*>;
115 using GlobalMap = std::map<GlobalKey, std::weak_ptr<TerrainInformationGl4>>;
116
117 void do_prepare_frame();
118 void fields_update();
119 void upload_road_textures();
120 void upload_terrain_data();
121 void brightness_update();
122 void do_update_minimap();
123 void upload_constant_textures();
124
125 struct PerTerrainData {
126 Vector2f offset;
127 int dither_layer;
128 float padding[1];
129 };
130 static_assert(sizeof(PerTerrainData) == 16, "incorrect padding");
131
132 struct PerFieldData {
133 uint8_t terrain_r;
134 uint8_t terrain_d;
135 uint8_t height;
136
137 // Will be interpreted as unsigned by the texel fetch in the shader.
138 int8_t brightness;
139 };
140 static_assert(sizeof(PerFieldData) == 4, "incorrect padding");
141
142 struct PerRoadTextureData {
143 float x, y, w, h;
144
145 PerRoadTextureData(const Rectf& rect);
146 };
147 static_assert(sizeof(PerRoadTextureData) == 16, "incorrect padding");
148
149 struct PlayerRoads {
150 unsigned normal_roads = 0;
151 unsigned num_normal_roads = 1;
152 unsigned busy_roads = 0;
153 unsigned num_busy_roads = 1;
154 };
155
156 static GlobalMap global_map_;
157
158 const Widelands::EditorGameBase& egbase_;
159 const Widelands::Player* player_;
160 uint32_t fields_base_version_;
161 uint32_t terrain_vision_version_;
162 std::vector<Recti> update_;
163 bool updated_minimap_;
164 bool need_update_minimap_;
165 unsigned minimap_update_next_;
166
167 Gl::StreamingBuffer<uint8_t> uploads_;
168
169 // The texture containing per-field information.
170 GLuint fields_texture_;
171
172 // Brightness texture: GL_R8.
173 GLuint brightness_texture_;
174 bool brightness_see_all_;
175
176 // Road textures information
177 Gl::Buffer<PerRoadTextureData> road_textures_;
178 std::vector<PlayerRoads> player_roads_;
179 GLuint road_texture_object_;
180
181 // Uniform buffer with per-terrain information.
182 Gl::Buffer<PerTerrainData> terrain_data_;
183
184 // Texture containing additional, minimap-only information.
185 GLuint minimap_texture_;
186
187 // Texture containing terrain colors for minimap.
188 GLuint terrain_color_texture_;
189
190 // Texture containing player colors.
191 GLuint player_color_texture_;
192
193 DISALLOW_COPY_AND_ASSIGN(TerrainInformationGl4);
194};
195
196class TerrainProgramGl4 {
197public:
198 // The patch height must be even.
199 static constexpr unsigned kPatchWidth = 8;
200 static constexpr unsigned kPatchHeight = 8;
201
202public:
203 TerrainProgramGl4();
204 ~TerrainProgramGl4();
205
206 static bool supported();
207
208 // Draws the terrain.
209 void draw(const TerrainGl4Arguments* args,
210 float z_value);
211
212 // Draws a mini-map.
213 void draw_minimap(const TerrainGl4Arguments* args,
214 float z_value);
215
216 // Draw roads.
217 void draw_roads(const TerrainGl4Arguments* args, float z_value);
218
219private:
220 void init_vertex_data();
221 void upload_terrain_data(const TerrainGl4Arguments* args, uint32_t gametime);
222 unsigned upload_instance_data(const TerrainGl4Arguments* args);
223
224 void setup_road_index_buffer(unsigned num_roads);
225 void upload_road_data(const TerrainGl4Arguments* args);
226
227 struct PerInstanceData {
228 Vector2i coordinate;
229 };
230 static_assert(sizeof(PerInstanceData) == 8, "incorrect padding");
231
232 struct PerVertexData {
233 Vector2i vertex_coordinate;
234 Vector2i triangle_coordinate;
235
236 PerVertexData(int vx, int vy, int tx, int ty, bool r, uint dither_vid)
237 : vertex_coordinate(vx, vy), triangle_coordinate((tx << 3) | (dither_vid << 1) | r, ty) {
238 }
239 };
240 static_assert(sizeof(PerVertexData) == 16, "incorrect padding");
241
242 struct PerRoadData {
243 Vector2i start;
244 uint32_t direction;
245 uint32_t texture;
246
247 PerRoadData(const Vector2i& start_, uint32_t direction_, uint32_t texture_)
248 : start(start_), direction(direction_), texture(texture_) {
249 }
250 };
251 static_assert(sizeof(PerRoadData) == 16, "incorrect padding");
252
253 struct Terrain {
254 Terrain();
255 ~Terrain();
256
257 // The program used for drawing the terrain.
258 Gl::Program gl_program;
259
260 // Per-instance/patch data.
261 Gl::StreamingBuffer<PerInstanceData> instance_data;
262
263 // Per-vertex data.
264 Gl::Buffer<PerVertexData> vertex_data;
265
266 std::unique_ptr<Texture> dither_mask;
267
268 // Vertex attributes.
269 GLint in_vertex_coordinate;
270 GLint in_patch_coordinate;
271
272 // Uniforms.
273 GLint u_position_scale;
274 GLint u_position_offset;
275 GLint u_z_value;
276 GLint u_texture_dimensions;
277
278 GLint u_terrain_base;
279 GLint u_player_brightness;
280 GLint u_terrain_texture;
281 GLint u_dither_texture;
282
283 // Uniform block.
284 GLint block_terrains_idx;
285 } terrain_;
286
287 struct Roads {
288 Roads();
289 ~Roads();
290
291 // The program used for drawing the roads.
292 Gl::Program gl_program;
293
294 // Index (element array) buffer.
295 Gl::Buffer<uint16_t> gl_index_buffer;
296 unsigned num_index_roads;
297
298 // The per-road data buffer.
299 Gl::StreamingBuffer<PerRoadData> road_data;
300
301 // Uniforms.
302 GLint u_position_scale;
303 GLint u_position_offset;
304 GLint u_z_value;
305
306 GLint u_terrain_base;
307 GLint u_player_brightness;
308 GLint u_texture;
309
310 // Uniform block.
311 GLint block_textures_idx;
312 } roads_;
313
314 struct MiniMap {
315 MiniMap();
316 ~MiniMap();
317
318 struct VertexData {
319 float x, y, z;
320 float tx, ty;
321 };
322 static_assert(sizeof(VertexData) == 20, "incorrect padding");
323
324 // The program used for drawing the minimap.
325 Gl::Program gl_program;
326
327 // The vertex array.
328 Gl::StreamingBuffer<VertexData> vertex_data;
329
330 // Vertex attributes.
331 GLint in_position;
332 GLint in_field;
333
334 // Uniforms.
335 GLint u_layer_terrain;
336 GLint u_layer_owner;
337 GLint u_layer_details;
338
339 GLint u_frame_topleft;
340 GLint u_frame_bottomright;
341
342 GLint u_terrain_base;
343 GLint u_player_brightness;
344 GLint u_minimap_extra;
345 GLint u_terrain_color;
346 GLint u_player_color;
347 } minimap_;
348
349 DISALLOW_COPY_AND_ASSIGN(TerrainProgramGl4);
350};
351
352#endif // end of include guard: WL_GRAPHIC_GL_TERRAIN_PROGRAM_GL4_H
0353
=== modified file 'src/graphic/gl/utils.cc'
--- src/graphic/gl/utils.cc 2016-09-06 07:59:30 +0000
+++ src/graphic/gl/utils.cc 2017-01-07 12:36:16 +0000
@@ -82,6 +82,7 @@
82class Shader {82class Shader {
83public:83public:
84 Shader(GLenum type);84 Shader(GLenum type);
85 Shader(Shader&& other);
85 ~Shader();86 ~Shader();
8687
87 GLuint object() const {88 GLuint object() const {
@@ -89,11 +90,11 @@
89 }90 }
9091
91 // Compiles 'source'. Throws an exception on error.92 // Compiles 'source'. Throws an exception on error.
92 void compile(const char* source);93 void compile(const char* source, const char* shader_name = nullptr);
9394
94private:95private:
95 const GLenum type_;96 GLenum type_;
96 const GLuint shader_object_;97 GLuint shader_object_;
9798
98 DISALLOW_COPY_AND_ASSIGN(Shader);99 DISALLOW_COPY_AND_ASSIGN(Shader);
99};100};
@@ -104,13 +105,19 @@
104 }105 }
105}106}
106107
108Shader::Shader(Shader&& other) {
109 type_ = other.type_;
110 shader_object_ = other.shader_object_;
111 other.shader_object_ = 0;
112}
113
107Shader::~Shader() {114Shader::~Shader() {
108 if (shader_object_) {115 if (shader_object_) {
109 glDeleteShader(shader_object_);116 glDeleteShader(shader_object_);
110 }117 }
111}118}
112119
113void Shader::compile(const char* source) {120void Shader::compile(const char* source, const char* shader_name) {
114 glShaderSource(shader_object_, 1, &source, nullptr);121 glShaderSource(shader_object_, 1, &source, nullptr);
115122
116 glCompileShader(shader_object_);123 glCompileShader(shader_object_);
@@ -123,7 +130,8 @@
123 std::unique_ptr<char[]> infoLog(new char[infoLen]);130 std::unique_ptr<char[]> infoLog(new char[infoLen]);
124 glGetShaderInfoLog(shader_object_, infoLen, NULL, infoLog.get());131 glGetShaderInfoLog(shader_object_, infoLen, NULL, infoLog.get());
125 throw wexception(132 throw wexception(
126 "Error compiling %s shader:\n%s", shader_to_string(type_).c_str(), infoLog.get());133 "Error compiling %s shader (%s):\n%s", shader_to_string(type_).c_str(),
134 shader_name ? shader_name : "unnamed", infoLog.get());
127 }135 }
128 }136 }
129}137}
@@ -140,17 +148,23 @@
140 }148 }
141}149}
142150
143void Program::build(const std::string& program_name) {151void Program::build_vp_fp(const std::vector<std::string>& vp_names,
144 std::string fragment_shader_source = read_file("shaders/" + program_name + ".fp");152 const std::vector<std::string>& fp_names) {
145 std::string vertex_shader_source = read_file("shaders/" + program_name + ".vp");153 // Shader objects are marked for deletion immediately, but the GL holds
146154 // onto them as long as they're attached to the program object.
147 vertex_shader_.reset(new Shader(GL_VERTEX_SHADER));155 for (const std::string& vp_name : vp_names) {
148 vertex_shader_->compile(vertex_shader_source.c_str());156 std::string vertex_shader_source = read_file("shaders/" + vp_name + ".vp");
149 glAttachShader(program_object_, vertex_shader_->object());157 Shader shader(GL_VERTEX_SHADER);
150158 shader.compile(vertex_shader_source.c_str(), vp_name.c_str());
151 fragment_shader_.reset(new Shader(GL_FRAGMENT_SHADER));159 glAttachShader(program_object_, shader.object());
152 fragment_shader_->compile(fragment_shader_source.c_str());160 }
153 glAttachShader(program_object_, fragment_shader_->object());161
162 for (const std::string& fp_name : fp_names) {
163 std::string fragment_shader_source = read_file("shaders/" + fp_name + ".fp");
164 Shader shader(GL_FRAGMENT_SHADER);
165 shader.compile(fragment_shader_source.c_str(), fp_name.c_str());
166 glAttachShader(program_object_, shader.object());
167 }
154168
155 glLinkProgram(program_object_);169 glLinkProgram(program_object_);
156170
@@ -169,8 +183,49 @@
169 }183 }
170}184}
171185
186void Program::build(const std::string& program_name) {
187 build_vp_fp({program_name}, {program_name});
188}
189
190void Capabilities::check() {
191 // Reset all variables
192 *this = {};
193
194 const char* glsl_version_string = reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION));
195 int major = 0, minor = 0;
196
197 if (sscanf(glsl_version_string, "%d.%d", &major, &minor) != 2)
198 log("Warning: Malformed GLSL version string: %s\n", glsl_version_string);
199
200 glsl_version = major * 100 + minor;
201
202 GLint num_extensions;
203 glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
204
205 for (GLint i = 0; i < num_extensions; ++i) {
206 const char* extension = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i));
207
208#define EXTENSION(basename) \
209 do { \
210 if (!strcmp(extension, "GL_" #basename)) \
211 basename = true; \
212 } while (false)
213
214 EXTENSION(ARB_separate_shader_objects);
215 EXTENSION(ARB_shader_storage_buffer_object);
216 EXTENSION(ARB_uniform_buffer_object);
217
218#undef EXTENSION
219 }
220}
221
172State::State()222State::State()
173 : last_active_texture_(NONE), current_framebuffer_(0), current_framebuffer_texture_(0) {223 : target_to_texture_(kMaxTextureTargets), last_active_texture_(NONE),
224 current_framebuffer_(0), current_framebuffer_texture_(0) {
225}
226
227void State::check_capabilities() {
228 caps_.check();
174}229}
175230
176void State::bind(const GLenum target, const GLuint texture) {231void State::bind(const GLenum target, const GLuint texture) {
@@ -181,7 +236,8 @@
181}236}
182237
183void State::do_bind(const GLenum target, const GLuint texture) {238void State::do_bind(const GLenum target, const GLuint texture) {
184 const auto currently_bound_texture = target_to_texture_[target];239 const unsigned target_idx = target - GL_TEXTURE0;
240 const auto currently_bound_texture = target_to_texture_[target_idx];
185 if (currently_bound_texture == texture) {241 if (currently_bound_texture == texture) {
186 return;242 return;
187 }243 }
@@ -191,28 +247,19 @@
191 }247 }
192 glBindTexture(GL_TEXTURE_2D, texture);248 glBindTexture(GL_TEXTURE_2D, texture);
193249
194 target_to_texture_[target] = texture;250 target_to_texture_[target_idx] = texture;
195 texture_to_target_[currently_bound_texture] = NONE;
196 texture_to_target_[texture] = target;
197}
198
199void State::unbind_texture_if_bound(const GLuint texture) {
200 if (texture == 0) {
201 return;
202 }
203 const auto target = texture_to_target_[texture];
204 if (target != 0) {
205 do_bind(target, 0);
206 }
207}251}
208252
209void State::delete_texture(const GLuint texture) {253void State::delete_texture(const GLuint texture) {
210 unbind_texture_if_bound(texture);
211 glDeleteTextures(1, &texture);254 glDeleteTextures(1, &texture);
212255
213 if (current_framebuffer_texture_ == texture) {256 if (current_framebuffer_texture_ == texture) {
214 current_framebuffer_texture_ = 0;257 current_framebuffer_texture_ = 0;
215 }258 }
259 for (unsigned i = 0; i < target_to_texture_.size(); ++i) {
260 if (target_to_texture_[i] == texture)
261 target_to_texture_[i] = 0;
262 }
216}263}
217264
218void State::bind_framebuffer(const GLuint framebuffer, const GLuint texture) {265void State::bind_framebuffer(const GLuint framebuffer, const GLuint texture) {
@@ -227,7 +274,6 @@
227274
228 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);275 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
229 if (framebuffer != 0) {276 if (framebuffer != 0) {
230 unbind_texture_if_bound(texture);
231 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);277 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
232 assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);278 assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
233 }279 }
234280
=== modified file 'src/graphic/gl/utils.h'
--- src/graphic/gl/utils.h 2016-08-04 15:49:05 +0000
+++ src/graphic/gl/utils.h 2017-01-07 12:36:16 +0000
@@ -53,11 +53,11 @@
53 // Creates and compiles shader objects based on the corresponding files in data/shaders,53 // Creates and compiles shader objects based on the corresponding files in data/shaders,
54 // then links them into the program.54 // then links them into the program.
55 void build(const std::string& program_name);55 void build(const std::string& program_name);
56 void build_vp_fp(const std::vector<std::string>& vp_names,
57 const std::vector<std::string>& fp_names);
5658
57private:59private:
58 const GLuint program_object_;60 const GLuint program_object_;
59 std::unique_ptr<Shader> vertex_shader_;
60 std::unique_ptr<Shader> fragment_shader_;
6161
62 DISALLOW_COPY_AND_ASSIGN(Program);62 DISALLOW_COPY_AND_ASSIGN(Program);
63};63};
@@ -79,6 +79,11 @@
79 }79 }
80 }80 }
8181
82 // Returns the OpenGL object for direct use.
83 GLuint object() const {
84 return object_;
85 }
86
82 // Calls glBindBuffer on the underlying buffer data.87 // Calls glBindBuffer on the underlying buffer data.
83 void bind() const {88 void bind() const {
84 glBindBuffer(GL_ARRAY_BUFFER, object_);89 glBindBuffer(GL_ARRAY_BUFFER, object_);
@@ -99,6 +104,17 @@
99 DISALLOW_COPY_AND_ASSIGN(Buffer);104 DISALLOW_COPY_AND_ASSIGN(Buffer);
100};105};
101106
107// Selected GL capabilities that are queried during initialization.
108struct Capabilities {
109 unsigned glsl_version = 0; // major * 100 + minor
110
111 bool ARB_separate_shader_objects = false;
112 bool ARB_shader_storage_buffer_object = false;
113 bool ARB_uniform_buffer_object = false;
114
115 void check();
116};
117
102// Some GL drivers do not remember the current pipeline state. If you rebind a118// Some GL drivers do not remember the current pipeline state. If you rebind a
103// texture that has already bound to the same target, they will happily stall119// texture that has already bound to the same target, they will happily stall
104// the pipeline. We therefore cache the state of the GL driver in this class120// the pipeline. We therefore cache the state of the GL driver in this class
@@ -110,28 +126,30 @@
110 void bind_framebuffer(GLuint framebuffer, GLuint texture);126 void bind_framebuffer(GLuint framebuffer, GLuint texture);
111127
112 // Wrapper around glActiveTexture() and glBindTexture(). We never unbind a128 // Wrapper around glActiveTexture() and glBindTexture(). We never unbind a
113 // texture, i.e. calls with texture == 0 are ignored. It costs only time and129 // texture, i.e. calls with texture == 0 are ignored.
114 // is only needed when the bounded texture is rendered on - see
115 // 'unbind_texture_if_bound'.
116 void bind(GLenum target, GLuint texture);130 void bind(GLenum target, GLuint texture);
117131
118 // Checks if the texture is bound to any target. If so, unbinds it. This is
119 // needed before the texture is used as target for rendering.
120 void unbind_texture_if_bound(GLuint texture);
121
122 void delete_texture(GLuint texture);132 void delete_texture(GLuint texture);
123133
124 // Calls glEnableVertexAttribArray on all 'entries' and disables all others134 // Calls glEnableVertexAttribArray on all 'entries' and disables all others
125 // that are activated. 'entries' is taken by value on purpose.135 // that are activated. 'entries' is taken by value on purpose.
126 void enable_vertex_attrib_array(std::unordered_set<GLint> entries);136 void enable_vertex_attrib_array(std::unordered_set<GLint> entries);
127137
138 const Capabilities& capabilities() const {
139 return caps_;
140 }
141
142 void check_capabilities();
143
128private:144private:
129 std::unordered_map<GLenum, GLuint> target_to_texture_;145 static const unsigned kMaxTextureTargets = 16;
130 std::unordered_map<GLuint, GLenum> texture_to_target_;146
147 std::vector<GLuint> target_to_texture_;
131 std::unordered_set<GLint> enabled_attrib_arrays_;148 std::unordered_set<GLint> enabled_attrib_arrays_;
132 GLenum last_active_texture_;149 GLenum last_active_texture_;
133 GLuint current_framebuffer_;150 GLuint current_framebuffer_;
134 GLuint current_framebuffer_texture_;151 GLuint current_framebuffer_texture_;
152 Capabilities caps_;
135153
136 State();154 State();
137155
138156
=== modified file 'src/graphic/image_io.cc'
--- src/graphic/image_io.cc 2016-08-04 15:49:05 +0000
+++ src/graphic/image_io.cc 2017-01-07 12:36:16 +0000
@@ -124,7 +124,7 @@
124 std::unique_ptr<png_byte[]> row(new png_byte[row_size]);124 std::unique_ptr<png_byte[]> row(new png_byte[row_size]);
125125
126 // Write each row126 // Write each row
127 texture->lock();127 texture->lock(Texture::Lock_Preserve);
128128
129 // Write each row129 // Write each row
130 RGBAColor color;130 RGBAColor color;
131131
=== added file 'src/graphic/minimap_layer.h'
--- src/graphic/minimap_layer.h 1970-01-01 00:00:00 +0000
+++ src/graphic/minimap_layer.h 2017-01-07 12:36:16 +0000
@@ -0,0 +1,45 @@
1/*
2 * Copyright (C) 2010-2016 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20#ifndef WL_GRAPHIC_MINIMAP_LAYER_H
21#define WL_GRAPHIC_MINIMAP_LAYER_H
22
23// Layers for selecting what do display on the minimap.
24enum class MiniMapLayer {
25 Terrain = 1,
26 Owner = 2,
27 Flag = 4,
28 Road = 8,
29 Building = 16,
30 Zoom2 = 32,
31 ViewWindow = 64,
32};
33
34// A bunch of operators that turn MiniMapLayer into a bitwise combinable flag class.
35inline MiniMapLayer operator|(MiniMapLayer left, MiniMapLayer right) {
36 return MiniMapLayer(static_cast<int>(left) | static_cast<int>(right));
37}
38inline int operator&(MiniMapLayer left, MiniMapLayer right) {
39 return static_cast<int>(left) & static_cast<int>(right);
40}
41inline MiniMapLayer operator^(MiniMapLayer left, MiniMapLayer right) {
42 return MiniMapLayer(static_cast<int>(left) ^ static_cast<int>(right));
43}
44
45#endif // end of include guard: WL_GRAPHIC_MINIMAP_LAYER_H
046
=== modified file 'src/graphic/minimap_renderer.cc'
--- src/graphic/minimap_renderer.cc 2016-12-03 13:32:28 +0000
+++ src/graphic/minimap_renderer.cc 2017-01-07 12:36:16 +0000
@@ -24,7 +24,13 @@
24#include "base/macros.h"24#include "base/macros.h"
25#include "economy/flag.h"25#include "economy/flag.h"
26#include "economy/road.h"26#include "economy/road.h"
27#include "graphic/game_renderer_gl4.h"
28#include "graphic/gl/terrain_program_gl4.h"
27#include "graphic/graphic.h"29#include "graphic/graphic.h"
30#include "graphic/image_io.h"
31#include "graphic/render_queue.h"
32#include "graphic/rendertarget.h"
33#include "graphic/texture.h"
28#include "logic/field.h"34#include "logic/field.h"
29#include "logic/map_objects/world/terrain_description.h"35#include "logic/map_objects/world/terrain_description.h"
30#include "logic/map_objects/world/world.h"36#include "logic/map_objects/world/world.h"
@@ -189,13 +195,118 @@
189 owner = field.owner;195 owner = field.owner;
190 }196 }
191197
192 if (vision > 0) {198 RGBColor color;
193 texture->set_pixel(x, y, calc_minimap_color(egbase, f, layers, owner, vision > 1));199 if (vision > 0)
194 }200 color = calc_minimap_color(egbase, f, layers, owner, vision > 1);
201 texture->set_pixel(x, y, color);
195 }202 }
196 }203 }
197}204}
198205
206/**
207 * Mini-map renderer implementation that generates a mini-map texture in
208 * software and blits it.
209 */
210class MiniMapRendererSoftware : public MiniMapRenderer {
211public:
212 MiniMapRendererSoftware(const Widelands::EditorGameBase& egbase,
213 const Widelands::Player* player)
214 : MiniMapRenderer(egbase, player) {
215 }
216
217 void draw(RenderTarget& dst,
218 const Rectf& viewarea,
219 MiniMapType type,
220 MiniMapLayer layers) override {
221 texture_ = draw_minimap(egbase(), player(), viewarea, type, layers);
222 dst.blit(Vector2f(), texture_.get());
223 }
224
225private:
226 std::unique_ptr<Texture> texture_;
227};
228
229/**
230 * Mini-map renderer that delegates to the GL4 programs for mini-map drawing
231 * in a pixel shader.
232 */
233class MiniMapRendererGl4 : public MiniMapRenderer {
234public:
235 MiniMapRendererGl4(const Widelands::EditorGameBase& egbase,
236 const Widelands::Player* player)
237 : MiniMapRenderer(egbase, player) {
238 args_.terrain = TerrainInformationGl4::get(egbase, player);
239 }
240
241 void draw(RenderTarget& dst,
242 const Rectf& view_area,
243 MiniMapType minimap_type,
244 MiniMapLayer layers) override {
245 Surface* surface = dst.get_surface();
246 if (!surface)
247 return;
248
249 args_.terrain->update_minimap();
250
251 const Recti& bounding_rect = dst.get_rect();
252
253 // Determine the field shown at the top-left of the minimap.
254 const bool zoom = layers & MiniMapLayer::Zoom2;
255 Vector2f top_left =
256 minimap_pixel_to_mappixel(egbase().map(), Vector2i(0, 0), view_area, minimap_type, zoom);
257 const Coords node =
258 MapviewPixelFunctions::calc_node_and_triangle(egbase().map(), top_left.x, top_left.y).node;
259
260 args_.minimap_tl_fx = node.x;
261 args_.minimap_tl_fy = node.y;
262 args_.minimap_layers = layers;
263
264 // Calculate frame coordinates
265 int frame_width = view_area.w / kTriangleWidth;
266 int frame_height = view_area.h / kTriangleHeight;
267
268 Vector2i center_field;
269 switch (minimap_type) {
270 case MiniMapType::kStaticViewWindow:
271 center_field.x = node.x + egbase().map().get_width() / 2;
272 center_field.y = node.y + egbase().map().get_height() / 2;
273 break;
274
275 case MiniMapType::kStaticMap: {
276 Vector2f origin = view_area.center();
277 MapviewPixelFunctions::normalize_pix(egbase().map(), &origin);
278 center_field.x = origin.x / kTriangleWidth;
279 center_field.y = origin.y / kTriangleHeight;
280 break;
281 }
282 }
283
284 args_.minfx = center_field.x - frame_width / 2;
285 args_.minfy = center_field.y - frame_height / 2;
286 args_.maxfx = center_field.x + frame_width / 2;
287 args_.maxfy = center_field.y + frame_height / 2;
288
289 args_.surface_offset = (bounding_rect.origin() + dst.get_offset()).cast<float>();
290 args_.surface_width = surface->width();
291 args_.surface_height = surface->height();
292
293 // Enqueue the drawing.
294 RenderQueue::Item i;
295 i.program_id = RenderQueue::Program::kMiniMapGl4;
296 i.blend_mode = BlendMode::Copy;
297 i.terrain_arguments.destination_rect =
298 Rectf(bounding_rect.x, args_.surface_height - bounding_rect.y - bounding_rect.h,
299 bounding_rect.w, bounding_rect.h);
300 i.terrain_arguments.renderbuffer_width = args_.surface_width;
301 i.terrain_arguments.renderbuffer_height = args_.surface_height;
302 i.terrain_gl4_arguments = &args_;
303 RenderQueue::instance().enqueue(i);
304 }
305
306private:
307 TerrainGl4Arguments args_;
308};
309
199} // namespace310} // namespace
200311
201Vector2f minimap_pixel_to_mappixel(const Widelands::Map& map,312Vector2f minimap_pixel_to_mappixel(const Widelands::Map& map,
@@ -235,10 +346,6 @@
235 const int16_t map_w = (layers & MiniMapLayer::Zoom2) ? map.get_width() * 2 : map.get_width();346 const int16_t map_w = (layers & MiniMapLayer::Zoom2) ? map.get_width() * 2 : map.get_width();
236 const int16_t map_h = (layers & MiniMapLayer::Zoom2) ? map.get_height() * 2 : map.get_height();347 const int16_t map_h = (layers & MiniMapLayer::Zoom2) ? map.get_height() * 2 : map.get_height();
237348
238 std::unique_ptr<Texture> texture(new Texture(map_w, map_h));
239
240 texture->fill_rect(Rectf(0, 0, texture->width(), texture->height()), RGBAColor(0, 0, 0, 255));
241
242 // Center the view on the middle of the 'view_area'.349 // Center the view on the middle of the 'view_area'.
243 const bool zoom = layers & MiniMapLayer::Zoom2;350 const bool zoom = layers & MiniMapLayer::Zoom2;
244 Vector2f top_left =351 Vector2f top_left =
@@ -246,7 +353,8 @@
246 const Coords node =353 const Coords node =
247 MapviewPixelFunctions::calc_node_and_triangle(map, top_left.x, top_left.y).node;354 MapviewPixelFunctions::calc_node_and_triangle(map, top_left.x, top_left.y).node;
248355
249 texture->lock();356 std::unique_ptr<Texture> texture(new Texture(map_w, map_h));
357 texture->lock(Texture::Lock_Discard);
250 do_draw_minimap(texture.get(), egbase, player, Vector2i(node.x, node.y), layers);358 do_draw_minimap(texture.get(), egbase, player, Vector2i(node.x, node.y), layers);
251359
252 if (layers & MiniMapLayer::ViewWindow) {360 if (layers & MiniMapLayer::ViewWindow) {
@@ -256,3 +364,16 @@
256364
257 return texture;365 return texture;
258}366}
367
368MiniMapRenderer::MiniMapRenderer(const Widelands::EditorGameBase& egbase,
369 const Widelands::Player* player)
370 : egbase_(egbase), player_(player) {
371}
372
373std::unique_ptr<MiniMapRenderer>
374MiniMapRenderer::create(const Widelands::EditorGameBase& egbase,
375 const Widelands::Player* player) {
376 if (TerrainProgramGl4::supported())
377 return std::unique_ptr<MiniMapRenderer>(new MiniMapRendererGl4(egbase, player));
378 return std::unique_ptr<MiniMapRenderer>(new MiniMapRendererSoftware(egbase, player));
379}
259380
=== modified file 'src/graphic/minimap_renderer.h'
--- src/graphic/minimap_renderer.h 2016-10-24 20:07:22 +0000
+++ src/graphic/minimap_renderer.h 2017-01-07 12:36:16 +0000
@@ -24,31 +24,16 @@
2424
25#include "base/rect.h"25#include "base/rect.h"
26#include "base/vector.h"26#include "base/vector.h"
27#include "graphic/texture.h"27#include "graphic/minimap_layer.h"
28#include "logic/editor_game_base.h"28
29#include "logic/map.h"29class RenderTarget;
30#include "logic/player.h"30class StreamWrite;
3131class Texture;
32// Layers for selecting what do display on the minimap.32
33enum class MiniMapLayer {33namespace Widelands {
34 Terrain = 1,34class Map;
35 Owner = 2,35class Player;
36 Flag = 4,36class EditorGameBase;
37 Road = 8,
38 Building = 16,
39 Zoom2 = 32,
40 ViewWindow = 64,
41};
42
43// A bunch of operators that turn MiniMapLayer into a bitwise combinable flag class.
44inline MiniMapLayer operator|(MiniMapLayer left, MiniMapLayer right) {
45 return MiniMapLayer(static_cast<int>(left) | static_cast<int>(right));
46}
47inline int operator&(MiniMapLayer left, MiniMapLayer right) {
48 return static_cast<int>(left) & static_cast<int>(right);
49}
50inline MiniMapLayer operator^(MiniMapLayer left, MiniMapLayer right) {
51 return MiniMapLayer(static_cast<int>(left) ^ static_cast<int>(right));
52}37}
5338
54enum class MiniMapType {39enum class MiniMapType {
@@ -59,6 +44,45 @@
59 kStaticMap,44 kStaticMap,
60};45};
6146
47/**
48 * Virtual base class for mini-map renderers. Each mini-map view should own
49 * an instance of this class, which must be kept alive during rendering (due
50 * to commands sent to the RenderQueue).
51 */
52class MiniMapRenderer {
53public:
54 static std::unique_ptr<MiniMapRenderer>
55 create(const Widelands::EditorGameBase& egbase,
56 const Widelands::Player* player);
57
58 virtual ~MiniMapRenderer() {}
59
60 const Widelands::EditorGameBase& egbase() const {
61 return egbase_;
62 }
63
64 const Widelands::Player* player() const {
65 return player_;
66 }
67
68 /**
69 * Draw the minimap into the given destination. The @p viewpoint is the
70 * map field shown in the top-left corner of the minimap.
71 */
72 virtual void draw(RenderTarget& dst,
73 const Rectf& viewarea,
74 MiniMapType type,
75 MiniMapLayer layers) = 0;
76
77protected:
78 MiniMapRenderer(const Widelands::EditorGameBase& egbase,
79 const Widelands::Player* player);
80
81private:
82 const Widelands::EditorGameBase& egbase_;
83 const Widelands::Player* player_;
84};
85
62// Converts between minimap pixel and map pixel.86// Converts between minimap pixel and map pixel.
63// Remember to call 'normalize_pix' after applying the transformation.87// Remember to call 'normalize_pix' after applying the transformation.
64Vector2f minimap_pixel_to_mappixel(const Widelands::Map& map,88Vector2f minimap_pixel_to_mappixel(const Widelands::Map& map,
6589
=== modified file 'src/graphic/render_queue.cc'
--- src/graphic/render_queue.cc 2016-10-22 18:19:22 +0000
+++ src/graphic/render_queue.cc 2017-01-07 12:36:16 +0000
@@ -30,6 +30,7 @@
30#include "graphic/gl/fill_rect_program.h"30#include "graphic/gl/fill_rect_program.h"
31#include "graphic/gl/road_program.h"31#include "graphic/gl/road_program.h"
32#include "graphic/gl/terrain_program.h"32#include "graphic/gl/terrain_program.h"
33#include "graphic/gl/terrain_program_gl4.h"
3334
34namespace {35namespace {
3536
@@ -47,7 +48,7 @@
47// - we batch up by program to have maximal batching.48// - we batch up by program to have maximal batching.
48// - and we want to render frontmost objects first, so that we do not render49// - and we want to render frontmost objects first, so that we do not render
49// any pixel more than once.50// any pixel more than once.
50static_assert(RenderQueue::Program::kHighestProgramId <= 8,51static_assert(RenderQueue::Program::kHighestProgramId <= 16,
51 "Need to change sorting keys."); // 4 bits.52 "Need to change sorting keys."); // 4 bits.
5253
53uint64_t54uint64_t
@@ -139,10 +140,14 @@
139} // namespace140} // namespace
140141
141RenderQueue::RenderQueue()142RenderQueue::RenderQueue()
142 : next_z_(1),143 : next_z_(1) {
143 terrain_program_(new TerrainProgram()),144 if (!TerrainProgramGl4::supported()) {
144 dither_program_(new DitherProgram()),145 terrain_program_.reset(new TerrainProgram);
145 road_program_(new RoadProgram()) {146 dither_program_.reset(new DitherProgram);
147 road_program_.reset(new RoadProgram);
148 } else {
149 terrain_program_gl4_.reset(new TerrainProgramGl4);
150 }
146}151}
147152
148// static153// static
@@ -165,6 +170,9 @@
165 case Program::kTerrainBase:170 case Program::kTerrainBase:
166 case Program::kTerrainDither:171 case Program::kTerrainDither:
167 case Program::kTerrainRoad:172 case Program::kTerrainRoad:
173 case Program::kTerrainGl4:
174 case Program::kTerrainRoadGl4:
175 case Program::kMiniMapGl4:
168 /* all fallthroughs intended */176 /* all fallthroughs intended */
169 break;177 break;
170178
@@ -191,6 +199,8 @@
191 throw wexception("Too many drawn layers. Ran out of z-values.");199 throw wexception("Too many drawn layers. Ran out of z-values.");
192 }200 }
193201
202 TerrainInformationGl4::prepare_frame();
203
194 Gl::State::instance().bind_framebuffer(0, 0);204 Gl::State::instance().bind_framebuffer(0, 0);
195 glViewport(0, 0, screen_width, screen_height);205 glViewport(0, 0, screen_width, screen_height);
196206
@@ -256,6 +266,27 @@
256 ++i;266 ++i;
257 } break;267 } break;
258268
269 case Program::kTerrainGl4: {
270 ScopedScissor scoped_scissor(item.terrain_arguments.destination_rect);
271 terrain_program_gl4_->draw(item.terrain_gl4_arguments,
272 item.z_value);
273 ++i;
274 } break;
275
276 case Program::kTerrainRoadGl4: {
277 ScopedScissor scoped_scissor(item.terrain_arguments.destination_rect);
278 terrain_program_gl4_->draw_roads(item.terrain_gl4_arguments,
279 item.z_value);
280 ++i;
281 } break;
282
283 case Program::kMiniMapGl4: {
284 ScopedScissor scoped_scissor(item.terrain_arguments.destination_rect);
285 terrain_program_gl4_->draw_minimap(item.terrain_gl4_arguments,
286 item.z_value);
287 ++i;
288 } break;
289
259 default:290 default:
260 throw wexception("Unknown item.program_id: %d", item.program_id);291 throw wexception("Unknown item.program_id: %d", item.program_id);
261 }292 }
262293
=== modified file 'src/graphic/render_queue.h'
--- src/graphic/render_queue.h 2016-10-22 11:20:33 +0000
+++ src/graphic/render_queue.h 2017-01-07 12:36:16 +0000
@@ -38,6 +38,8 @@
38class DitherProgram;38class DitherProgram;
39class RoadProgram;39class RoadProgram;
40class TerrainProgram;40class TerrainProgram;
41class TerrainProgramGl4;
42struct TerrainGl4Arguments;
4143
42// The RenderQueue is a singleton implementing the concept of deferred44// The RenderQueue is a singleton implementing the concept of deferred
43// rendering: Every rendering call that pretends to draw onto the screen will45// rendering: Every rendering call that pretends to draw onto the screen will
@@ -83,6 +85,9 @@
83 kTerrainBase,85 kTerrainBase,
84 kTerrainDither,86 kTerrainDither,
85 kTerrainRoad,87 kTerrainRoad,
88 kTerrainGl4,
89 kTerrainRoadGl4,
90 kMiniMapGl4,
86 kBlit,91 kBlit,
87 kRect,92 kRect,
88 kLine,93 kLine,
@@ -120,7 +125,7 @@
120 int renderbuffer_width;125 int renderbuffer_width;
121 int renderbuffer_height;126 int renderbuffer_height;
122 const DescriptionMaintainer<Widelands::TerrainDescription>* terrains;127 const DescriptionMaintainer<Widelands::TerrainDescription>* terrains;
123 FieldsToDraw* fields_to_draw;128 FieldsToDrawGl2* fields_to_draw;
124 float scale;129 float scale;
125 Rectf destination_rect;130 Rectf destination_rect;
126 };131 };
@@ -156,6 +161,7 @@
156 TerrainArguments terrain_arguments;161 TerrainArguments terrain_arguments;
157 RectArguments rect_arguments;162 RectArguments rect_arguments;
158 LineArguments line_arguments;163 LineArguments line_arguments;
164 TerrainGl4Arguments* terrain_gl4_arguments;
159 };165 };
160166
161 static RenderQueue& instance();167 static RenderQueue& instance();
@@ -179,6 +185,7 @@
179 int next_z_;185 int next_z_;
180186
181 std::unique_ptr<TerrainProgram> terrain_program_;187 std::unique_ptr<TerrainProgram> terrain_program_;
188 std::unique_ptr<TerrainProgramGl4> terrain_program_gl4_;
182 std::unique_ptr<DitherProgram> dither_program_;189 std::unique_ptr<DitherProgram> dither_program_;
183 std::unique_ptr<RoadProgram> road_program_;190 std::unique_ptr<RoadProgram> road_program_;
184191
185192
=== modified file 'src/graphic/texture.cc'
--- src/graphic/texture.cc 2016-10-24 14:07:28 +0000
+++ src/graphic/texture.cc 2017-01-07 12:36:16 +0000
@@ -189,7 +189,7 @@
189 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, static_cast<GLint>(GL_LINEAR));189 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, static_cast<GLint>(GL_LINEAR));
190}190}
191191
192void Texture::lock() {192void Texture::lock(LockMode mode) {
193 if (blit_data_.texture_id == 0) {193 if (blit_data_.texture_id == 0) {
194 return;194 return;
195 }195 }
@@ -203,8 +203,10 @@
203203
204 pixels_.reset(new uint8_t[width() * height() * 4]);204 pixels_.reset(new uint8_t[width() * height() * 4]);
205205
206 Gl::State::instance().bind(GL_TEXTURE0, blit_data_.texture_id);206 if (mode == Lock_Preserve) {
207 glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels_.get());207 Gl::State::instance().bind(GL_TEXTURE0, blit_data_.texture_id);
208 glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels_.get());
209 }
208}210}
209211
210void Texture::unlock(UnlockMode mode) {212void Texture::unlock(UnlockMode mode) {
211213
=== modified file 'src/graphic/texture.h'
--- src/graphic/texture.h 2016-11-03 07:20:57 +0000
+++ src/graphic/texture.h 2017-01-07 12:36:16 +0000
@@ -50,6 +50,25 @@
50 // Implements Image.50 // Implements Image.
51 const BlitData& blit_data() const override;51 const BlitData& blit_data() const override;
5252
53 enum LockMode {
54 /**
55 * Previously existing pixel data will be discarded.
56 *
57 * The contents of the texture will be undefined unless all pixels
58 * values are explicitly set and \ref unlock is called in Unlock_Update
59 * mode.
60 */
61 Lock_Discard = 0,
62
63 /**
64 * The existing data in the texture will be preserved.
65 *
66 * Avoid this when possible, since the texture may have to be
67 * re-downloaded from the GPU which involves a graphics pipeline stall.
68 */
69 Lock_Preserve
70 };
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to status/vote changes: