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;
};

config_t unpack_vertex_options()
{
	config_t result;
	result.no_vertex_snap = bitfieldExtract(vertex_config, 0, 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)
{
	vec4 ret = (coord * ui_scale.zwzw) / ui_scale.xyxy;
#ifndef VULKAN
	// Flip Y for OpenGL
	ret.yw = 1. - ret.yw;
#endif
	return ret;
}

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

void main()
{
	tc0.xy = in_pos.zw;
	color = albedo;
	clip_rect = ndc_to_window(clip_to_ndc(clip_bounds));

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

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

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