1 /*
2 	This file is part of Warzone 2100.
3 	Copyright (C) 2017-2020  Warzone 2100 Project
4 
5 	Warzone 2100 is free software; you can redistribute it and/or modify
6 	it under the terms of the GNU General Public License as published by
7 	the Free Software Foundation; either version 2 of the License, or
8 	(at your option) any later version.
9 
10 	Warzone 2100 is distributed in the hope that it will be useful,
11 	but WITHOUT ANY WARRANTY; without even the implied warranty of
12 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 	GNU General Public License for more details.
14 
15 	You should have received a copy of the GNU General Public License
16 	along with Warzone 2100; if not, write to the Free Software
17 	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19 
20 #pragma once
21 
22 #include <memory>
23 #include <string>
24 #include <map>
25 #include <vector>
26 #include <tuple>
27 #include <functional>
28 
29 #include "lib/framework/frame.h"
30 #include "screen.h"
31 #include "pietypes.h"
32 
33 #include <glm/glm.hpp>
34 
35 namespace gfx_api
36 {
37 	// Must be implemented by backend (ex. SDL)
38 	class backend_Null_Impl;   // see: gfx_api_null.h
39 	class backend_OpenGL_Impl; // see: gfx_api_gl.h
40 #if defined(WZ_VULKAN_ENABLED)
41 	class backend_Vulkan_Impl; // see: gfx_api_vk.h
42 #endif
43 	class backend_Impl_Factory
44 	{
45 	public:
~backend_Impl_Factory()46 		virtual ~backend_Impl_Factory() {};
47 		virtual std::unique_ptr<backend_Null_Impl> createNullBackendImpl() const = 0;
48 		virtual std::unique_ptr<backend_OpenGL_Impl> createOpenGLBackendImpl() const = 0;
49 #if defined(WZ_VULKAN_ENABLED)
50 		virtual std::unique_ptr<backend_Vulkan_Impl> createVulkanBackendImpl() const = 0;
51 #endif
52 	};
53 	//
54 }
55 
56 namespace gfx_api
57 {
58 	enum class backend_type
59 	{
60 		null_backend,
61 		opengl_backend,
62 		vulkan_backend
63 	};
64 }
65 
66 namespace gfx_api
67 {
68 #ifdef GL_ONLY
69 	using gfxFloat = GLfloat;
70 	using gfxByte = GLbyte;
71 	using gfxUByte = GLubyte;
72 #else
73 	using gfxFloat = float;
74 	using gfxByte = char;
75 	using gfxUByte = unsigned char;
76 #endif
77 
78 	enum class pixel_format
79 	{
80 		invalid,
81 		FORMAT_RGBA8_UNORM_PACK8,
82 		FORMAT_BGRA8_UNORM_PACK8,
83 		FORMAT_RGB8_UNORM_PACK8,
84 	};
85 
86 	struct texture
87 	{
~texturetexture88 		virtual ~texture() {};
89 		virtual void bind() = 0;
90 		virtual void upload(const size_t& mip_level, const size_t& offset_x, const size_t& offset_y, const size_t& width, const size_t& height, const pixel_format& buffer_format, const void* data) = 0;
91 		virtual void upload_and_generate_mipmaps(const size_t& offset_x, const size_t& offset_y, const size_t& width, const size_t& height, const pixel_format& buffer_format, const void* data) = 0;
92 		virtual unsigned id() = 0;
93 
94 		texture( const texture& other ) = delete; // non construction-copyable
95 		texture& operator=( const texture& ) = delete; // non copyable
texturetexture96 		texture() {};
97 	};
98 
99 	// An abstract base that manages a single gfx buffer
100 	struct buffer
101 	{
102 		enum class usage
103 		{
104 			 vertex_buffer,
105 			 index_buffer,
106 		};
107 		static const size_t USAGE_COUNT = 2;
108 
109 		// Create a new data store for the buffer. Any existing data store will be deleted.
110 		// The new data store is created with the specified `size` in bytes.
111 		// If `data` is not NULL, the data store is initialized with `size` bytes of data from the pointer.
112 		// - If data is NULL, a data store of the specified `size` is still created, but its contents may be uninitialized (and thus undefined).
113 		//
114 		// NOTE: A single buffer instance should only be uploaded / updated *once per frame*.
115 		//       Buffer uploads / updates may be re-ordered to occur before the processing of *any* draw calls in a frame,
116 		//       hence re-using a single buffer instance for different data may lead to anything using that buffer (in that frame)
117 		//       seeing only the last-written data in the buffer.
118 		//       (i.e. Don't re-use a buffer instance for different data in the same frame - use separate buffer instances.)
119 		virtual void upload(const size_t& size, const void* data) = 0;
120 
121 		enum class update_flag {
122 			// Default behavior
123 			none,
124 
125 			// This flag disables asserts caused by multiple updates to a single buffer instance in a single frame
126 			// *ONLY* use if you are certain the updates are non-overlapping
127 			non_overlapping_updates_promise
128 		};
129 
130 		// Update `size` bytes in the existing data store, from the `start` offset, using the data at `data`.
131 		// - The `start` offset must be within the range of the existing data store (0 to buffer_size - 1, inclusive).
132 		// - The `size` of the data specified (+ the offset) must not attempt to write past the end of the existing data store.
133 		// - A data store must be allocated before it can be updated - call `upload` on a `buffer` instance before `update`.
134 		//
135 		// NOTE: A single buffer instance should only be uploaded / updated *once per frame*.
136 		//       Buffer uploads / updates may be re-ordered to occur before the processing of *any* draw calls in a frame,
137 		//       hence re-using a single buffer instance for different data may lead to anything using that buffer (in that frame)
138 		//       seeing only the last-written data in the buffer.
139 		//       (i.e. Don't re-use a buffer instance for different data in the same frame - use separate buffer instances.)
140 		virtual void update(const size_t& start, const size_t& size, const void* data, const update_flag flag = update_flag::none) = 0;
141 
142 		virtual void bind() = 0;
143 
~bufferbuffer144 		virtual ~buffer() {};
145 
146 		buffer( const buffer& other ) = delete; // non construction-copyable
147 		buffer& operator=( const buffer& ) = delete; // non copyable
bufferbuffer148 		buffer() {};
149 	};
150 
151 	enum class primitive_type
152 	{
153 		lines,
154 		line_strip,
155 		triangles,
156 		triangle_strip,
157 		// NOTE: Do *NOT* support triangle_fan, for portability reasons
158 	};
159 
160 	enum class index_type
161 	{
162 		u16,
163 		u32,
164 	};
165 
166 	enum class polygon_offset
167 	{
168 		enabled,
169 		disabled,
170 	};
171 
172 	enum class stencil_mode
173 	{
174 		stencil_shadow_silhouette,
175 		stencil_shadow_quad,
176 		stencil_disabled,
177 	};
178 
179 	enum class cull_mode
180 	{
181 		back,
182 		none,
183 	};
184 
185 	struct state_description
186 	{
187 		const REND_MODE blend_state;
188 		const DEPTH_MODE depth_mode;
189 		const uint8_t output_mask;
190 		const bool offset;
191 		const stencil_mode stencil;
192 		const cull_mode cull;
193 
state_descriptionstate_description194 		constexpr state_description(REND_MODE _blend_state, DEPTH_MODE _depth_mode, uint8_t _output_mask, polygon_offset _polygon_offset, stencil_mode _stencil, cull_mode _cull) :
195 		blend_state(_blend_state), depth_mode(_depth_mode), output_mask(_output_mask), offset(_polygon_offset == polygon_offset::enabled), stencil(_stencil), cull(_cull) {}
196 	};
197 
198 	enum class vertex_attribute_type
199 	{
200 		float2,
201 		float3,
202 		float4,
203 		u8x4_norm,
204 	};
205 
206 	struct vertex_buffer_input
207 	{
208 		const std::size_t id;
209 		const vertex_attribute_type type;
210 		const std::size_t offset;
211 
vertex_buffer_inputvertex_buffer_input212 		constexpr vertex_buffer_input(std::size_t _id, vertex_attribute_type _type, std::size_t _offset)
213 		: id(_id), type(_type), offset(_offset)
214 		{}
215 	};
216 
217 	struct vertex_buffer
218 	{
219 		const std::size_t stride;
220 		const std::vector<vertex_buffer_input> attributes;
vertex_buffervertex_buffer221 		vertex_buffer(std::size_t _stride, std::vector<vertex_buffer_input>&& _attributes)
222 		: stride(_stride), attributes(std::forward<std::vector<vertex_buffer_input>>(_attributes))
223 		{}
224 	};
225 
226 	enum class sampler_type
227 	{
228 		bilinear,
229 		bilinear_repeat,
230 		anisotropic,
231 		nearest_clamped,
232 		anisotropic_repeat,
233 	};
234 
235 	struct texture_input
236 	{
237 		const std::size_t id;
238 		const sampler_type sampler;
239 
texture_inputtexture_input240 		constexpr texture_input(std::size_t _id, sampler_type _sampler)
241 		: id(_id), sampler(_sampler)
242 		{}
243 	};
244 
245 	struct pipeline_state_object
246 	{
~pipeline_state_objectpipeline_state_object247 		virtual ~pipeline_state_object() {}
248 	};
249 
250 	struct context
251 	{
252 		enum class buffer_storage_hint
253 		{
254 				static_draw,
255 				stream_draw,
256 				dynamic_draw,
257 		};
258 
259 		enum class context_value
260 		{
261 			MAX_ELEMENTS_VERTICES,
262 			MAX_ELEMENTS_INDICES,
263 			MAX_TEXTURE_SIZE,
264 			MAX_SAMPLES, // max antialiasing
265 		};
266 
267 		enum class swap_interval_mode
268 		{
269 			adaptive_vsync = -1,
270 			immediate = 0,
271 			vsync = 1,
272 		};
273 		static const swap_interval_mode min_swap_interval_mode = swap_interval_mode::adaptive_vsync;
274 		static const swap_interval_mode max_swap_interval_mode = swap_interval_mode::vsync;
275 
~contextcontext276 		virtual ~context() {};
277 		virtual texture* create_texture(const size_t& mipmap_count, const size_t& width, const size_t& height, const pixel_format& internal_format, const std::string& filename = "") = 0;
278 		virtual buffer* create_buffer_object(const buffer::usage&, const buffer_storage_hint& = buffer_storage_hint::static_draw) = 0;
279 		virtual pipeline_state_object* build_pipeline(const state_description&,
280 													  const SHADER_MODE&,
281 													  const gfx_api::primitive_type& primitive,
282 													  const std::vector<gfx_api::texture_input>& texture_desc,
283 													  const std::vector<vertex_buffer>& attribute_descriptions) = 0;
284 		virtual void bind_pipeline(pipeline_state_object*, bool notextures) = 0;
285 		virtual void bind_index_buffer(buffer&, const index_type&) = 0;
286 		virtual void unbind_index_buffer(buffer&) = 0;
287 		virtual void bind_vertex_buffers(const std::size_t& first, const std::vector<std::tuple<gfx_api::buffer*, std::size_t>>& vertex_buffers_offset) = 0;
288 		virtual void unbind_vertex_buffers(const std::size_t& first, const std::vector<std::tuple<gfx_api::buffer*, std::size_t>>& vertex_buffers_offset) = 0;
289 		virtual void disable_all_vertex_buffers() = 0;
290 		virtual void bind_streamed_vertex_buffers(const void* data, const std::size_t size) = 0;
291 		virtual void bind_textures(const std::vector<texture_input>& attribute_descriptions, const std::vector<texture*>& textures) = 0;
292 		virtual void set_constants(const void* buffer, const std::size_t& size) = 0;
293 		virtual void draw(const std::size_t& offset, const std::size_t&, const primitive_type&) = 0;
294 		virtual void draw_elements(const std::size_t& offset, const std::size_t&, const primitive_type&, const index_type&) = 0;
295 		virtual void set_polygon_offset(const float& offset, const float& slope) = 0;
296 		virtual void set_depth_range(const float& min, const float& max) = 0;
297 		virtual int32_t get_context_value(const context_value property) = 0;
298 		static context& get();
299 		static bool initialize(const gfx_api::backend_Impl_Factory& impl, int32_t antialiasing, swap_interval_mode mode, gfx_api::backend_type backend);
300 		virtual void flip(int clearMode) = 0;
301 		virtual void debugStringMarker(const char *str) = 0;
302 		virtual void debugSceneBegin(const char *descr) = 0;
303 		virtual void debugSceneEnd(const char *descr) = 0;
304 		virtual bool debugPerfAvailable() = 0;
305 		virtual bool debugPerfStart(size_t sample) = 0;
306 		virtual void debugPerfStop() = 0;
307 		virtual void debugPerfBegin(PERF_POINT pp, const char *descr) = 0;
308 		virtual void debugPerfEnd(PERF_POINT pp) = 0;
309 		virtual uint64_t debugGetPerfValue(PERF_POINT pp) = 0;
310 		virtual std::map<std::string, std::string> getBackendGameInfo() = 0;
311 		virtual const std::string& getFormattedRendererInfoString() const = 0;
312 		virtual bool getScreenshot(std::function<void (std::unique_ptr<iV_Image>)> callback) = 0;
313 		virtual void handleWindowSizeChange(unsigned int oldWidth, unsigned int oldHeight, unsigned int newWidth, unsigned int newHeight) = 0;
314 		virtual void shutdown() = 0;
315 		virtual const size_t& current_FrameNum() const = 0;
316 		virtual bool setSwapInterval(swap_interval_mode mode) = 0;
317 		virtual swap_interval_mode getSwapInterval() const = 0;
318 	private:
319 		virtual bool _initialize(const backend_Impl_Factory& impl, int32_t antialiasing, swap_interval_mode mode) = 0;
320 	};
321 
322 	template<std::size_t id, vertex_attribute_type type, std::size_t offset>
323 	struct vertex_attribute_description
324 	{
get_descvertex_attribute_description325 		static vertex_buffer_input get_desc()
326 		{
327 			return vertex_buffer_input{id, type, offset};
328 		}
329 	};
330 
331 	/**
332 	 * A struct templated by a tuple.
333 	 * Describes a buffer input.
334 	 * input_description describes the various vertex attributes fetched from this buffer.
335 	 */
336 	template<std::size_t stride, typename... input_description>
337 	struct vertex_buffer_description
338 	{
get_descvertex_buffer_description339 		static vertex_buffer get_desc()
340 		{
341 			return { stride, { input_description::get_desc()...} };
342 		}
343 	};
344 
345 	template<std::size_t texture_unit, sampler_type sampler>
346 	struct texture_description
347 	{
get_desctexture_description348 		static texture_input get_desc()
349 		{
350 			return texture_input{ texture_unit, sampler };
351 		}
352 	};
353 
354 	template<REND_MODE render_mode, DEPTH_MODE depth_mode, uint8_t output_mask, polygon_offset offset, stencil_mode stencil, cull_mode cull>
355 	struct rasterizer_state
356 	{
getrasterizer_state357 		static state_description get()
358 		{
359 			return state_description{ render_mode, depth_mode, output_mask, offset, stencil, cull };
360 		}
361 	};
362 
363 	template<SHADER_MODE T>
364 	struct constant_buffer_type {};
365 
366 	template<typename rasterizer, primitive_type primitive, index_type index, typename vertex_buffer_inputs, typename texture_inputs, SHADER_MODE shader>
367 	struct pipeline_state_helper
368 	{
369 		using texture_tuple = texture_inputs;
370 
getpipeline_state_helper371 		static pipeline_state_helper<rasterizer, primitive, index, vertex_buffer_inputs, texture_inputs, shader>& get()
372 		{
373 			static pipeline_state_helper < rasterizer, primitive, index, vertex_buffer_inputs, texture_inputs, shader> object;
374 			return object;
375 		}
376 
bindpipeline_state_helper377 		void bind()
378 		{
379 			gfx_api::context::get().bind_pipeline(pso, std::tuple_size<texture_inputs>::value == 0);
380 		}
381 
382 		template<typename... Args>
bind_vertex_bufferspipeline_state_helper383 		void bind_vertex_buffers(Args&&... args)
384 		{
385 			static_assert(sizeof...(args) == std::tuple_size<vertex_buffer_inputs>::value, "Wrong number of vertex buffer");
386 			gfx_api::context::get().bind_vertex_buffers(0, { std::make_tuple(args, 0)... });
387 		}
388 
389 		template<typename... Args>
unbind_vertex_bufferspipeline_state_helper390 		void unbind_vertex_buffers(Args&&... args)
391 		{
392 			static_assert(sizeof...(args) == std::tuple_size<vertex_buffer_inputs>::value, "Wrong number of vertex buffer");
393 			gfx_api::context::get().unbind_vertex_buffers(0, { std::make_tuple(args, 0)... });
394 		}
395 
396 		template<typename...Args>
bind_texturespipeline_state_helper397 		void bind_textures(Args&&... args)
398 		{
399 			static_assert(sizeof...(args) == std::tuple_size<texture_inputs>::value, "Wrong number of textures");
400 			gfx_api::context::get().bind_textures(untuple<texture_input>(texture_inputs{}), { args... });
401 		}
402 
bind_constantspipeline_state_helper403 		void bind_constants(const constant_buffer_type<shader>& data)
404 		{
405 			// Vulkan: Many platforms have a maxUniformBufferRange of 64k
406 			// - see: https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxUniformBufferRange
407 			static_assert(sizeof(constant_buffer_type<shader>) <= 65536, "Constant buffer size exceeds 64k");
408 
409 			gfx_api::context::get().set_constants(static_cast<const void*>(&data), sizeof(constant_buffer_type<shader>));
410 		}
411 
drawpipeline_state_helper412 		void draw(const std::size_t& count, const std::size_t& offset)
413 		{
414 			context::get().draw(offset, count, primitive);
415 		}
416 
draw_elementspipeline_state_helper417 		void draw_elements(const std::size_t& count, const std::size_t& offset)
418 		{
419 			context::get().draw_elements(offset, count, primitive, index);
420 		}
421 	private:
422 		pipeline_state_object* pso;
pipeline_state_helperpipeline_state_helper423 		pipeline_state_helper()
424 		{
425 			pso = gfx_api::context::get().build_pipeline(rasterizer::get(), shader, primitive, untuple<texture_input>(texture_inputs{}), untuple<vertex_buffer>(vertex_buffer_inputs{}));
426 		}
427 
428 //		// Requires C++14 (+)
429 //		template<typename...Args>
430 //		auto untuple(const std::tuple<Args...>&)
431 //		{
432 //			auto type_holder = { Args::get_desc()... };
433 //			using output_type = decltype(type_holder);
434 //			return std::vector<typename output_type::value_type>(type_holder);
435 //		}
436 //
437 //		std::vector<gfx_api::texture_input> untuple(const std::tuple<>&)
438 //		{
439 //			return std::vector<gfx_api::texture_input>{};
440 //		}
441 
442 		// C++11, but requires specifying Output type
443 		template<typename Output, typename...Args>
untuplepipeline_state_helper444 		std::vector<Output> untuple(const std::tuple<Args...>&)
445 		{
446 			return std::vector<Output>({ Args::get_desc()... });
447 		}
448 	};
449 
450 	constexpr std::size_t position = 0;
451 	constexpr std::size_t texcoord = 1;
452 	constexpr std::size_t color = 2;
453 	constexpr std::size_t normal = 3;
454 	constexpr std::size_t tangent = 4;
455 
456 	using notexture = std::tuple<>;
457 
458 	// NOTE: Be very careful changing these constant_buffer_type structs;
459 	//		 they must match std140 layout rules (see: the Vulkan shaders)
460 	template<>
461 	struct constant_buffer_type<SHADER_BUTTON>
462 	{
463 		glm::vec4 colour;
464 		glm::vec4 teamcolour;
465 		float shaderStretch;
466 		int tcmask;
467 		int fogEnabled;
468 		int normalMap;
469 		int specularMap;
470 		int ecmState;
471 		int alphaTest;
472 		float timeState;
473 		glm::mat4 ModelViewMatrix;
474 		glm::mat4 ModelViewProjectionMatrix;
475 		glm::mat4 NormalMatrix;
476 		glm::vec4 sunPos;
477 		glm::vec4 sceneColor;
478 		glm::vec4 ambient;
479 		glm::vec4 diffuse;
480 		glm::vec4 specular;
481 		glm::vec4 fogColour;
482 		float fogEnd;
483 		float fogBegin;
484 		int hasTangents;
485 	};
486 
487 	template<>
488 	struct constant_buffer_type<SHADER_COMPONENT>
489 	{
490 		glm::vec4 colour;
491 		glm::vec4 teamcolour;
492 		float shaderStretch;
493 		int tcmask;
494 		int fogEnabled;
495 		int normalMap;
496 		int specularMap;
497 		int ecmState;
498 		int alphaTest;
499 		float timeState;
500 		glm::mat4 ModelViewMatrix;
501 		glm::mat4 ModelViewProjectionMatrix;
502 		glm::mat4 NormalMatrix;
503 		glm::vec4 sunPos;
504 		glm::vec4 sceneColor;
505 		glm::vec4 ambient;
506 		glm::vec4 diffuse;
507 		glm::vec4 specular;
508 		glm::vec4 fogColour;
509 		float fogEnd;
510 		float fogBegin;
511 		int hasTangents;
512 	};
513 
514 	template<>
515 	struct constant_buffer_type<SHADER_NOLIGHT>
516 	{
517 		glm::vec4 colour;
518 		glm::vec4 teamcolour;
519 		float shaderStretch;
520 		int tcmask;
521 		int fogEnabled;
522 		int normalMap;
523 		int specularMap;
524 		int ecmState;
525 		int alphaTest;
526 		float timeState;
527 		glm::mat4 ModelViewMatrix;
528 		glm::mat4 ModelViewProjectionMatrix;
529 		glm::mat4 NormalMatrix;
530 		glm::vec4 sunPos;
531 		glm::vec4 sceneColor;
532 		glm::vec4 ambient;
533 		glm::vec4 diffuse;
534 		glm::vec4 specular;
535 		glm::vec4 fogColour;
536 		float fogEnd;
537 		float fogBegin;
538 		int hasTangents;
539 	};
540 
541 	template<REND_MODE render_mode, SHADER_MODE shader>
542 	using Draw3DShape = typename gfx_api::pipeline_state_helper<rasterizer_state<render_mode, DEPTH_CMP_LEQ_WRT_ON, 255, polygon_offset::disabled, stencil_mode::stencil_disabled, cull_mode::back>, primitive_type::triangles, index_type::u16,
543 	std::tuple<
544 	vertex_buffer_description<12, vertex_attribute_description<position, gfx_api::vertex_attribute_type::float3, 0>>,
545 	vertex_buffer_description<12, vertex_attribute_description<normal, gfx_api::vertex_attribute_type::float3, 0>>,
546 	vertex_buffer_description<8, vertex_attribute_description<texcoord, gfx_api::vertex_attribute_type::float2, 0>>,
547 	vertex_buffer_description<16, vertex_attribute_description<tangent, gfx_api::vertex_attribute_type::float4, 0>>
548 	>,
549 	std::tuple<
550 	texture_description<0, sampler_type::anisotropic>, // diffuse
551 	texture_description<1, sampler_type::bilinear>, // team color mask
552 	texture_description<2, sampler_type::anisotropic>, // normal map
553 	texture_description<3, sampler_type::anisotropic> // specular map
554 	>, shader>;
555 
556 	using Draw3DButtonPSO = Draw3DShape<REND_OPAQUE, SHADER_BUTTON>;
557 	using Draw3DShapeOpaque = Draw3DShape<REND_OPAQUE, SHADER_COMPONENT>;
558 	using Draw3DShapeAlpha = Draw3DShape<REND_ALPHA, SHADER_COMPONENT>;
559 	using Draw3DShapePremul = Draw3DShape<REND_PREMULTIPLIED, SHADER_COMPONENT>;
560 	using Draw3DShapeAdditive = Draw3DShape<REND_ADDITIVE, SHADER_COMPONENT>;
561 	using Draw3DShapeNoLightOpaque = Draw3DShape<REND_OPAQUE, SHADER_NOLIGHT>;
562 	using Draw3DShapeNoLightAlpha = Draw3DShape<REND_ALPHA, SHADER_NOLIGHT>;
563 	using Draw3DShapeNoLightPremul = Draw3DShape<REND_PREMULTIPLIED, SHADER_NOLIGHT>;
564 	using Draw3DShapeNoLightAdditive = Draw3DShape<REND_ADDITIVE, SHADER_NOLIGHT>;
565 
566 	template<>
567 	struct constant_buffer_type<SHADER_GENERIC_COLOR>
568 	{
569 		glm::mat4 transform_matrix;
570 		glm::vec2 unused;
571 		glm::vec2 unused2;
572 		glm::vec4 colour;
573 	};
574 
575 	using TransColouredTrianglePSO = typename gfx_api::pipeline_state_helper<rasterizer_state<REND_ADDITIVE, DEPTH_CMP_LEQ_WRT_ON, 255, polygon_offset::disabled, stencil_mode::stencil_disabled, cull_mode::back>, primitive_type::triangle_strip, index_type::u16,
576 	std::tuple<
577 	vertex_buffer_description<12, vertex_attribute_description<position, gfx_api::vertex_attribute_type::float3, 0>>
578 	>, notexture, SHADER_GENERIC_COLOR>;
579 	using DrawStencilShadow = typename gfx_api::pipeline_state_helper<rasterizer_state<REND_OPAQUE, DEPTH_CMP_LEQ_WRT_OFF, 0, polygon_offset::disabled, stencil_mode::stencil_shadow_silhouette, cull_mode::none>, primitive_type::triangles, index_type::u16,
580 	std::tuple<
581 	vertex_buffer_description<12, vertex_attribute_description<position, gfx_api::vertex_attribute_type::float3, 0>>
582 	>, notexture, SHADER_GENERIC_COLOR>;
583 
584 	template<>
585 	struct constant_buffer_type<SHADER_TERRAIN_DEPTH>
586 	{
587 		glm::mat4 transform_matrix;
588 		glm::vec4 paramX;
589 		glm::vec4 paramY;
590 		glm::vec4 paramXLight;
591 		glm::vec4 paramYLight;
592 		glm::mat4 unused;
593 		glm::mat4 texture_matrix;
594 		glm::vec4 fog_colour;
595 		int fog_enabled;
596 		float fog_begin;
597 		float fog_end;
598 		int texture0;
599 		int texture1;
600 	};
601 
602 	using TerrainDepth = typename gfx_api::pipeline_state_helper<rasterizer_state<REND_OPAQUE, DEPTH_CMP_LEQ_WRT_ON, 0, polygon_offset::enabled, stencil_mode::stencil_disabled, cull_mode::back>, primitive_type::triangles, index_type::u32,
603 	std::tuple<
604 	vertex_buffer_description<12, vertex_attribute_description<position, gfx_api::vertex_attribute_type::float3, 0>>
605 	>, notexture, SHADER_TERRAIN_DEPTH>;
606 
607 	template<>
608 	struct constant_buffer_type<SHADER_TERRAIN>
609 	{
610 		glm::mat4 transform_matrix;
611 		glm::vec4 paramX;
612 		glm::vec4 paramY;
613 		glm::vec4 paramXLight;
614 		glm::vec4 paramYLight;
615 		glm::mat4 unused;
616 		glm::mat4 texture_matrix;
617 		glm::vec4 fog_colour;
618 		int fog_enabled;
619 		float fog_begin;
620 		float fog_end;
621 		int texture0;
622 		int texture1;
623 	};
624 
625 	using TerrainLayer = typename gfx_api::pipeline_state_helper<rasterizer_state<REND_ADDITIVE, DEPTH_CMP_LEQ_WRT_OFF, 255, polygon_offset::disabled, stencil_mode::stencil_disabled, cull_mode::back>, primitive_type::triangles, index_type::u32,
626 	std::tuple<
627 	vertex_buffer_description<12, vertex_attribute_description<position, gfx_api::vertex_attribute_type::float3, 0>>,
628 	vertex_buffer_description<4, vertex_attribute_description<color, gfx_api::vertex_attribute_type::u8x4_norm, 0>>
629 	>, std::tuple<texture_description<0, sampler_type::anisotropic_repeat>, texture_description<1, sampler_type::bilinear>>, SHADER_TERRAIN>;
630 
631 	template<>
632 	struct constant_buffer_type<SHADER_DECALS>
633 	{
634 		glm::mat4 transform_matrix;
635 		glm::mat4 texture_matrix;
636 		glm::vec4 param1;
637 		glm::vec4 param2;
638 		glm::vec4 fog_colour;
639 		int fog_enabled;
640 		float fog_begin;
641 		float fog_end;
642 		int texture0;
643 		int texture1;
644 	};
645 
646 	using TerrainDecals = typename gfx_api::pipeline_state_helper<rasterizer_state<REND_ALPHA, DEPTH_CMP_LEQ_WRT_OFF, 255, polygon_offset::disabled, stencil_mode::stencil_disabled, cull_mode::back>, primitive_type::triangles, index_type::u16,
647 	std::tuple<
648 	vertex_buffer_description<sizeof(glm::vec3) + sizeof(glm::vec2),
649 	vertex_attribute_description<position, gfx_api::vertex_attribute_type::float3, 0>,
650 	vertex_attribute_description<texcoord, gfx_api::vertex_attribute_type::float2, sizeof(glm::vec3)>
651 	>
652 	>, std::tuple<texture_description<0, sampler_type::anisotropic>, texture_description<1, sampler_type::bilinear>>, SHADER_DECALS>;
653 
654 	template<>
655 	struct constant_buffer_type<SHADER_WATER>
656 	{
657 		glm::mat4 transform_matrix;
658 		glm::vec4 param1;
659 		glm::vec4 param2;
660 		glm::vec4 param3;
661 		glm::vec4 param4;
662 		glm::mat4 translation;
663 		glm::mat4 texture_matrix;
664 		glm::vec4 fog_colour;
665 		int fog_enabled;
666 		float fog_begin;
667 		float fog_end;
668 		int texture0;
669 		int texture1;
670 	};
671 
672 	using WaterPSO = typename gfx_api::pipeline_state_helper<rasterizer_state<REND_MULTIPLICATIVE, DEPTH_CMP_LEQ_WRT_OFF, 255, polygon_offset::disabled, stencil_mode::stencil_disabled, cull_mode::back>, primitive_type::triangles, index_type::u32,
673 	std::tuple<
674 	vertex_buffer_description<12, vertex_attribute_description<position, gfx_api::vertex_attribute_type::float3, 0>>
675 	>, std::tuple<texture_description<0, sampler_type::anisotropic_repeat>, texture_description<1, sampler_type::anisotropic_repeat>>, SHADER_WATER>;
676 
677 	using gfx_tc = vertex_buffer_description<8, vertex_attribute_description<texcoord, gfx_api::vertex_attribute_type::float2, 0>>;
678 	using gfx_colour = vertex_buffer_description<4, vertex_attribute_description<color, gfx_api::vertex_attribute_type::u8x4_norm, 0>>;
679 	using gfx_vtx2 = vertex_buffer_description<8, vertex_attribute_description<position, gfx_api::vertex_attribute_type::float2, 0>>;
680 	using gfx_vtx3 = vertex_buffer_description<12, vertex_attribute_description<position, gfx_api::vertex_attribute_type::float3, 0>>;
681 
682 	template<>
683 	struct constant_buffer_type<SHADER_GFX_TEXT>
684 	{
685 		glm::mat4 transform_matrix;
686 		glm::vec2 offset;
687 		glm::vec2 size;
688 		glm::vec4 color;
689 		int texture;
690 	};
691 
692 	template<>
693 	struct constant_buffer_type<SHADER_GFX_COLOUR>
694 	{
695 		glm::mat4 transform_matrix;
696 //		glm::vec2 offset;
697 //		glm::vec2 size;
698 //		glm::vec4 color;
699 //		int texture;
700 	};
701 
702 	template<REND_MODE rm, DEPTH_MODE dm, primitive_type primitive, typename VTX, typename Second, SHADER_MODE shader, typename texture>
703 	using GFX = typename gfx_api::pipeline_state_helper<rasterizer_state<rm, dm, 255, polygon_offset::disabled, stencil_mode::stencil_disabled, cull_mode::back>, primitive, index_type::u16, std::tuple<VTX, Second>, texture, shader>;
704 	using VideoPSO = GFX<REND_OPAQUE, DEPTH_CMP_ALWAYS_WRT_OFF, primitive_type::triangle_strip, gfx_vtx2, gfx_tc, SHADER_GFX_TEXT, std::tuple<texture_description<0, gfx_api::sampler_type::bilinear>>>;
705 	using BackDropPSO = GFX<REND_OPAQUE, DEPTH_CMP_ALWAYS_WRT_OFF, primitive_type::triangle_strip, gfx_vtx2, gfx_tc, SHADER_GFX_TEXT, std::tuple<texture_description<0, gfx_api::sampler_type::nearest_clamped>>>;
706 	using SkyboxPSO = GFX<REND_ALPHA, DEPTH_CMP_LEQ_WRT_OFF, primitive_type::triangles, gfx_vtx3, gfx_tc, SHADER_GFX_TEXT, std::tuple<texture_description<0, gfx_api::sampler_type::bilinear_repeat>>>;
707 	using RadarPSO = GFX<REND_ALPHA, DEPTH_CMP_ALWAYS_WRT_OFF, primitive_type::triangle_strip, gfx_vtx2, gfx_tc, SHADER_GFX_TEXT, std::tuple<texture_description<0, gfx_api::sampler_type::nearest_clamped>>>;
708 	using RadarViewInsideFillPSO = GFX<REND_ALPHA, DEPTH_CMP_ALWAYS_WRT_OFF, primitive_type::triangle_strip, gfx_vtx2, gfx_colour, SHADER_GFX_COLOUR, notexture>;
709 	using RadarViewOutlinePSO = GFX<REND_ALPHA, DEPTH_CMP_ALWAYS_WRT_OFF, primitive_type::line_strip, gfx_vtx2, gfx_colour, SHADER_GFX_COLOUR, notexture>;
710 
711 	template<>
712 	struct constant_buffer_type<SHADER_TEXT>
713 	{
714 		glm::mat4 transform_matrix;
715 		glm::vec2 offset;
716 		glm::vec2 size;
717 		glm::vec4 color;
718 		int texture;
719 	};
720 
721 	using DrawImageTextPSO = typename gfx_api::pipeline_state_helper<rasterizer_state<REND_TEXT, DEPTH_CMP_ALWAYS_WRT_OFF, 255, polygon_offset::disabled, stencil_mode::stencil_disabled, cull_mode::none>, primitive_type::triangle_strip, index_type::u16,
722 	std::tuple<
723 	vertex_buffer_description<4, vertex_attribute_description<position, gfx_api::vertex_attribute_type::u8x4_norm, 0>>
724 	>,
725 	std::tuple<texture_description<0, sampler_type::bilinear>>, SHADER_TEXT>;
726 
727 	template<>
728 	struct constant_buffer_type<SHADER_RECT>
729 	{
730 		glm::mat4 transform_matrix;
731 		glm::vec2 offset;
732 		glm::vec2 size;
733 		glm::vec4 colour;
734 	};
735 
736 	using ShadowBox2DPSO = typename gfx_api::pipeline_state_helper<rasterizer_state<REND_OPAQUE, DEPTH_CMP_ALWAYS_WRT_OFF, 255, polygon_offset::disabled, stencil_mode::stencil_disabled, cull_mode::back>, primitive_type::triangle_strip, index_type::u16,
737 	std::tuple<
738 	vertex_buffer_description<4, vertex_attribute_description<position, gfx_api::vertex_attribute_type::u8x4_norm, 0>>
739 	>, notexture, SHADER_RECT>;
740 	using UniTransBoxPSO = typename gfx_api::pipeline_state_helper<rasterizer_state<REND_ALPHA, DEPTH_CMP_ALWAYS_WRT_OFF, 255, polygon_offset::disabled, stencil_mode::stencil_disabled, cull_mode::back>, primitive_type::triangle_strip, index_type::u16,
741 	std::tuple<
742 	vertex_buffer_description<4, vertex_attribute_description<position, gfx_api::vertex_attribute_type::u8x4_norm, 0>>
743 	>, notexture, SHADER_RECT>;
744 
745 	template<>
746 	struct constant_buffer_type<SHADER_TEXRECT>
747 	{
748 		glm::mat4 transform_matrix;
749 		glm::vec2 offset;
750 		glm::vec2 size;
751 		glm::vec4 color;
752 		int texture;
753 	};
754 
755 	using DrawImagePSO = typename gfx_api::pipeline_state_helper<rasterizer_state<REND_ALPHA, DEPTH_CMP_ALWAYS_WRT_OFF, 255, polygon_offset::disabled, stencil_mode::stencil_disabled, cull_mode::back>, primitive_type::triangle_strip, index_type::u16,
756 	std::tuple<
757 	vertex_buffer_description<4, vertex_attribute_description<position, gfx_api::vertex_attribute_type::u8x4_norm, 0>>
758 	>, std::tuple<texture_description<0, sampler_type::bilinear>>, SHADER_TEXRECT>;
759 
760 	using DrawImageAnisotropicPSO = typename gfx_api::pipeline_state_helper<rasterizer_state<REND_ALPHA, DEPTH_CMP_ALWAYS_WRT_OFF, 255, polygon_offset::disabled, stencil_mode::stencil_disabled, cull_mode::back>, primitive_type::triangle_strip, index_type::u16,
761 	std::tuple<
762 	vertex_buffer_description<4, vertex_attribute_description<position, gfx_api::vertex_attribute_type::u8x4_norm, 0>>
763 	>, std::tuple<texture_description<0, sampler_type::anisotropic>>, SHADER_TEXRECT>;
764 
765 	using BoxFillPSO = typename gfx_api::pipeline_state_helper<rasterizer_state<REND_OPAQUE, DEPTH_CMP_ALWAYS_WRT_OFF, 255, polygon_offset::disabled, stencil_mode::stencil_disabled, cull_mode::back>, primitive_type::triangle_strip, index_type::u16,
766 	std::tuple<
767 	vertex_buffer_description<4, vertex_attribute_description<position, gfx_api::vertex_attribute_type::u8x4_norm, 0>>
768 	>, notexture, SHADER_RECT>;
769 	using BoxFillAlphaPSO = typename gfx_api::pipeline_state_helper<rasterizer_state<REND_ALPHA, DEPTH_CMP_ALWAYS_WRT_OFF, 255, polygon_offset::disabled, stencil_mode::stencil_shadow_quad, cull_mode::back>, primitive_type::triangle_strip, index_type::u16,
770 	std::tuple<
771 	vertex_buffer_description<4, vertex_attribute_description<position, gfx_api::vertex_attribute_type::u8x4_norm, 0>>
772 	>, notexture, SHADER_RECT>;
773 
774 	template<>
775 	struct constant_buffer_type<SHADER_LINE>
776 	{
777 		glm::mat4 mat;
778 		glm::vec2 p0;
779 		glm::vec2 p1;
780 		glm::vec4 colour;
781 	};
782 
783 	using LinePSO = typename gfx_api::pipeline_state_helper<rasterizer_state<REND_ALPHA, DEPTH_CMP_ALWAYS_WRT_OFF, 255, polygon_offset::disabled, stencil_mode::stencil_disabled, cull_mode::back>, primitive_type::lines, index_type::u16,
784 	std::tuple<
785 	vertex_buffer_description<4, vertex_attribute_description<position, gfx_api::vertex_attribute_type::u8x4_norm, 0>>
786 	>, notexture, SHADER_LINE>;
787 }
788 
789 static inline int to_int(gfx_api::context::swap_interval_mode mode)
790 {
791 	return static_cast<int>(mode);
792 }
793 
794 static inline gfx_api::context::swap_interval_mode to_swap_mode(int value)
795 {
796 	switch (value) {
797 		case static_cast<int>(gfx_api::context::swap_interval_mode::immediate):
798 			return gfx_api::context::swap_interval_mode::immediate;
799 		case static_cast<int>(gfx_api::context::swap_interval_mode::vsync):
800 			return gfx_api::context::swap_interval_mode::vsync;
801 		case static_cast<int>(gfx_api::context::swap_interval_mode::adaptive_vsync):
802 			return gfx_api::context::swap_interval_mode::adaptive_vsync;
803 		default:
804 			debug(LOG_WARNING, "Invalid vsync value (%d); defaulting to vsync ON", value);
805 	}
806 	return gfx_api::context::swap_interval_mode::vsync;
807 }
808