R"(
#version 450
#extension GL_ARB_separate_shader_objects : enable

%preprocessor

#ifdef VULKAN
	#define USE_UBO 1
#endif

layout(location=0) in vec4 in_pos;
layout(location=0) out vec2 tc0;
layout(location=1) out vec4 color;
layout(location=2) out vec4 clip_rect;

#if USE_UBO
layout(%push_block) uniform Configuration
{
	vec4 ui_scale;
	vec4 albedo;
	vec4 viewport;
	vec4 clip_bounds;
	uint vertex_config;
};
#else
	uniform vec4 ui_scale;
	uniform vec4 albedo;
	uniform vec4 viewport;
	uniform vec4 clip_bounds;
	uniform uint vertex_config;
#endif

struct config_t
{
	bool no_vertex_snap;
	bool flip_vertically;
};

config_t unpack_vertex_options()
{
	config_t result;
	result.no_vertex_snap = bitfieldExtract(vertex_config, 0, 1) != 0;
	result.flip_vertically = bitfieldExtract(vertex_config, 1, 1) != 0;
	return result;
}

vec2 snap_to_grid(const in vec2 normalized)
{
	return floor(fma(normalized, viewport.xy, vec2(0.5))) / viewport.xy;
}

vec4 clip_to_ndc(const in vec4 coord, const in bool flip_vertically)
{
	vec4 ret = (coord * ui_scale.zwzw) / ui_scale.xyxy;
#ifndef VULKAN
	// Flip Y for OpenGL
	if (!flip_vertically) ret.yw = 1. - ret.yw;
#else
	if (flip_vertically) ret.yw = 1. - ret.yw;
#endif
	return ret;
}

vec4 ndc_to_window(const in vec4 coord)
{
	return fma(coord, viewport.xyxy, viewport.zwzw);
}

vec4 make_aabb(const in vec4 coords)
{
	// AABB requires the intersections to be concave
	// i.e X1 < X2 and Y1 < Y2
	vec4 result = coords;
	if (coords.x > coords.z)
	{
		result.xz = coords.zx;
	}
	if (coords.y > coords.w)
	{
		result.yw = coords.wy;
	}
	return result;
}

void main()
{
	config_t config = unpack_vertex_options();

	tc0.xy = in_pos.zw;
	color = albedo;
	clip_rect = make_aabb(ndc_to_window(clip_to_ndc(clip_bounds, config.flip_vertically)));

	vec4 pos = vec4(clip_to_ndc(in_pos, config.flip_vertically).xy, 0.5, 1.);

	if (!config.no_vertex_snap)
	{
		pos.xy = snap_to_grid(pos.xy);
	}

	gl_Position = (pos + pos) - 1.;
}
)"
