/*
Copyright (C) 1996-2001 Id Software, Inc.
Copyright (C) 2002-2009 John Fitzgibbons and others
Copyright (C) 2007-2008 Kristian Duske
Copyright (C) 2010-2014 QuakeSpasm developers
Copyright (C) 2016 Axel Gneiting

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

#ifndef GLQUAKE_H
#define GLQUAKE_H

#include "atomics.h"
#include "tasks.h"

void		  GL_WaitForDeviceIdle (void);
void		  VID_Restart (qboolean set_mode);
qboolean	  GL_BeginRendering (qboolean use_tasks, task_handle_t *begin_rendering_task, int *width, int *height);
qboolean	  GL_AcquireNextSwapChainImage (void);
task_handle_t GL_EndRendering (qboolean use_tasks, qboolean use_swapchain);
void		  GL_SynchronizeEndRenderingTask (void);
void		  GL_UpdateDescriptorSets (void);

extern int glwidth, glheight;

// r_local.h -- private refresh defs

#define ALIAS_BASE_SIZE_RATIO (1.0 / 11.0)
// normalizing factor so player model works out to about
//  1 pixel per triangle
#define MAX_LBM_HEIGHT		  480

#define TILE_SIZE 128 // size of textures generated by R_GenTiledSurf

#define SKYSHIFT 7
#define SKYSIZE	 (1 << SKYSHIFT)
#define SKYMASK	 (SKYSIZE - 1)

#define BACKFACE_EPSILON 0.01

#define MAX_GLTEXTURES				(16 * 4096)
#define MAX_SANITY_LIGHTMAPS		256
#define MIN_NB_DESCRIPTORS_PER_TYPE 32

#define NUM_COLOR_BUFFERS			   2
#define INITIAL_STAGING_BUFFER_SIZE_KB 16384

#define FAN_INDEX_BUFFER_SIZE 126

typedef struct
{
	VkBuffer		buffer;
	uint32_t		current_offset;
	unsigned char  *data;
	VkDeviceAddress device_address;
} dynbuffer_t;

#define LIGHTMAP_BYTES 4

void	   R_TimeRefresh_f (void);
void	   R_ReadPointFile_f (void);
texture_t *R_TextureAnimation (texture_t *base, int frame);

typedef enum
{
	pt_static,
	pt_grav,
	pt_slowgrav,
	pt_fire,
	pt_explode,
	pt_explode2,
	pt_blob,
	pt_blob2
} ptype_t;

// !!! if this is changed, it must be changed in d_ifacea.h too !!!
typedef struct particle_s
{
	// driver-usable fields
	vec3_t			   org;
	float			   color;
	// drivers never touch the following fields
	struct particle_s *next;
	vec3_t			   vel;
	float			   ramp;
	float			   die;
	ptype_t			   type;
} particle_t;

#define P_INVALID -1
#ifdef PSET_SCRIPT
void PScript_InitParticles (void);
void PScript_Shutdown (void);
void PScript_FlushDlightsTask (void *unused);				// serial: dlights queued by last frame's deferred effect spawns; must
															// run before anything reads cl_dlights and before the next layout
void PScript_UpdateParticlesSetupTask (void *unused);		// serial: frame time step, rain spawns, kill + flatten live particles
void PScript_UpdateParticlesTask (int index, void *unused); // indexed over the worker count: the parallel particle update
void PScript_LayoutParticlesTask (void *unused);			// serial: batch creation and vertex range reservation, beams, decals
void PScript_EmitParticlesTask (int index, void *unused);	// indexed over the worker count: parallel vertex generation
void PScript_DrawParticles (cb_context_t *blend_cbx, cb_context_t *wboit_cbx);
void PScript_DrawParticles_ShowTris (cb_context_t *cbx);
struct trailstate_s;
int	 PScript_ParticleTrail (vec3_t startpos, vec3_t end, int type, float timeinterval, int dlkey, vec3_t axis[3], struct trailstate_s **tsk);
int	 PScript_RunParticleEffectState (vec3_t org, vec3_t dir, float count, int typenum, struct trailstate_s **tsk);
void PScript_RunParticleWeather (vec3_t minb, vec3_t maxb, vec3_t dir, float count, int colour, const char *efname);
void PScript_EmitSkyEffectTris (qmodel_t *mod, msurface_t *fa, int ptype);
int	 PScript_FindParticleType (const char *fullname);
int	 PScript_RunParticleEffectTypeString (vec3_t org, vec3_t dir, float count, const char *name);
int	 PScript_EntParticleTrail (vec3_t oldorg, entity_t *ent, const char *name);
int	 PScript_RunParticleEffect (vec3_t org, vec3_t dir, int color, int count);
void PScript_DelinkTrailstate (struct trailstate_s **tsk);
void PScript_ClearParticles (qboolean load);
void PScript_UpdateModelEffects (qmodel_t *mod);
void PScript_ClearSurfaceParticles (qmodel_t *mod); // model is being unloaded.

extern int r_trace_line_cache_counter;
#define InvalidateTraceLineCache()    \
	do                                \
	{                                 \
		++r_trace_line_cache_counter; \
	} while (0);
#else
#define PScript_RunParticleEffectState(o, d, c, t, s)	true
#define PScript_RunParticleEffectTypeString(o, d, c, n) true // just unconditionally returns an error
#define PScript_EntParticleTrail(o, e, n)				true
#define PScript_ParticleTrail(o, e, t, d, a, s)			true
#define PScript_EntParticleTrail(o, e, n)				true
#define PScript_RunParticleEffect(o, d, p, c)			true
#define PScript_RunParticleWeather(min, max, d, c, p, n)
#define PScript_ClearSurfaceParticles(m)
#define PScript_DelinkTrailstate(tsp)
#define InvalidateTraceLineCache()
#endif

typedef struct vulkan_pipeline_layout_s
{
	VkPipelineLayout	handle;
	VkPushConstantRange push_constant_range;
	int					mboit_input_attachment_set;
} vulkan_pipeline_layout_t;

typedef struct vulkan_pipeline_s
{
	VkPipeline				 handle;
	vulkan_pipeline_layout_t layout;
} vulkan_pipeline_t;

typedef struct vulkan_desc_set_layout_s
{
	VkDescriptorSetLayout handle;
	int					  num_combined_image_samplers;
	int					  num_ubos;
	int					  num_ubos_dynamic;
	int					  num_storage_buffers;
	int					  num_input_attachments;
	int					  num_storage_images;
	int					  num_sampled_images;
	int					  num_acceleration_structures;
} vulkan_desc_set_layout_t;

typedef enum
{
	VULKAN_MEMORY_TYPE_NONE,
	VULKAN_MEMORY_TYPE_DEVICE,
	VULKAN_MEMORY_TYPE_HOST,
} vulkan_memory_type_t;

typedef struct vulkan_memory_s
{
	VkDeviceMemory		 handle;
	size_t				 size;
	vulkan_memory_type_t type;
} vulkan_memory_t;

#define WORLD_PIPELINE_COUNT			   16
// slot layout of the alias/md5 pipeline arrays: 0..3 encode alpha test/blend, 4..5 are the r_showtris variants
#define MODEL_PIPELINE_ALPHA_TEST_BIT	   1
#define MODEL_PIPELINE_ALPHA_BLEND_BIT	   2
#define MODEL_PIPELINE_SHOWTRIS			   4
#define MODEL_PIPELINE_SHOWTRIS_DEPTH_TEST 5
#define MODEL_PIPELINE_COUNT			   6
#define FTE_PARTICLE_PIPELINE_COUNT		   16
#define MAX_BATCH_SIZE					   65536
#define NUM_WORLD_CBX					   6
#define NUM_ENTITIES_CBX				   6

typedef enum
{
	PCBX_BUILD_ACCELERATION_STRUCTURES,
	PCBX_UPDATE_LIGHTMAPS,
	PCBX_UPDATE_WARP,
	PCBX_RENDER_PASSES,
	PCBX_NUM,
} primary_cb_contexts_t;

typedef enum
{
	// Main render pass:
	SCBX_WORLD,
	SCBX_ENTITIES,
	SCBX_SKY,
	SCBX_VIEW_MODEL,
	SCBX_FTE_PARTICLES_BLEND,
	SCBX_ALPHA_ENTITIES_ACROSS_WATER,
	SCBX_WATER,
	SCBX_ALPHA_ENTITIES,
	SCBX_PARTICLES,
	SCBX_MBOIT_COMPOSITE_ALPHA_ENTITIES_ACROSS_WATER,
	SCBX_MBOIT_COMPOSITE_WATER,
	SCBX_MBOIT_COMPOSITE_ALPHA_ENTITIES,
	SCBX_MBOIT_COMPOSITE_PARTICLES,
	SCBX_OIT_RESOLVE,
	// UI render Pass:
	SCBX_GUI,
	SCBX_POST_PROCESS,
	SCBX_NUM,
	// Last pass before UI
	SCBX_MAIN_OPAQUE_PASS_LAST = SCBX_FTE_PARTICLES_BLEND,
	SCBX_MAIN_PASS_LAST = SCBX_PARTICLES,
	SCBX_MBOIT_COMPOSITE_PASS_FIRST = SCBX_MBOIT_COMPOSITE_ALPHA_ENTITIES_ACROSS_WATER,
	SCBX_MBOIT_COMPOSITE_PASS_LAST = SCBX_MBOIT_COMPOSITE_PARTICLES,
} secondary_cb_contexts_t;

typedef enum
{
	RENDER_PASS_INDEX_MAIN,
	RENDER_PASS_INDEX_UI,
	RENDER_PASS_INDEX_MAIN_OIT,
	RENDER_PASS_INDEX_MAIN_MBOIT,
	RENDER_PASS_INDEX_WBOIT,
	RENDER_PASS_INDEX_MBOIT_MOMENTS,
	RENDER_PASS_INDEX_MBOIT_COMPOSITE,
	RENDER_PASS_INDEX_COUNT,
} render_pass_index_t;

typedef enum
{
	MAIN_RENDER_PASS_STANDARD,
	MAIN_RENDER_PASS_OIT,
	MAIN_RENDER_PASS_MBOIT,
	MAIN_RENDER_PASS_VARIANT_COUNT,
} main_render_pass_variant_t;

typedef enum
{
	MAIN_RENDER_PASS_STENCIL_CLEAR,
	MAIN_RENDER_PASS_NO_STENCIL,
	MAIN_RENDER_PASS_STENCIL_COUNT,
} main_render_pass_stencil_t;

typedef enum
{
	OIT_MODE_NONE,
	OIT_MODE_WBOIT,
	OIT_MODE_MBOIT,
} oit_mode_t;

extern oit_mode_t frame_oit_mode;

static inline qboolean R_UseOIT (void)
{
	return frame_oit_mode != OIT_MODE_NONE;
}

static inline qboolean R_UseWBOIT (void)
{
	return frame_oit_mode == OIT_MODE_WBOIT;
}

static inline qboolean R_UseMBOIT (void)
{
	return frame_oit_mode == OIT_MODE_MBOIT;
}

static inline main_render_pass_variant_t R_MainPassPipelineVariant (int render_pass_index)
{
	if (render_pass_index == RENDER_PASS_INDEX_MAIN_OIT)
		return MAIN_RENDER_PASS_OIT;
	if (render_pass_index == RENDER_PASS_INDEX_MAIN_MBOIT)
		return MAIN_RENDER_PASS_MBOIT;
	return MAIN_RENDER_PASS_STANDARD;
}

// selects between the standard, WBOIT accumulation, MBOIT moment and MBOIT composite
// pipelines of a shader family based on the render pass a context records into
static inline vulkan_pipeline_t R_PipelineForRenderPass (
	int render_pass_index, vulkan_pipeline_t main_pipeline, vulkan_pipeline_t wboit_pipeline, vulkan_pipeline_t mboit_moment_pipeline,
	vulkan_pipeline_t mboit_composite_pipeline)
{
	switch (render_pass_index)
	{
	case RENDER_PASS_INDEX_WBOIT:
		return wboit_pipeline;
	case RENDER_PASS_INDEX_MBOIT_MOMENTS:
		return mboit_moment_pipeline;
	case RENDER_PASS_INDEX_MBOIT_COMPOSITE:
		return mboit_composite_pipeline;
	default:
		return main_pipeline;
	}
}

static const int SECONDARY_CB_MULTIPLICITY[SCBX_NUM] = {
	NUM_WORLD_CBX,	  // SCBX_WORLD,
	NUM_ENTITIES_CBX, // SCBX_ENTITIES,
	1,				  // SCBX_SKY,
	1,				  // SCBX_VIEW_MODEL,
	1,				  // SCBX_FTE_PARTICLES_BLEND,
	1,				  // SCBX_ALPHA_ENTITIES_ACROSS_WATER,
	1,				  // SCBX_WATER,
	1,				  // SCBX_ALPHA_ENTITIES,
	1,				  // SCBX_PARTICLES,
	1,				  // SCBX_MBOIT_COMPOSITE_ALPHA_ENTITIES_ACROSS_WATER,
	1,				  // SCBX_MBOIT_COMPOSITE_WATER,
	1,				  // SCBX_MBOIT_COMPOSITE_ALPHA_ENTITIES,
	1,				  // SCBX_MBOIT_COMPOSITE_PARTICLES,
	1,				  // SCBX_OIT_RESOLVE,
	1,				  // SCBX_GUI,
	1,				  // SCBX_POST_PROCESS,
};

typedef struct cb_context_s
{
	VkCommandBuffer	  cb;
	canvastype		  current_canvas;
	VkRenderPass	  render_pass;
	int				  render_pass_index;
	int				  subpass;
	vulkan_pipeline_t current_pipeline;
	uint32_t		  vbo_indices[MAX_BATCH_SIZE];
	unsigned int	  num_vbo_indices;
} cb_context_t;

typedef struct
{
	VkDevice						 device;
	qboolean						 device_idle;
	qboolean						 validation;
	qboolean						 debug_utils;
	VkQueue							 queue;
	cb_context_t					 primary_cb_contexts[PCBX_NUM];
	cb_context_t					*secondary_cb_contexts[SCBX_NUM];
	VkClearValue					 color_clear_value;
	VkFormat						 swap_chain_format;
	qboolean						 want_full_screen_exclusive;
	qboolean						 swap_chain_full_screen_exclusive;
	qboolean						 swap_chain_full_screen_acquired;
	VkPhysicalDeviceProperties		 device_properties;
	VkPhysicalDeviceFeatures		 device_features;
	VkPhysicalDeviceMemoryProperties memory_properties;
	uint32_t						 gfx_queue_family_index;
	VkFormat						 color_format;
	VkFormat						 depth_format;
	VkSampleCountFlagBits			 sample_count;
	qboolean						 supersampling;
	qboolean						 non_solid_fill;
	qboolean						 multi_draw_indirect;
	qboolean						 screen_effects_sops;

	// Instance extensions
	qboolean get_surface_capabilities_2;
	qboolean get_physical_device_properties_2;
	qboolean vulkan_1_1_available;

	// Device extensions
	qboolean dedicated_allocation;
	qboolean full_screen_exclusive;
	qboolean ray_query;
	qboolean present_wait;

	// Buffers
	VkImage color_buffers[NUM_COLOR_BUFFERS];
	VkImage oit_accum_buffer;
	VkImage oit_reveal_buffer;
	VkImage mboit_b0_buffer;
	VkImage mboit_moments0_buffer;
	VkImage mboit_color_buffer;

	// Index buffers
	VkBuffer fan_index_buffer;

	// Staging buffers
	int staging_buffer_size;

	// Render passes
	VkRenderPass main_render_pass[MAIN_RENDER_PASS_VARIANT_COUNT][MAIN_RENDER_PASS_STENCIL_COUNT];
	VkRenderPass warp_render_pass;

	// Pipelines
	vulkan_pipeline_t		 basic_alphatest_pipeline[RENDER_PASS_INDEX_COUNT];
	vulkan_pipeline_t		 basic_blend_pipeline[RENDER_PASS_INDEX_COUNT];
	vulkan_pipeline_t		 basic_notex_blend_pipeline[RENDER_PASS_INDEX_COUNT];
	vulkan_pipeline_layout_t basic_pipeline_layout;
	vulkan_pipeline_t		 world_pipelines[MAIN_RENDER_PASS_VARIANT_COUNT][WORLD_PIPELINE_COUNT];
	vulkan_pipeline_t		 world_wboit_pipelines[WORLD_PIPELINE_COUNT];
	vulkan_pipeline_t		 world_mboit_moment_pipelines[WORLD_PIPELINE_COUNT];
	vulkan_pipeline_t		 world_mboit_composite_pipelines[WORLD_PIPELINE_COUNT];
	vulkan_pipeline_layout_t world_pipeline_layout;
	vulkan_pipeline_t		 raster_tex_warp_pipeline;
	vulkan_pipeline_t		 particle_pipeline;
	vulkan_pipeline_t		 particle_oit_pipeline;
	vulkan_pipeline_t		 particle_post_oit_pipeline[MAIN_RENDER_PASS_VARIANT_COUNT];
	vulkan_pipeline_t		 particle_mboit_moment_pipeline;
	vulkan_pipeline_t		 particle_mboit_composite_pipeline;
	vulkan_pipeline_t		 sprite_pipeline[MAIN_RENDER_PASS_VARIANT_COUNT];
	vulkan_pipeline_t		 sprite_oit_pipeline;
	vulkan_pipeline_t		 sprite_mboit_moment_pipeline;
	vulkan_pipeline_t		 sprite_mboit_composite_pipeline;
	vulkan_pipeline_layout_t sky_pipeline_layout[2]; // one texture (cubemap-like), two textures (animated layers)
	vulkan_pipeline_t		 sky_stencil_pipeline[MAIN_RENDER_PASS_VARIANT_COUNT][2];
	vulkan_pipeline_t		 sky_color_pipeline[MAIN_RENDER_PASS_VARIANT_COUNT][2];
	vulkan_pipeline_t		 sky_box_pipeline[MAIN_RENDER_PASS_VARIANT_COUNT];
	vulkan_pipeline_t		 sky_cube_pipeline[MAIN_RENDER_PASS_VARIANT_COUNT][2];
	vulkan_pipeline_t		 sky_layer_pipeline[MAIN_RENDER_PASS_VARIANT_COUNT][2];
	vulkan_pipeline_t		 alias_pipelines[MAIN_RENDER_PASS_VARIANT_COUNT][MODEL_PIPELINE_COUNT];
	vulkan_pipeline_t		 alias_wboit_pipelines[MODEL_PIPELINE_COUNT];
	vulkan_pipeline_t		 alias_mboit_moment_pipelines[MODEL_PIPELINE_COUNT];
	vulkan_pipeline_t		 alias_mboit_composite_pipelines[MODEL_PIPELINE_COUNT];
	vulkan_pipeline_t		 md5_pipelines[MAIN_RENDER_PASS_VARIANT_COUNT][MODEL_PIPELINE_COUNT];
	vulkan_pipeline_t		 md5_wboit_pipelines[MODEL_PIPELINE_COUNT];
	vulkan_pipeline_t		 md5_mboit_moment_pipelines[MODEL_PIPELINE_COUNT];
	vulkan_pipeline_t		 md5_mboit_composite_pipelines[MODEL_PIPELINE_COUNT];
	vulkan_pipeline_t		 postprocess_pipeline;
	vulkan_pipeline_t		 wboit_resolve_pipeline;
	vulkan_pipeline_t		 mboit_resolve_pipeline;
	vulkan_pipeline_t		 screen_effects_pipeline;
	vulkan_pipeline_t		 screen_effects_scale_pipeline;
	vulkan_pipeline_t		 screen_effects_scale_sops_pipeline;
	vulkan_pipeline_t		 cs_tex_warp_pipeline;
	vulkan_pipeline_t		 showtris_pipeline[MAIN_RENDER_PASS_VARIANT_COUNT];
	vulkan_pipeline_t		 showtris_indirect_pipeline[MAIN_RENDER_PASS_VARIANT_COUNT];
	vulkan_pipeline_t		 showtris_depth_test_pipeline[MAIN_RENDER_PASS_VARIANT_COUNT];
	vulkan_pipeline_t		 showtris_indirect_depth_test_pipeline[MAIN_RENDER_PASS_VARIANT_COUNT];
	vulkan_pipeline_t		 showbboxes_pipeline[MAIN_RENDER_PASS_VARIANT_COUNT];
	vulkan_pipeline_t		 update_lightmap_pipeline;
	vulkan_pipeline_t		 update_lightmap_rt_pipeline;
	vulkan_pipeline_t		 indirect_draw_pipeline;
	vulkan_pipeline_t		 indirect_clear_pipeline;
	vulkan_pipeline_t		 ray_debug_pipeline;
	vulkan_pipeline_t		 mesh_interpolate_pipeline;
	vulkan_pipeline_t		 skinning_pipeline;
#ifdef PSET_SCRIPT
	vulkan_pipeline_t fte_particle_pipelines[MAIN_RENDER_PASS_VARIANT_COUNT][FTE_PARTICLE_PIPELINE_COUNT];
	vulkan_pipeline_t fte_particle_wboit_pipelines[FTE_PARTICLE_PIPELINE_COUNT];
	vulkan_pipeline_t fte_particle_post_oit_pipelines[MAIN_RENDER_PASS_VARIANT_COUNT][FTE_PARTICLE_PIPELINE_COUNT];
#endif

	// Descriptors
	VkDescriptorPool		 descriptor_pool;
	vulkan_desc_set_layout_t ubo_set_layout;
	vulkan_desc_set_layout_t single_texture_set_layout;
	vulkan_desc_set_layout_t input_attachment_set_layout;
	vulkan_desc_set_layout_t oit_input_attachment_set_layout;
	vulkan_desc_set_layout_t mboit_input_attachment_set_layout;
	VkDescriptorSet			 mboit_input_attachment_descriptor_set;
	VkDescriptorSet			 screen_effects_desc_set;
	vulkan_desc_set_layout_t screen_effects_set_layout;
	vulkan_desc_set_layout_t single_texture_cs_write_set_layout;
	vulkan_desc_set_layout_t lightmap_compute_set_layout;
	VkDescriptorSet			 indirect_compute_desc_set;
	vulkan_desc_set_layout_t indirect_compute_set_layout;
	vulkan_desc_set_layout_t ray_query_push_set_layout;
	VkDescriptorSet			 ray_debug_desc_set;
	vulkan_desc_set_layout_t ray_debug_set_layout;
	vulkan_desc_set_layout_t joints_buffer_set_layout;

	// Samplers
	VkSampler point_sampler;
	VkSampler linear_sampler;
	VkSampler point_aniso_sampler;
	VkSampler linear_aniso_sampler;
	VkSampler point_sampler_lod_bias;
	VkSampler linear_sampler_lod_bias;
	VkSampler point_aniso_sampler_lod_bias;
	VkSampler linear_aniso_sampler_lod_bias;

	// Matrices
	float projection_matrix[16];
	float view_matrix[16];
	float view_projection_matrix[16];

	// Dispatch table
	PFN_vkCmdBindPipeline			vk_cmd_bind_pipeline;
	PFN_vkCmdPushConstants			vk_cmd_push_constants;
	PFN_vkCmdBindDescriptorSets		vk_cmd_bind_descriptor_sets;
	PFN_vkCmdBindIndexBuffer		vk_cmd_bind_index_buffer;
	PFN_vkCmdBindVertexBuffers		vk_cmd_bind_vertex_buffers;
	PFN_vkCmdDraw					vk_cmd_draw;
	PFN_vkCmdDrawIndexed			vk_cmd_draw_indexed;
	PFN_vkCmdDrawIndexedIndirect	vk_cmd_draw_indexed_indirect;
	PFN_vkCmdPipelineBarrier		vk_cmd_pipeline_barrier;
	PFN_vkCmdCopyBufferToImage		vk_cmd_copy_buffer_to_image;
	PFN_vkCmdDispatch				vk_cmd_dispatch;
	PFN_vkCmdPushDescriptorSetKHR	vk_cmd_push_descriptor_set;
	PFN_vkGetBufferDeviceAddressKHR vk_get_buffer_device_address;

	PFN_vkGetAccelerationStructureBuildSizesKHR		   vk_get_acceleration_structure_build_sizes;
	PFN_vkCreateAccelerationStructureKHR			   vk_create_acceleration_structure;
	PFN_vkDestroyAccelerationStructureKHR			   vk_destroy_acceleration_structure;
	PFN_vkCmdBuildAccelerationStructuresKHR			   vk_cmd_build_acceleration_structures;
	PFN_vkGetAccelerationStructureDeviceAddressKHR	   vk_get_acceleration_structure_device_address;
	VkPhysicalDeviceAccelerationStructurePropertiesKHR physical_device_acceleration_structure_properties;

#ifdef _DEBUG
	PFN_vkCmdBeginDebugUtilsLabelEXT vk_cmd_begin_debug_utils_label;
	PFN_vkCmdEndDebugUtilsLabelEXT	 vk_cmd_end_debug_utils_label;
#endif
} vulkanglobals_t;

extern vulkanglobals_t vulkan_globals;
extern qboolean		   indirect;

//====================================================

extern int		r_visframecount; // ??? what difs?
extern int		r_framecount;
extern mplane_t frustum[4];
extern qboolean render_warp;
extern qboolean in_update_screen;
extern qboolean use_simd;
extern int		render_scale;

//
// view origin
//
extern vec3_t vup;
extern vec3_t vpn;
extern vec3_t vright;
extern vec3_t r_origin;

//
// screen size info
//
extern refdef_t r_refdef;
extern mleaf_t *r_viewleaf, *r_oldviewleaf;
extern int		d_lightstylevalue[MAX_LIGHTSTYLES]; // 8.8 fraction of base light value

extern cvar_t r_drawentities;
extern cvar_t r_drawworld;
extern cvar_t r_drawviewmodel;
extern cvar_t r_speeds;
extern cvar_t r_pos;
extern cvar_t r_waterwarp;
extern cvar_t r_fullbright;
extern cvar_t r_lightmap;
extern cvar_t r_wateralpha;
extern cvar_t r_oit;
extern cvar_t r_lavaalpha;
extern cvar_t r_telealpha;
extern cvar_t r_slimealpha;
extern cvar_t r_dynamic;
extern cvar_t r_novis;
extern cvar_t r_scale;

extern cvar_t gl_polyblend;
extern cvar_t gl_nocolors;

// johnfitz -- polygon offset
#define OFFSET_NONE	 0
#define OFFSET_DECAL 1

// johnfitz -- rendering statistics
extern atomic_uint32_t rs_brushpolys, rs_aliaspolys, rs_skypolys, rs_particles, rs_fogpolys;
extern atomic_uint32_t rs_dynamiclightmaps, rs_brushpasses, rs_aliaspasses;

extern atomic_uint64_t total_device_vulkan_allocation_size;
extern atomic_uint64_t total_host_vulkan_allocation_size;

// johnfitz -- track developer statistics that vary every frame
extern cvar_t devstats;
typedef struct
{
	int packetsize;
	int edicts;
	int visedicts;
	int efrags;
	int tempents;
	int beams;
	int dlights;
} devstats_t;
extern devstats_t dev_stats, dev_peakstats;

// ohnfitz -- reduce overflow warning spam
typedef struct
{
	double packetsize;
	double efrags;
	double beams;
	double varstring;
} overflowtimes_t;
extern overflowtimes_t dev_overflows; // this stores the last time overflow messages were displayed, not the last time overflows occured
#define CONSOLE_RESPAM_TIME 3		  // seconds between repeated warning messages

typedef struct
{
	float position[3];
	float texcoord[2];
	byte  color[4];
} basicvertex_t;

// johnfitz -- moved here from r_brush.c
extern int gl_lightmap_format;

// keep in sync with world.frag
#define LMBLOCK_WIDTH \
	1024 // FIXME: make dynamic. if we have a decent card there's no real reason not to use 4k or 16k (assuming there's no lightstyles/dynamics that need
		 // uploading...)
#define LMBLOCK_HEIGHT 1024 // Alternatively, use texture arrays, which would avoid the need to switch textures as often.

#define LM_CULL_BLOCK_W 128
#define LM_CULL_BLOCK_H 256

#define LM_WORKGROUP_SUBMODEL_EMPTY 0xFFFFFFFE // no surfaces assigned yet, converted to 0 before upload
#define LM_WORKGROUP_SUBMODEL_MIXED 0xFFFFFFFF // surfaces from multiple coordinate spaces, the shader can't cull and passes all lights

typedef struct lm_compute_workgroup_bounds_s
{
	float	 mins[3];
	float	 maxs[3];
	uint32_t submodel; // submodel the surfaces in this workgroup belong to (bounds are in its model space), or LM_WORKGROUP_SUBMODEL_*
} lm_compute_workgroup_bounds_t;
COMPILE_TIME_ASSERT (lm_compute_workgroup_bounds_t, sizeof (lm_compute_workgroup_bounds_t) == 28);

typedef struct glRect_s
{
	unsigned short l, t, w, h;
} glRect_t;
typedef struct glMaxUsed_s
{
	unsigned short w, h;
} glMaxUsed_t;
struct lightmap_s
{
	gltexture_t	   *texture;
	gltexture_t	   *surface_indices_texture;
	gltexture_t	   *lightstyle_textures[MAXLIGHTMAPS * 3 / 4];
	VkDescriptorSet descriptor_set;
	uint32_t	modified[TASKS_MAX_WORKERS]; // when using GPU lightmap update, bitmap of lightstyles that will be drawn using this lightmap (16..64 OR-folded
											 // into bits 16..31)
	VkBuffer	workgroup_bounds_buffer;
	glRect_t	rectchange;
	glMaxUsed_t lightstyle_rectused[1 + MAXLIGHTMAPS * 3 / 4]; // [0]: surface_indices; [1,2,3]: lightstyle_textures[0,1,2]

	lm_compute_workgroup_bounds_t global_bounds[LMBLOCK_HEIGHT / LM_CULL_BLOCK_H][LMBLOCK_WIDTH / LM_CULL_BLOCK_W];
	byte						  active_dlights[LMBLOCK_HEIGHT / LM_CULL_BLOCK_H][LMBLOCK_WIDTH / LM_CULL_BLOCK_W];
	byte						  block_has_submodels[LMBLOCK_HEIGHT / LM_CULL_BLOCK_H][LMBLOCK_WIDTH / LM_CULL_BLOCK_W];
	byte						  num_used_lightstyles[LMBLOCK_HEIGHT / LM_CULL_BLOCK_H][LMBLOCK_WIDTH / LM_CULL_BLOCK_W];
	byte						  used_lightstyles[LMBLOCK_HEIGHT / LM_CULL_BLOCK_H][LMBLOCK_WIDTH / LM_CULL_BLOCK_W][MAX_LIGHTSTYLES];
	int							  cached_light[MAX_LIGHTSTYLES];
	int							  cached_framecount;

	// the lightmap texture data needs to be kept in
	// main memory so texsubimage can update properly
	byte						  *data;			   //[4*LMBLOCK_WIDTH*LMBLOCK_HEIGHT];
	byte						  *lightstyle_data[4]; //[4*LMBLOCK_WIDTH*LMBLOCK_HEIGHT];
	uint32_t					  *surface_indices;	   //[LMBLOCK_WIDTH*LMBLOCK_HEIGHT];
	lm_compute_workgroup_bounds_t *workgroup_bounds;   //[(LMBLOCK_WIDTH/8)*(LMBLOCK_HEIGHT/8)];
};
extern struct lightmap_s *lightmaps;
extern int				  lightmap_count; // allocated lightmaps

extern qboolean r_fullbright_cheatsafe, r_lightmap_cheatsafe, r_drawworld_cheatsafe; // johnfitz

extern float map_wateralpha, map_lavaalpha, map_telealpha, map_slimealpha; // ericw
extern float map_fallbackalpha; // spike -- because we might want r_wateralpha to apply to teleporters while water itself wasn't watervised

qboolean R_UseAlphaSort (void);
qboolean R_UseIndirectTransparentWater (void);

extern task_handle_t prev_end_rendering_task;

// johnfitz -- fog functions called from outside gl_fog.c
void		Fog_Update (float density, float red, float green, float blue, float time);
void		Fog_ParseServerMessage (void);
void		Fog_GetColor (float *c);
float		Fog_GetDensity (void);
void		Fog_ResetFade (void);
const char *Fog_GetFogCommand (qboolean always);
void		Fog_EnableGFog (cb_context_t *cbx);
void		Fog_DisableGFog (cb_context_t *cbx);
void		Fog_SetupFrame (cb_context_t *cbx);
void		Fog_NewMap (void);
void		Fog_Init (void);

void R_NewGame (void);

void R_AnimateLight (void);
void R_BuildTopLevelAccelerationStructure (void *unused);
void R_UpdateAnimatedBLASes (cb_context_t *cbx);
void R_UpdateLightmapsAndIndirect (void *unused);
void R_MarkSurfaces (qboolean use_tasks, task_handle_t before_mark, task_handle_t *store_efrags, task_handle_t *cull_surfaces, task_handle_t *chain_surfaces);
qboolean	  R_CullBox (vec3_t emins, vec3_t emaxs);
void		  R_StoreEfrags (efrag_t **ppefrag);
qboolean	  R_CullModelForEntity (entity_t *e);
void		  R_RotateForEntity (float matrix[16], vec3_t origin, vec3_t angles, unsigned char scale);
void		  R_MarkLights (dlight_t *light, int num, mnode_t *node);
extern vec3_t lightmap_dlight_origins[MAX_DLIGHTS]; // dlight origins in the space of the model currently having its lightmaps built (CPU lightmap update path)
void		  R_ParseEntityDlights (void);			// 2021 rerelease "dynamiclight" entities, parsed from the entity lump at map load
void		  R_UpdateEntityDlights (void);			// keeps the parsed entity dlights alive, called every frame

void R_InitParticles (void);
void R_DrawParticles (cb_context_t *cbx);
void CL_RunParticles (void);
void R_ClearParticles (void);

void R_TranslatePlayerSkin (int playernum);
void R_TranslateNewPlayerSkin (int playernum); // johnfitz -- this handles cases when the actual texture changes
void R_UpdateWarpTextures (void *unused);

void R_MarkDeps (int combined_deps, int worker_index);

qboolean R_IndirectBrush (entity_t *e);

void R_DrawWorld (cb_context_t *cbx, int index);

// johnfitz -- struct for passing lerp information to drawing functions
typedef struct
{
	short  pose1;
	short  pose2;
	float  blend;
	vec3_t origin;
	vec3_t angles;
} lerpdata_t;
// johnfitz

void R_UpdateEntityAnimState (entity_t *e, aliashdr_t *paliashdr);
void R_UpdateEntityMoveState (entity_t *e);
void R_GetEntityLerpedTransform (entity_t *e, vec3_t out_origin, vec3_t out_angles);
void R_SetupAliasFrame (entity_t *e, aliashdr_t *paliashdr, int frame, lerpdata_t *lerpdata);
void R_DrawAliasModel (cb_context_t *cbx, entity_t *e, int *aliaspolys);
void R_DrawBrushModel (cb_context_t *cbx, entity_t *e, int chain, int *brushpolys, qboolean sort, qboolean water_opaque_only, qboolean water_transparent_only);
void R_DrawSpriteModel (cb_context_t *cbx, entity_t *e);
void R_DrawIndirectBrushes (cb_context_t *cbx, qboolean draw_water, qboolean transparent_water, qboolean draw_sky, int index);
void R_DrawIndirectBrushes_ShowTris (cb_context_t *cbx);

void R_DrawTextureChains_Water (cb_context_t *cbx, qmodel_t *model, entity_t *ent, texchain_t chain, qboolean opaque_only, qboolean transparent_only);

void GL_BuildLightmaps (void);
void GL_SetupIndirectDraws (void);
void GL_SetupLightmapCompute (void);
void GL_UpdateLightmapDescriptorSets (void);
void GL_DeleteBModelVertexBuffer (void);
void GL_DeleteBModelAccelerationStructures (void);
void GL_BuildBModelVertexBuffer (void);
void GL_BuildBModelAccelerationStructures (void);
void GL_PrepareSIMDAndParallelData (void);
void GLMesh_UploadBuffers (qmodel_t *mod, aliashdr_t *hdr, unsigned short *indexes, byte *vertexes, aliasmesh_t *desc, jointpose_t *joints);
void GLMesh_DeleteAllMeshBuffers (void);
void R_AllocateEntityBLAS (entity_t *e);
void R_FreeEntityBLAS (entity_t *e);
void R_FreeAllEntityBLASes (void);

extern dynbuffer_t as_scratch_buffer;
extern uint32_t	   as_scratch_buffer_size;
void			   R_EnsureASScratchBufferSize (uint32_t required_size);
void			   R_FreeASScratchBuffer (void);
void			   R_CollectTLASGarbage (void);

int R_LightPoint (vec3_t p, float ofs, lightcache_t *cache, vec3_t *lightcolor);

void R_BuildLightMap (msurface_t *surf, byte *dest, int stride);
void R_RenderDynamicLightmaps (msurface_t *fa);
void R_UploadLightmaps (void);

void R_DrawWorld_ShowTris (cb_context_t *cbx);
void R_DrawBrushModel_ShowTris (cb_context_t *cbx, entity_t *e);
void R_DrawAliasModel_ShowTris (cb_context_t *cbx, entity_t *e);
void R_DrawParticles_ShowTris (cb_context_t *cbx);
void R_DrawSpriteModel_ShowTris (cb_context_t *cbx, entity_t *e);

void DrawGLPoly (cb_context_t *cbx, glpoly_t *p, float color[3], float alpha);
void GLMesh_DeleteMeshBuffers (aliashdr_t *mainhdr);
void GL_MakeAliasModelDisplayLists (qmodel_t *m, aliashdr_t *hdr);

void		Sky_Init (void);
void		Sky_ClearAll (void);
void		Sky_DrawSky (cb_context_t *cbx);
qboolean	Sky_NeedStencil ();
void		Sky_NewMap (void);
void		Sky_LoadTexture (qmodel_t *mod, texture_t *mt, int tex_index);
void		Sky_LoadTextureQ64 (qmodel_t *mod, texture_t *mt, int tex_index);
void		Sky_LoadSkyBox (const char *name);
const char *Sky_GetSkyCommand (qboolean always);
void		Sky_SetSkyfog (float value);

void R_ClearTextureChains (qmodel_t *mod, texchain_t chain);
void R_ChainSurface (msurface_t *surf, texchain_t chain);
void R_DrawTextureChains (cb_context_t *cbx, qmodel_t *model, entity_t *ent, texchain_t chain);
void R_DrawWorld_Water (cb_context_t *cbx, qboolean transparent);

float GL_WaterAlphaForSurface (msurface_t *fa);
float GL_WaterAlphaForTextureType (textype_t type);
float GL_WaterAlphaForEntityTextureType (entity_t *ent, textype_t type);

int GL_MemoryTypeFromProperties (uint32_t type_bits, VkFlags requirements_mask, VkFlags preferred_mask);

void R_CreateDescriptorPool ();
void R_CreateDescriptorSetLayouts ();
void R_InitSamplers ();
void R_CreatePipelineLayouts ();
void R_CreatePipelines ();
void R_DestroyPipelines ();

#define MAX_PUSH_CONSTANT_SIZE 128 // Vulkan guaranteed minimum maxPushConstantsSize

static inline void R_BindPipeline (cb_context_t *cbx, VkPipelineBindPoint bind_point, vulkan_pipeline_t pipeline)
{
	static byte zeroes[MAX_PUSH_CONSTANT_SIZE];
	assert (pipeline.handle != VK_NULL_HANDLE);
	assert (pipeline.layout.handle != VK_NULL_HANDLE);
	assert (cbx->current_pipeline.layout.push_constant_range.size <= MAX_PUSH_CONSTANT_SIZE);
	if (cbx->current_pipeline.handle != pipeline.handle)
	{
		vulkan_globals.vk_cmd_bind_pipeline (cbx->cb, bind_point, pipeline.handle);
		if ((pipeline.layout.push_constant_range.size > 0) &&
			((cbx->current_pipeline.layout.push_constant_range.stageFlags != pipeline.layout.push_constant_range.stageFlags) ||
			 (cbx->current_pipeline.layout.push_constant_range.size != pipeline.layout.push_constant_range.size)))
			vulkan_globals.vk_cmd_push_constants (
				cbx->cb, pipeline.layout.handle, pipeline.layout.push_constant_range.stageFlags, 0, pipeline.layout.push_constant_range.size, zeroes);
		cbx->current_pipeline = pipeline;
		if (cbx->render_pass_index == RENDER_PASS_INDEX_MBOIT_COMPOSITE && pipeline.layout.mboit_input_attachment_set >= 0)
		{
			vulkan_globals.vk_cmd_bind_descriptor_sets (
				cbx->cb, bind_point, pipeline.layout.handle, pipeline.layout.mboit_input_attachment_set, 1,
				&vulkan_globals.mboit_input_attachment_descriptor_set, 0, NULL);
		}
	}
}

static inline void R_PushConstants (cb_context_t *cbx, VkShaderStageFlags stage_flags, int offset, int size, const void *data)
{
	vulkan_globals.vk_cmd_push_constants (cbx->cb, cbx->current_pipeline.layout.handle, stage_flags, offset, size, data);
}

static inline void R_BeginDebugUtilsLabel (cb_context_t *cbx, const char *name)
{
#ifdef _DEBUG
	ZEROED_STRUCT (VkDebugUtilsLabelEXT, label);
	label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
	label.pLabelName = name;
	if (vulkan_globals.vk_cmd_begin_debug_utils_label)
		vulkan_globals.vk_cmd_begin_debug_utils_label (cbx->cb, &label);
#endif
}

static inline void R_EndDebugUtilsLabel (cb_context_t *cbx)
{
#ifdef _DEBUG
	if (vulkan_globals.vk_cmd_end_debug_utils_label)
		vulkan_globals.vk_cmd_end_debug_utils_label (cbx->cb);
#endif
}

void R_AllocateVulkanMemory (vulkan_memory_t *memory, VkMemoryAllocateInfo *memory_allocate_info, vulkan_memory_type_t type, atomic_uint32_t *num_allocations);
void R_FreeVulkanMemory (vulkan_memory_t *memory, atomic_uint32_t *num_allocations);

void R_CreateBuffer (
	VkBuffer *buffer, vulkan_memory_t *memory, const size_t size, VkBufferUsageFlags usage, const VkFlags mem_requirements_mask,
	const VkFlags mem_preferred_mask, atomic_uint32_t *num_allocations, VkDeviceAddress *device_address, const char *name);
void R_FreeBuffer (const VkBuffer buffer, vulkan_memory_t *memory, atomic_uint32_t *num_allocations);

typedef struct buffer_create_info_s
{
	VkBuffer		  *buffer;
	size_t			   size;
	size_t			   alignment;
	VkBufferUsageFlags usage;
	void			 **mapped;
	VkDeviceAddress	  *address;
	const char		  *name;
} buffer_create_info_t;
size_t R_CreateBuffers (
	const int num_buffers, buffer_create_info_t *create_infos, vulkan_memory_t *memory, const VkFlags mem_requirements_mask, const VkFlags mem_preferred_mask,
	atomic_uint32_t *num_allocations, const char *memory_name);
void R_FreeBuffers (const int num_buffers, VkBuffer *buffers, vulkan_memory_t *memory, atomic_uint32_t *num_allocations);

VkDescriptorSet R_AllocateDescriptorSet (vulkan_desc_set_layout_t *layout);
void			R_FreeDescriptorSet (VkDescriptorSet desc_set, vulkan_desc_set_layout_t *layout);

void  R_InitStagingBuffers (void);
void  R_SubmitStagingBuffers (void);
byte *R_StagingAllocate (int size, int alignment, VkCommandBuffer *cb_context, VkBuffer *buffer, int *buffer_offset);
void  R_StagingBeginCopy (void);
void  R_StagingEndCopy (void);
void  R_StagingUploadBuffer (const VkBuffer buffer, const size_t size, const byte *data);

void		   R_InitGPUBuffers (void);
void		   R_InitMeshHeap (void);
glheapstats_t *R_GetMeshHeapStats (void);
void		   R_AddDynamicBufferGarbage (vulkan_memory_t memory, dynbuffer_t *buffers, int num_buffers, VkDescriptorSet *descriptor_sets);
void		   R_SwapDynamicBuffers (void);
void		   R_FlushDynamicBuffers (void);
void		   R_CollectDynamicBufferGarbage (void);
void		   R_CollectMeshBufferGarbage (void);
byte		  *R_VertexAllocate (int size, VkBuffer *buffer, VkDeviceSize *buffer_offset);
byte		  *R_IndexAllocate (int size, VkBuffer *buffer, VkDeviceSize *buffer_offset);
byte		  *R_UniformAllocate (int size, VkBuffer *buffer, uint32_t *buffer_offset, VkDescriptorSet *descriptor_set);
byte		  *R_StorageAllocate (int size, VkBuffer *buffer, VkDeviceSize *buffer_offset, VkDeviceAddress *device_address);

void R_AllocateLightmapComputeBuffers ();

void GL_SetObjectName (uint64_t object, VkObjectType object_type, const char *name);

#endif /* GLQUAKE_H */
