Browse Source

Added variable rate shading.

Бранимир Караџић 1 month ago
parent
commit
358223c63e

+ 34 - 4
bindings/bf/bgfx.bf

@@ -1142,25 +1142,30 @@ public static class bgfx
 		/// </summary>
 		TransparentBackbuffer  = 0x0000000004000000,
 	
+		/// <summary>
+		/// Variable Rate Shading
+		/// </summary>
+		VariableRateShading    = 0x0000000008000000,
+	
 		/// <summary>
 		/// Vertex attribute half-float is supported.
 		/// </summary>
-		VertexAttribHalf       = 0x0000000008000000,
+		VertexAttribHalf       = 0x0000000010000000,
 	
 		/// <summary>
 		/// Vertex attribute 10_10_10_2 is supported.
 		/// </summary>
-		VertexAttribUint10     = 0x0000000010000000,
+		VertexAttribUint10     = 0x0000000020000000,
 	
 		/// <summary>
 		/// Rendering with VertexID only is supported.
 		/// </summary>
-		VertexId               = 0x0000000020000000,
+		VertexId               = 0x0000000040000000,
 	
 		/// <summary>
 		/// Viewport layer is available in vertex shader.
 		/// </summary>
-		ViewportLayerArray     = 0x0000000040000000,
+		ViewportLayerArray     = 0x0000000080000000,
 	
 		/// <summary>
 		/// All texture compare modes are supported.
@@ -2006,6 +2011,20 @@ public static class bgfx
 		Count
 	}
 	
+	[AllowDuplicates]
+	public enum ShadingRate : uint32
+	{
+		Rate1x1,
+		Rate1x2,
+		Rate2x1,
+		Rate2x2,
+		Rate2x4,
+		Rate4x2,
+		Rate4x4,
+	
+		Count
+	}
+	
 	[AllowDuplicates]
 	public enum NativeWindowHandleType : uint32
 	{
@@ -3630,6 +3649,17 @@ public static class bgfx
 	[LinkName("bgfx_set_view_order")]
 	public static extern void set_view_order(ViewId _id, uint16 _num, ViewId* _order);
 	
+	/// <summary>
+	/// Set view shading rate.
+	/// @attention Availability depends on: `BGFX_CAPS_VARIABLE_RATE_SHADING`.
+	/// </summary>
+	///
+	/// <param name="_id">View id.</param>
+	/// <param name="_shadingRate">Shading rate.</param>
+	///
+	[LinkName("bgfx_set_view_shading_rate")]
+	public static extern void set_view_shading_rate(ViewId _id, ShadingRate _shadingRate);
+	
 	/// <summary>
 	/// Reset all view settings to default.
 	/// </summary>

+ 26 - 4
bindings/c3/bgfx.c3

@@ -723,17 +723,20 @@ enum CapsFlags : const ulong
 	// Transparent back buffer supported.
 	TRANSPARENTBACKBUFFER  = 0x0000000004000000,
 
+	// Variable Rate Shading
+	VARIABLERATESHADING    = 0x0000000008000000,
+
 	// Vertex attribute half-float is supported.
-	VERTEXATTRIBHALF       = 0x0000000008000000,
+	VERTEXATTRIBHALF       = 0x0000000010000000,
 
 	// Vertex attribute 10_10_10_2 is supported.
-	VERTEXATTRIBUINT10     = 0x0000000010000000,
+	VERTEXATTRIBUINT10     = 0x0000000020000000,
 
 	// Rendering with VertexID only is supported.
-	VERTEXID               = 0x0000000020000000,
+	VERTEXID               = 0x0000000040000000,
 
 	// Viewport layer is available in vertex shader.
-	VIEWPORTLAYERARRAY     = 0x0000000040000000,
+	VIEWPORTLAYERARRAY     = 0x0000000080000000,
 
 	// All texture compare modes are supported.
 	TEXTURECOMPAREALL      = 0x0000000000180000,
@@ -1294,6 +1297,19 @@ enum ViewMode : uint
 	COUNT
 }
 
+enum ShadingRate : uint
+{
+	RATE1X1,
+	RATE1X2,
+	RATE2X1,
+	RATE2X2,
+	RATE2X4,
+	RATE4X2,
+	RATE4X4,
+
+	COUNT
+}
+
 enum NativeWindowHandleType : uint
 {
 	// Platform default handle type (X11 on Linux).
@@ -2564,6 +2580,12 @@ extern fn void set_view_transform(ushort _id, void* _view, void* _proj) @extern(
 // _order : `View remap id table. Passing `NULL` will reset view ids to default state.`
 extern fn void set_view_order(ushort _id, ushort _num, ushort* _order) @extern("bgfx_set_view_order");
 
+// Set view shading rate.
+// @attention Availability depends on: `BGFX_CAPS_VARIABLE_RATE_SHADING`.
+// _id : `View id.`
+// _shadingRate : `Shading rate.`
+extern fn void set_view_shading_rate(ushort _id, ShadingRate _shadingRate) @extern("bgfx_set_view_shading_rate");
+
 // Reset all view settings to default.
 extern fn void reset_view(ushort _id) @extern("bgfx_reset_view");
 

+ 33 - 4
bindings/cs/bgfx.cs

@@ -1141,25 +1141,30 @@ public static partial class bgfx
 		/// </summary>
 		TransparentBackbuffer  = 0x0000000004000000,
 	
+		/// <summary>
+		/// Variable Rate Shading
+		/// </summary>
+		VariableRateShading    = 0x0000000008000000,
+	
 		/// <summary>
 		/// Vertex attribute half-float is supported.
 		/// </summary>
-		VertexAttribHalf       = 0x0000000008000000,
+		VertexAttribHalf       = 0x0000000010000000,
 	
 		/// <summary>
 		/// Vertex attribute 10_10_10_2 is supported.
 		/// </summary>
-		VertexAttribUint10     = 0x0000000010000000,
+		VertexAttribUint10     = 0x0000000020000000,
 	
 		/// <summary>
 		/// Rendering with VertexID only is supported.
 		/// </summary>
-		VertexId               = 0x0000000020000000,
+		VertexId               = 0x0000000040000000,
 	
 		/// <summary>
 		/// Viewport layer is available in vertex shader.
 		/// </summary>
-		ViewportLayerArray     = 0x0000000040000000,
+		ViewportLayerArray     = 0x0000000080000000,
 	
 		/// <summary>
 		/// All texture compare modes are supported.
@@ -1992,6 +1997,19 @@ public static partial class bgfx
 		Count
 	}
 	
+	public enum ShadingRate
+	{
+		Rate1x1,
+		Rate1x2,
+		Rate2x1,
+		Rate2x2,
+		Rate2x4,
+		Rate4x2,
+		Rate4x4,
+	
+		Count
+	}
+	
 	public enum NativeWindowHandleType
 	{
 		/// <summary>
@@ -3585,6 +3603,17 @@ public static partial class bgfx
 	[DllImport(DllName, EntryPoint="bgfx_set_view_order", CallingConvention = CallingConvention.Cdecl)]
 	public static extern unsafe void set_view_order(ushort _id, ushort _num, ushort* _order);
 	
+	/// <summary>
+	/// Set view shading rate.
+	/// @attention Availability depends on: `BGFX_CAPS_VARIABLE_RATE_SHADING`.
+	/// </summary>
+	///
+	/// <param name="_id">View id.</param>
+	/// <param name="_shadingRate">Shading rate.</param>
+	///
+	[DllImport(DllName, EntryPoint="bgfx_set_view_shading_rate", CallingConvention = CallingConvention.Cdecl)]
+	public static extern unsafe void set_view_shading_rate(ushort _id, ShadingRate _shadingRate);
+	
 	/// <summary>
 	/// Reset all view settings to default.
 	/// </summary>

+ 5 - 0
bindings/d/impl.d

@@ -84,6 +84,11 @@ extern(C++, "bgfx") package final abstract class ViewMode{
 		default_,sequential,depthAscending,depthDescending,count
 	}
 }
+extern(C++, "bgfx") package final abstract class ShadingRate{
+	enum Enum{
+		rate1x1,rate1x2,rate2x1,rate2x2,rate2x4,rate4x2,rate4x4,count
+	}
+}
 extern(C++, "bgfx") package final abstract class NativeWindowHandleType{
 	enum Enum{
 		default_,wayland,count

+ 27 - 5
bindings/d/package.d

@@ -9,7 +9,7 @@ import bindbc.common.types: c_int64, c_uint64, va_list;
 import bindbc.bgfx.config;
 static import bgfx.impl;
 
-enum uint apiVersion = 132;
+enum uint apiVersion = 133;
 
 alias ViewID = ushort;
 
@@ -518,10 +518,11 @@ enum CapFlags: CapFlags_{
 	texture2DArray          = 0x0000_0000_0100_0000, ///2D texture array is supported.
 	texture3D               = 0x0000_0000_0200_0000, ///3D textures are supported.
 	transparentBackbuffer   = 0x0000_0000_0400_0000, ///Transparent back buffer supported.
-	vertexAttribHalf        = 0x0000_0000_0800_0000, ///Vertex attribute half-float is supported.
-	vertexAttribUint10      = 0x0000_0000_1000_0000, ///Vertex attribute 10_10_10_2 is supported.
-	vertexID                = 0x0000_0000_2000_0000, ///Rendering with VertexID only is supported.
-	viewportLayerArray      = 0x0000_0000_4000_0000, ///Viewport layer is available in vertex shader.
+	variableRateShading     = 0x0000_0000_0800_0000, ///Variable Rate Shading
+	vertexAttribHalf        = 0x0000_0000_1000_0000, ///Vertex attribute half-float is supported.
+	vertexAttribUint10      = 0x0000_0000_2000_0000, ///Vertex attribute 10_10_10_2 is supported.
+	vertexID                = 0x0000_0000_4000_0000, ///Rendering with VertexID only is supported.
+	viewportLayerArray      = 0x0000_0000_8000_0000, ///Viewport layer is available in vertex shader.
 	textureCompareAll       = 0x0000_0000_0018_0000, ///All texture compare modes are supported.
 }
 
@@ -835,6 +836,18 @@ enum ViewMode: bgfx.impl.ViewMode.Enum{
 	count = bgfx.impl.ViewMode.Enum.count,
 }
 
+///Shading Rate.
+enum ShadingRate: bgfx.impl.ShadingRate.Enum{
+	rate1x1 = bgfx.impl.ShadingRate.Enum.rate1x1,
+	rate1x2 = bgfx.impl.ShadingRate.Enum.rate1x2,
+	rate2x1 = bgfx.impl.ShadingRate.Enum.rate2x1,
+	rate2x2 = bgfx.impl.ShadingRate.Enum.rate2x2,
+	rate2x4 = bgfx.impl.ShadingRate.Enum.rate2x4,
+	rate4x2 = bgfx.impl.ShadingRate.Enum.rate4x2,
+	rate4x4 = bgfx.impl.ShadingRate.Enum.rate4x4,
+	count = bgfx.impl.ShadingRate.Enum.count,
+}
+
 ///Native window handle type.
 enum NativeWindowHandleType: bgfx.impl.NativeWindowHandleType.Enum{
 	default_ = bgfx.impl.NativeWindowHandleType.Enum.default_,
@@ -3024,6 +3037,15 @@ mixin(joinFnBinds((){
 		*/
 		{q{void}, q{setViewOrder}, q{ViewID id=0, ushort num=ushort.max, const(ViewID)* order=null}, ext: `C++, "bgfx"`},
 		
+		/**
+		* Set view shading rate.
+		* Attention: Availability depends on: `BGFX_CAPS_VARIABLE_RATE_SHADING`.
+		Params:
+			id = View id.
+			shadingRate = Shading rate.
+		*/
+		{q{void}, q{setViewShadingRate}, q{ViewID id, bgfx.impl.ShadingRate.Enum shadingRate=ShadingRate.rate1x1}, ext: `C++, "bgfx"`},
+		
 		/**
 		* Reset all view settings to default.
 		*/

+ 28 - 4
bindings/zig/bgfx.zig

@@ -705,17 +705,20 @@ pub const CapsFlags_Texture3D: CapsFlags              = 0x0000000002000000;
 /// Transparent back buffer supported.
 pub const CapsFlags_TransparentBackbuffer: CapsFlags  = 0x0000000004000000;
 
+/// Variable Rate Shading
+pub const CapsFlags_VariableRateShading: CapsFlags    = 0x0000000008000000;
+
 /// Vertex attribute half-float is supported.
-pub const CapsFlags_VertexAttribHalf: CapsFlags       = 0x0000000008000000;
+pub const CapsFlags_VertexAttribHalf: CapsFlags       = 0x0000000010000000;
 
 /// Vertex attribute 10_10_10_2 is supported.
-pub const CapsFlags_VertexAttribUint10: CapsFlags     = 0x0000000010000000;
+pub const CapsFlags_VertexAttribUint10: CapsFlags     = 0x0000000020000000;
 
 /// Rendering with VertexID only is supported.
-pub const CapsFlags_VertexId: CapsFlags               = 0x0000000020000000;
+pub const CapsFlags_VertexId: CapsFlags               = 0x0000000040000000;
 
 /// Viewport layer is available in vertex shader.
-pub const CapsFlags_ViewportLayerArray: CapsFlags     = 0x0000000040000000;
+pub const CapsFlags_ViewportLayerArray: CapsFlags     = 0x0000000080000000;
 
 /// All texture compare modes are supported.
 pub const CapsFlags_TextureCompareAll: CapsFlags      = 0x0000000000180000;
@@ -1254,6 +1257,18 @@ pub const ViewMode = enum(c_int) {
     Count
 };
 
+pub const ShadingRate = enum(c_int) {
+    Rate1x1,
+    Rate1x2,
+    Rate2x1,
+    Rate2x2,
+    Rate2x4,
+    Rate4x2,
+    Rate4x4,
+
+    Count
+};
+
 pub const NativeWindowHandleType = enum(c_int) {
     /// Platform default handle type (X11 on Linux).
     Default,
@@ -2927,6 +2942,15 @@ pub inline fn setViewOrder(_id: ViewId, _num: u16, _order: [*c]const ViewId) voi
 }
 extern fn bgfx_set_view_order(_id: ViewId, _num: u16, _order: [*c]const ViewId) void;
 
+/// Set view shading rate.
+/// @attention Availability depends on: `BGFX_CAPS_VARIABLE_RATE_SHADING`.
+/// <param name="_id">View id.</param>
+/// <param name="_shadingRate">Shading rate.</param>
+pub inline fn setViewShadingRate(_id: ViewId, _shadingRate: ShadingRate) void {
+    return bgfx_set_view_shading_rate(_id, _shadingRate);
+}
+extern fn bgfx_set_view_shading_rate(_id: ViewId, _shadingRate: ShadingRate) void;
+
 /// Reset all view settings to default.
 pub inline fn resetView(_id: ViewId) void {
     return bgfx_reset_view(_id);

+ 32 - 3
examples/09-hdr/hdr.cpp

@@ -341,11 +341,11 @@ public:
 			showExampleDialog(this);
 
 			ImGui::SetNextWindowPos(
-				  ImVec2(m_width - m_width / 5.0f - 10.0f, 10.0f)
+				  ImVec2(m_width - m_width / 5.0f - 40.0f, 10.0f)
 				, ImGuiCond_FirstUseEver
 				);
 			ImGui::SetNextWindowSize(
-				  ImVec2(m_width / 5.0f, m_height / 2.0f)
+				  ImVec2(m_width / 5.0f + 30.0f, m_height / 2.0f)
 				, ImGuiCond_FirstUseEver
 				);
 			ImGui::Begin("Settings"
@@ -369,6 +369,26 @@ public:
 				ImGui::SliderFloat("Lum Avg", &lumAvg, 0.0f, 1.0f);
 			}
 
+			const bgfx::Caps* caps = bgfx::getCaps();
+
+			static const char* s_shadingRateName[] =
+			{
+				"1x1",
+				"1x2",
+				"2x1",
+				"2x2",
+				"2x4",
+				"4x2",
+				"4x4",
+			};
+			static_assert(bgfx::ShadingRate::Count == BX_COUNTOF(s_shadingRateName) );
+
+			static bgfx::ShadingRate::Enum shadingRate = bgfx::ShadingRate::Rate1x1;
+
+			ImGui::BeginDisabled(0 == (caps->supported & BGFX_CAPS_VARIABLE_RATE_SHADING) );
+			ImGui::Combo("Shading Rate", (int*)&shadingRate, s_shadingRateName, BX_COUNTOF(s_shadingRateName) );
+			ImGui::EndDisabled();
+
 			ImGui::End();
 
 			imguiEndFrame();
@@ -404,46 +424,55 @@ public:
 			bgfx::setViewClear(hdrSkybox, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x303030ff, 1.0f, 0);
 			bgfx::setViewRect(hdrSkybox, 0, 0, bgfx::BackbufferRatio::Equal);
 			bgfx::setViewFrameBuffer(hdrSkybox, m_fbh);
+			bgfx::setViewShadingRate(hdrSkybox, shadingRate);
 
 			bgfx::setViewName(hdrMesh, "Mesh");
 			bgfx::setViewClear(hdrMesh, BGFX_CLEAR_DISCARD_DEPTH | BGFX_CLEAR_DISCARD_STENCIL);
 			bgfx::setViewRect(hdrMesh, 0, 0, bgfx::BackbufferRatio::Equal);
 			bgfx::setViewFrameBuffer(hdrMesh, m_fbh);
+			bgfx::setViewShadingRate(hdrMesh, shadingRate);
 
 			bgfx::setViewName(hdrLuminance, "Luminance");
 			bgfx::setViewRect(hdrLuminance, 0, 0, 128, 128);
 			bgfx::setViewFrameBuffer(hdrLuminance, m_lum[0]);
+			bgfx::setViewShadingRate(hdrLuminance, bgfx::ShadingRate::Rate1x1);
 
 			bgfx::setViewName(hdrLumScale0, "Downscale luminance 0");
 			bgfx::setViewRect(hdrLumScale0, 0, 0, 64, 64);
 			bgfx::setViewFrameBuffer(hdrLumScale0, m_lum[1]);
+			bgfx::setViewShadingRate(hdrLumScale0, bgfx::ShadingRate::Rate1x1);
 
 			bgfx::setViewName(hdrLumScale1, "Downscale luminance 1");
 			bgfx::setViewRect(hdrLumScale1, 0, 0, 16, 16);
 			bgfx::setViewFrameBuffer(hdrLumScale1, m_lum[2]);
+			bgfx::setViewShadingRate(hdrLumScale1, bgfx::ShadingRate::Rate1x1);
 
 			bgfx::setViewName(hdrLumScale2, "Downscale luminance 2");
 			bgfx::setViewRect(hdrLumScale2, 0, 0, 4, 4);
 			bgfx::setViewFrameBuffer(hdrLumScale2, m_lum[3]);
+			bgfx::setViewShadingRate(hdrLumScale2, bgfx::ShadingRate::Rate1x1);
 
 			bgfx::setViewName(hdrLumScale3, "Downscale luminance 3");
 			bgfx::setViewRect(hdrLumScale3, 0, 0, 1, 1);
 			bgfx::setViewFrameBuffer(hdrLumScale3, m_lum[4]);
+			bgfx::setViewShadingRate(hdrLumScale3, bgfx::ShadingRate::Rate1x1);
 
 			bgfx::setViewName(hdrBrightness, "Brightness");
 			bgfx::setViewRect(hdrBrightness, 0, 0, bgfx::BackbufferRatio::Half);
 			bgfx::setViewFrameBuffer(hdrBrightness, m_bright);
+			bgfx::setViewShadingRate(hdrBrightness, bgfx::ShadingRate::Rate1x1);
 
 			bgfx::setViewName(hdrVBlur, "Blur vertical");
 			bgfx::setViewRect(hdrVBlur, 0, 0, bgfx::BackbufferRatio::Eighth);
 			bgfx::setViewFrameBuffer(hdrVBlur, m_blur);
+			bgfx::setViewShadingRate(hdrVBlur, bgfx::ShadingRate::Rate1x1);
 
 			bgfx::setViewName(hdrHBlurTonemap, "Blur horizontal + tonemap");
 			bgfx::setViewRect(hdrHBlurTonemap, 0, 0, bgfx::BackbufferRatio::Equal);
 			bgfx::FrameBufferHandle invalid = BGFX_INVALID_HANDLE;
 			bgfx::setViewFrameBuffer(hdrHBlurTonemap, invalid);
+			bgfx::setViewShadingRate(hdrHBlurTonemap, bgfx::ShadingRate::Rate1x1);
 
-			const bgfx::Caps* caps = bgfx::getCaps();
 			float proj[16];
 			bx::mtxOrtho(proj, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 100.0f, 0.0f, caps->homogeneousDepth);
 

+ 21 - 2
examples/18-ibl/ibl.cpp

@@ -658,6 +658,26 @@ public:
 				ImGui::ColorWheel("Specular:", &m_settings.m_rgbSpec[0], 0.7f);
 			}
 
+			const bgfx::Caps* caps = bgfx::getCaps();
+
+			static const char* s_shadingRateName[] =
+			{
+				"1x1",
+				"1x2",
+				"2x1",
+				"2x2",
+				"2x4",
+				"4x2",
+				"4x4",
+			};
+			static_assert(bgfx::ShadingRate::Count == BX_COUNTOF(s_shadingRateName) );
+
+			static bgfx::ShadingRate::Enum shadingRate = bgfx::ShadingRate::Rate1x1;
+
+			ImGui::BeginDisabled(0 == (caps->supported & BGFX_CAPS_VARIABLE_RATE_SHADING) );
+			ImGui::Combo("Shading Rate", (int*)&shadingRate, s_shadingRateName, BX_COUNTOF(s_shadingRateName) );
+			ImGui::EndDisabled();
+
 			ImGui::End();
 
 			imguiEndFrame();
@@ -712,8 +732,6 @@ public:
 			float view[16];
 			bx::mtxIdentity(view);
 
-			const bgfx::Caps* caps = bgfx::getCaps();
-
 			float proj[16];
 			bx::mtxOrtho(proj, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 100.0f, 0.0, caps->homogeneousDepth);
 			bgfx::setViewTransform(0, view, proj);
@@ -722,6 +740,7 @@ public:
 			m_camera.mtxLookAt(view);
 			bx::mtxProj(proj, 45.0f, float(m_width)/float(m_height), 0.1f, 100.0f, caps->homogeneousDepth);
 			bgfx::setViewTransform(1, view, proj);
+			bgfx::setViewShadingRate(1, shadingRate);
 
 			// View rect.
 			bgfx::setViewRect(0, 0, 0, uint16_t(m_width), uint16_t(m_height) );

+ 33 - 1
include/bgfx/bgfx.h

@@ -403,11 +403,31 @@ namespace bgfx
 		};
 	};
 
+	/// Shading Rate.
+	///
+	/// @attention C99's equivalent binding is `bgfx_bgfx_shading_rate_t`.
+	///
+	struct ShadingRate
+	{
+		/// Shading rate:
+		enum Enum
+		{
+			Rate1x1,
+			Rate1x2,
+			Rate2x1,
+			Rate2x2,
+			Rate2x4,
+			Rate4x2,
+			Rate4x4,
+
+			Count
+		};
+	};
+
 	/// Native window handle type.
 	///
 	/// @attention C99's equivalent binding is `bgfx_native_window_handle_type_t`.
 	///
-
 	struct NativeWindowHandleType
 	{
 		enum Enum
@@ -3547,6 +3567,18 @@ namespace bgfx
 		, const ViewId* _remap = NULL
 		);
 
+	/// Set view shading rate.
+	///
+	/// @param[in] _id View id.
+	/// @param[in] _rate
+	///
+	/// @attention C99's equivalent binding is `bgfx_set_view_shading_rate`.
+	///
+	void setViewShadingRate(
+		  ViewId _id
+		, ShadingRate::Enum _shadingRate
+		);
+
 	/// Reset all view settings to default.
 	///
 	/// @param[in] _id View id.

+ 30 - 0
include/bgfx/c99/bgfx.h

@@ -389,6 +389,24 @@ typedef enum bgfx_view_mode
 
 } bgfx_view_mode_t;
 
+/**
+ * Shading Rate.
+ *
+ */
+typedef enum bgfx_shading_rate
+{
+    BGFX_SHADING_RATE_RATE_1X_1,              /** ( 0)                                */
+    BGFX_SHADING_RATE_RATE_1X_2,              /** ( 1)                                */
+    BGFX_SHADING_RATE_RATE_2X_1,              /** ( 2)                                */
+    BGFX_SHADING_RATE_RATE_2X_2,              /** ( 3)                                */
+    BGFX_SHADING_RATE_RATE_2X_4,              /** ( 4)                                */
+    BGFX_SHADING_RATE_RATE_4X_2,              /** ( 5)                                */
+    BGFX_SHADING_RATE_RATE_4X_4,              /** ( 6)                                */
+
+    BGFX_SHADING_RATE_COUNT
+
+} bgfx_shading_rate_t;
+
 /**
  * Native window handle type.
  *
@@ -2391,6 +2409,16 @@ BGFX_C_API void bgfx_set_view_transform(bgfx_view_id_t _id, const void* _view, c
  */
 BGFX_C_API void bgfx_set_view_order(bgfx_view_id_t _id, uint16_t _num, const bgfx_view_id_t* _order);
 
+/**
+ * Set view shading rate.
+ * @attention Availability depends on: `BGFX_CAPS_VARIABLE_RATE_SHADING`.
+ *
+ * @param[in] _id View id.
+ * @param[in] _shadingRate Shading rate.
+ *
+ */
+BGFX_C_API void bgfx_set_view_shading_rate(bgfx_view_id_t _id, bgfx_shading_rate_t _shadingRate);
+
 /**
  * Reset all view settings to default.
  *
@@ -3594,6 +3622,7 @@ typedef enum bgfx_function_id
     BGFX_FUNCTION_ID_SET_VIEW_FRAME_BUFFER,
     BGFX_FUNCTION_ID_SET_VIEW_TRANSFORM,
     BGFX_FUNCTION_ID_SET_VIEW_ORDER,
+    BGFX_FUNCTION_ID_SET_VIEW_SHADING_RATE,
     BGFX_FUNCTION_ID_RESET_VIEW,
     BGFX_FUNCTION_ID_ENCODER_BEGIN,
     BGFX_FUNCTION_ID_ENCODER_END,
@@ -3800,6 +3829,7 @@ struct bgfx_interface_vtbl
     void (*set_view_frame_buffer)(bgfx_view_id_t _id, bgfx_frame_buffer_handle_t _handle);
     void (*set_view_transform)(bgfx_view_id_t _id, const void* _view, const void* _proj);
     void (*set_view_order)(bgfx_view_id_t _id, uint16_t _num, const bgfx_view_id_t* _order);
+    void (*set_view_shading_rate)(bgfx_view_id_t _id, bgfx_shading_rate_t _shadingRate);
     void (*reset_view)(bgfx_view_id_t _id);
     bgfx_encoder_t* (*encoder_begin)(bool _forThread);
     void (*encoder_end)(bgfx_encoder_t* _encoder);

+ 6 - 5
include/bgfx/defines.h

@@ -15,7 +15,7 @@
 #ifndef BGFX_DEFINES_H_HEADER_GUARD
 #define BGFX_DEFINES_H_HEADER_GUARD
 
-#define BGFX_API_VERSION UINT32_C(132)
+#define BGFX_API_VERSION UINT32_C(133)
 
 /**
  * Color RGB/alpha/depth write. When it's not specified write will be disabled.
@@ -489,10 +489,11 @@
 #define BGFX_CAPS_TEXTURE_2D_ARRAY                UINT64_C(0x0000000001000000) //!< 2D texture array is supported.
 #define BGFX_CAPS_TEXTURE_3D                      UINT64_C(0x0000000002000000) //!< 3D textures are supported.
 #define BGFX_CAPS_TRANSPARENT_BACKBUFFER          UINT64_C(0x0000000004000000) //!< Transparent back buffer supported.
-#define BGFX_CAPS_VERTEX_ATTRIB_HALF              UINT64_C(0x0000000008000000) //!< Vertex attribute half-float is supported.
-#define BGFX_CAPS_VERTEX_ATTRIB_UINT10            UINT64_C(0x0000000010000000) //!< Vertex attribute 10_10_10_2 is supported.
-#define BGFX_CAPS_VERTEX_ID                       UINT64_C(0x0000000020000000) //!< Rendering with VertexID only is supported.
-#define BGFX_CAPS_VIEWPORT_LAYER_ARRAY            UINT64_C(0x0000000040000000) //!< Viewport layer is available in vertex shader.
+#define BGFX_CAPS_VARIABLE_RATE_SHADING           UINT64_C(0x0000000008000000) //!< Variable Rate Shading
+#define BGFX_CAPS_VERTEX_ATTRIB_HALF              UINT64_C(0x0000000010000000) //!< Vertex attribute half-float is supported.
+#define BGFX_CAPS_VERTEX_ATTRIB_UINT10            UINT64_C(0x0000000020000000) //!< Vertex attribute 10_10_10_2 is supported.
+#define BGFX_CAPS_VERTEX_ID                       UINT64_C(0x0000000040000000) //!< Rendering with VertexID only is supported.
+#define BGFX_CAPS_VIEWPORT_LAYER_ARRAY            UINT64_C(0x0000000080000000) //!< Viewport layer is available in vertex shader.
 /// All texture compare modes are supported.
 #define BGFX_CAPS_TEXTURE_COMPARE_ALL (0 \
 	| BGFX_CAPS_TEXTURE_COMPARE_RESERVED \

+ 23 - 1
scripts/bgfx.idl

@@ -1,7 +1,7 @@
 -- vim: syntax=lua
 -- bgfx interface
 
-version(132)
+version(133)
 
 typedef "bool"
 typedef "char"
@@ -392,6 +392,7 @@ flag.Caps { bits = 64, base = 1, name = "Caps" }
 	.Texture_2dArray        --- 2D texture array is supported.
 	.Texture_3d             --- 3D textures are supported.
 	.TransparentBackbuffer  --- Transparent back buffer supported.
+	.VariableRateShading    --- Variable Rate Shading
 	.VertexAttribHalf       --- Vertex attribute half-float is supported.
 	.VertexAttribUint10     --- Vertex attribute 10_10_10_2 is supported.
 	.VertexId               --- Rendering with VertexID only is supported.
@@ -689,6 +690,17 @@ enum.ViewMode { underscore, comment = "View modes:" }
 	.DepthDescending --- Sort draw call depth in descending order.
 	()
 
+--- Shading Rate.
+enum.ShadingRate { underscore, comment = "Shading rate:" }
+	.Rate1x1
+	.Rate1x2
+	.Rate2x1
+	.Rate2x2
+	.Rate2x4
+	.Rate4x2
+	.Rate4x4
+	()
+
 --- Native window handle type.
 enum.NativeWindowHandleType { underscore, comment = "Native Window handle type:" }
 	.Default         --- Platform default handle type (X11 on Linux).
@@ -2150,6 +2162,16 @@ func.setViewOrder
 	                       --- to default state.
 	 { default = NULL }
 
+--- Set view shading rate.
+---
+--- @attention Availability depends on: `BGFX_CAPS_VARIABLE_RATE_SHADING`.
+---
+func.setViewShadingRate
+	"void"
+	.id          "ViewId"            --- View id.
+	.shadingRate "ShadingRate::Enum" --- Shading rate.
+	 { default = "ShadingRate::Rate1x1" }
+
 --- Reset all view settings to default.
 func.resetView
 	"void"

+ 1 - 1
scripts/genie.lua

@@ -65,7 +65,7 @@ newaction {
 		end
 
 		generate("temp.bgfx.h" ,      "../include/bgfx/c99/bgfx.h", "    ")
---		generate("temp.bgfx.hpp" ,    "../include/bgfx/bgfx.h",     "\t")
+--		generate("temp.bgfx.hpp" ,    "../include/bgfx/bgfx.hpp",   "\t")
 		generate("temp.bgfx.idl.inl", "../src/bgfx.idl.inl",        "\t")
 		generate("temp.defines.h",    "../include/bgfx/defines.h",  "\t")
 

+ 9 - 2
src/bgfx.cpp

@@ -1525,21 +1525,22 @@ namespace bgfx
 		CAPS_FLAGS(BGFX_CAPS_INDEX32),
 		CAPS_FLAGS(BGFX_CAPS_INSTANCING),
 		CAPS_FLAGS(BGFX_CAPS_OCCLUSION_QUERY),
+		CAPS_FLAGS(BGFX_CAPS_PRIMITIVE_ID),
 		CAPS_FLAGS(BGFX_CAPS_RENDERER_MULTITHREADED),
 		CAPS_FLAGS(BGFX_CAPS_SWAP_CHAIN),
 		CAPS_FLAGS(BGFX_CAPS_TEXTURE_2D_ARRAY),
 		CAPS_FLAGS(BGFX_CAPS_TEXTURE_3D),
 		CAPS_FLAGS(BGFX_CAPS_TEXTURE_BLIT),
-		CAPS_FLAGS(BGFX_CAPS_TRANSPARENT_BACKBUFFER),
 		CAPS_FLAGS(BGFX_CAPS_TEXTURE_COMPARE_ALL),
 		CAPS_FLAGS(BGFX_CAPS_TEXTURE_COMPARE_LEQUAL),
 		CAPS_FLAGS(BGFX_CAPS_TEXTURE_CUBE_ARRAY),
 		CAPS_FLAGS(BGFX_CAPS_TEXTURE_DIRECT_ACCESS),
 		CAPS_FLAGS(BGFX_CAPS_TEXTURE_READ_BACK),
+		CAPS_FLAGS(BGFX_CAPS_TRANSPARENT_BACKBUFFER),
+		CAPS_FLAGS(BGFX_CAPS_VARIABLE_RATE_SHADING),
 		CAPS_FLAGS(BGFX_CAPS_VERTEX_ATTRIB_HALF),
 		CAPS_FLAGS(BGFX_CAPS_VERTEX_ATTRIB_UINT10),
 		CAPS_FLAGS(BGFX_CAPS_VERTEX_ID),
-		CAPS_FLAGS(BGFX_CAPS_PRIMITIVE_ID),
 		CAPS_FLAGS(BGFX_CAPS_VIEWPORT_LAYER_ARRAY),
 #undef CAPS_FLAGS
 	};
@@ -5329,6 +5330,12 @@ namespace bgfx
 		s_ctx->setViewOrder(_id, _num, _order);
 	}
 
+	void setViewShadingRate(ViewId _id, ShadingRate::Enum _shadingRate)
+	{
+		BX_ASSERT(checkView(_id), "Invalid view id: %d", _id);
+		s_ctx->setViewShadingRate(_id, _shadingRate);
+	}
+
 	void resetView(ViewId _id)
 	{
 		BX_ASSERT(checkView(_id), "Invalid view id: %d", _id);

+ 6 - 0
src/bgfx.idl.inl

@@ -699,6 +699,11 @@ BGFX_C_API void bgfx_set_view_order(bgfx_view_id_t _id, uint16_t _num, const bgf
 	bgfx::setViewOrder((bgfx::ViewId)_id, _num, (const bgfx::ViewId*)_order);
 }
 
+BGFX_C_API void bgfx_set_view_shading_rate(bgfx_view_id_t _id, bgfx_shading_rate_t _shadingRate)
+{
+	bgfx::setViewShadingRate((bgfx::ViewId)_id, (bgfx::ShadingRate::Enum)_shadingRate);
+}
+
 BGFX_C_API void bgfx_reset_view(bgfx_view_id_t _id)
 {
 	bgfx::resetView((bgfx::ViewId)_id);
@@ -1405,6 +1410,7 @@ BGFX_C_API bgfx_interface_vtbl_t* bgfx_get_interface(uint32_t _version)
 			bgfx_set_view_frame_buffer,
 			bgfx_set_view_transform,
 			bgfx_set_view_order,
+			bgfx_set_view_shading_rate,
 			bgfx_reset_view,
 			bgfx_encoder_begin,
 			bgfx_encoder_end,

+ 17 - 5
src/bgfx_p.h

@@ -2058,6 +2058,7 @@ namespace bgfx
 			setScissor(0, 0, 0, 0);
 			setClear(BGFX_CLEAR_NONE, 0, 0.0f, 0);
 			setMode(ViewMode::Default);
+			setShadingRate(ShadingRate::Rate1x1);
 			setFrameBuffer(BGFX_INVALID_HANDLE);
 			setTransform(NULL, NULL);
 		}
@@ -2093,6 +2094,11 @@ namespace bgfx
 			m_mode = uint8_t(_mode);
 		}
 
+		void setShadingRate(ShadingRate::Enum _shadingRate)
+		{
+			m_shadingRate = uint8_t(_shadingRate);
+		}
+
 		void setFrameBuffer(FrameBufferHandle _handle)
 		{
 			m_fbh = _handle;
@@ -2126,6 +2132,7 @@ namespace bgfx
 		Matrix4 m_proj;
 		FrameBufferHandle m_fbh;
 		uint8_t m_mode;
+		uint8_t m_shadingRate;
 	};
 
 	struct FrameCache
@@ -5160,11 +5167,6 @@ namespace bgfx
 			m_view[_id].setTransform(_view, _proj);
 		}
 
-		BGFX_API_FUNC(void resetView(ViewId _id) )
-		{
-			m_view[_id].reset();
-		}
-
 		BGFX_API_FUNC(void setViewOrder(ViewId _id, uint16_t _num, const ViewId* _order) )
 		{
 			const uint32_t num = bx::min(_id + _num, BGFX_CONFIG_MAX_VIEWS) - _id;
@@ -5182,6 +5184,16 @@ namespace bgfx
 			}
 		}
 
+		BGFX_API_FUNC(void setViewShadingRate(ViewId _id, ShadingRate::Enum _shadingRate) )
+		{
+			m_view[_id].setShadingRate(_shadingRate);
+		}
+
+		BGFX_API_FUNC(void resetView(ViewId _id) )
+		{
+			m_view[_id].reset();
+		}
+
 		BGFX_API_FUNC(Encoder* begin(bool _forThread) );
 
 		BGFX_API_FUNC(void end(Encoder* _encoder) );

+ 47 - 22
src/renderer_d3d12.cpp

@@ -129,6 +129,18 @@ namespace bgfx { namespace d3d12
 		D3D12_CULL_MODE_BACK,
 	};
 
+	static const D3D12_SHADING_RATE s_shadingRate[] =
+	{
+		D3D12_SHADING_RATE_1X1,
+		D3D12_SHADING_RATE_1X2,
+		D3D12_SHADING_RATE_2X1,
+		D3D12_SHADING_RATE_2X2,
+		D3D12_SHADING_RATE_2X4,
+		D3D12_SHADING_RATE_4X2,
+		D3D12_SHADING_RATE_4X4,
+	};
+	static_assert(ShadingRate::Count == BX_COUNTOF(s_shadingRate) );
+
 	static const D3D12_TEXTURE_ADDRESS_MODE s_textureAddress[] =
 	{
 		D3D12_TEXTURE_ADDRESS_MODE_WRAP,
@@ -714,6 +726,7 @@ namespace bgfx { namespace d3d12
 			, m_backBufferColorIdx(0)
 			, m_rtMsaa(false)
 			, m_directAccessSupport(false)
+			, m_variableRateShadingSupport(false)
 		{
 		}
 
@@ -1078,6 +1091,9 @@ namespace bgfx { namespace d3d12
 				BX_TRACE("\tAdditionalShadingRatesSupported %d", options6.AdditionalShadingRatesSupported);
 				BX_TRACE("\tPerPrimitiveShadingRateSupportedWithViewportIndexing %d", options6.PerPrimitiveShadingRateSupportedWithViewportIndexing);
 				BX_TRACE("\tVariableShadingRateTier %d", options6.VariableShadingRateTier);
+
+				m_variableRateShadingSupport = D3D12_VARIABLE_SHADING_RATE_TIER_NOT_SUPPORTED != options6.VariableShadingRateTier;
+
 				BX_TRACE("\tShadingRateImageTileSize %d", options6.ShadingRateImageTileSize);
 				BX_TRACE("\tBackgroundProcessingSupported %d", options6.BackgroundProcessingSupported);
 				break;
@@ -1440,30 +1456,31 @@ namespace bgfx { namespace d3d12
 					;
 
 				g_caps.supported |= ( 0
-					| BGFX_CAPS_TEXTURE_3D
-					| BGFX_CAPS_TEXTURE_COMPARE_ALL
-					| BGFX_CAPS_INDEX32
-					| BGFX_CAPS_INSTANCING
-					| BGFX_CAPS_DRAW_INDIRECT
-					| BGFX_CAPS_VERTEX_ATTRIB_HALF
-					| BGFX_CAPS_VERTEX_ATTRIB_UINT10
-					| BGFX_CAPS_VERTEX_ID
-					| BGFX_CAPS_FRAGMENT_DEPTH
+					| BGFX_CAPS_ALPHA_TO_COVERAGE
 					| BGFX_CAPS_BLEND_INDEPENDENT
 					| BGFX_CAPS_COMPUTE
-					| (m_options.ROVsSupported ? BGFX_CAPS_FRAGMENT_ORDERING     : 0)
-					| (m_directAccessSupport   ? BGFX_CAPS_TEXTURE_DIRECT_ACCESS : 0)
-					| (BX_ENABLED(BX_PLATFORM_WINDOWS) ? BGFX_CAPS_SWAP_CHAIN : 0)
-					| BGFX_CAPS_TEXTURE_BLIT
-					| BGFX_CAPS_TEXTURE_READ_BACK
+					| BGFX_CAPS_DRAW_INDIRECT
+					| BGFX_CAPS_DRAW_INDIRECT_COUNT
+					| BGFX_CAPS_FRAGMENT_DEPTH
+					| (m_options.ROVsSupported ? BGFX_CAPS_FRAGMENT_ORDERING : 0)
+					| BGFX_CAPS_IMAGE_RW
+					| BGFX_CAPS_INDEX32
+					| BGFX_CAPS_INSTANCING
 					| BGFX_CAPS_OCCLUSION_QUERY
-					| BGFX_CAPS_ALPHA_TO_COVERAGE
+					| BGFX_CAPS_PRIMITIVE_ID
+					| (BX_ENABLED(BX_PLATFORM_WINDOWS) ? BGFX_CAPS_SWAP_CHAIN : 0)
 					| BGFX_CAPS_TEXTURE_2D_ARRAY
+					| BGFX_CAPS_TEXTURE_3D
+					| BGFX_CAPS_TEXTURE_BLIT
+					| BGFX_CAPS_TEXTURE_COMPARE_ALL
 					| BGFX_CAPS_TEXTURE_CUBE_ARRAY
-					| BGFX_CAPS_IMAGE_RW
+					| (m_directAccessSupport ? BGFX_CAPS_TEXTURE_DIRECT_ACCESS : 0)
+					| BGFX_CAPS_TEXTURE_READ_BACK
+					| (m_variableRateShadingSupport ? BGFX_CAPS_VARIABLE_RATE_SHADING : 0)
+					| BGFX_CAPS_VERTEX_ATTRIB_HALF
+					| BGFX_CAPS_VERTEX_ATTRIB_UINT10
+					| BGFX_CAPS_VERTEX_ID
 					| BGFX_CAPS_VIEWPORT_LAYER_ARRAY
-					| BGFX_CAPS_DRAW_INDIRECT_COUNT
-					| BGFX_CAPS_PRIMITIVE_ID
 					);
 				g_caps.limits.maxTextureSize     = D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION;
 				g_caps.limits.maxTextureLayers   = D3D12_REQ_TEXTURE2D_ARRAY_AXIS_DIMENSION;
@@ -3748,6 +3765,7 @@ namespace bgfx { namespace d3d12
 		uint32_t m_backBufferColorIdx;
 		bool m_rtMsaa;
 		bool m_directAccessSupport;
+		bool m_variableRateShadingSupport;
 	};
 
 	static RendererContextD3D12* s_renderD3D12;
@@ -6634,12 +6652,14 @@ namespace bgfx { namespace d3d12
 
 					profiler.begin(view);
 
+					const View& renderView = _render->m_view[view];
+
 					fbh = _render->m_view[view].m_fbh;
 					setFrameBuffer(fbh);
 
-					viewState.m_rect = _render->m_view[view].m_rect;
-					const Rect& rect        = _render->m_view[view].m_rect;
-					const Rect& scissorRect = _render->m_view[view].m_scissor;
+					viewState.m_rect = renderView.m_rect;
+					const Rect& rect        = renderView.m_rect;
+					const Rect& scissorRect = renderView.m_scissor;
 					viewHasScissor  = !scissorRect.isZero();
 					viewScissorRect = viewHasScissor ? scissorRect : rect;
 
@@ -6660,7 +6680,7 @@ namespace bgfx { namespace d3d12
 					m_commandList->RSSetScissorRects(1, &rc);
 					restoreScissor = false;
 
-					Clear& clr = _render->m_view[view].m_clear;
+					const Clear& clr = renderView.m_clear;
 					if (BGFX_CLEAR_NONE != clr.m_flags)
 					{
 						Rect clearRect = rect;
@@ -6671,6 +6691,11 @@ namespace bgfx { namespace d3d12
 					prim = s_primInfo[Topology::Count]; // Force primitive type update.
 
 					submitBlit(bs, view);
+
+					if (m_variableRateShadingSupport)
+					{
+						reinterpret_cast<ID3D12GraphicsCommandList5*>(m_commandList)->RSSetShadingRate(s_shadingRate[renderView.m_shadingRate], NULL);
+					}
 				}
 
 				if (isCompute)

+ 33 - 0
src/renderer_mtl.h

@@ -540,6 +540,11 @@ namespace bgfx { namespace mtl
 			return false;
 		}
 
+		bool supportsVariableRasterizationRate()
+		{
+			return [m_obj supportsRasterizationRateMapWithLayerCount:1];
+		}
+
 		id<MTLLibrary> newLibraryWithData(const void* _data)
 		{
 			NSError* error;
@@ -670,6 +675,13 @@ namespace bgfx { namespace mtl
 			return state;
 		}
 
+		id<MTLRasterizationRateMap> newRasterizationRateMapWithDescriptor(MTLRasterizationRateMapDescriptor* _descriptor)
+		{
+			return [m_obj
+				newRasterizationRateMapWithDescriptor: _descriptor
+			];
+		}
+
 		bool supportsTextureSampleCount(int32_t sampleCount)
 		{
 			if (BX_ENABLED(BX_PLATFORM_IOS) && !iOSVersionEqualOrGreater("9.0.0") )
@@ -1107,6 +1119,27 @@ namespace bgfx { namespace mtl
 		return [MTLCaptureDescriptor new];
 	}
 
+	typedef MTLRasterizationRateMapDescriptor* RasterizationRateMapDescriptor;
+
+	typedef MTLRasterizationRateLayerDescriptor* RasterizationRateLayerDescriptor;
+
+	inline MTLRasterizationRateLayerDescriptor* newRasterizationRateLayerDescriptor(float _rate)
+	{
+		const float rate[1] = { _rate };
+		return [[MTLRasterizationRateLayerDescriptor alloc]
+			initWithSampleCount: MTLSizeMake(1, 1, 0)
+			horizontal: rate
+			vertical: rate
+		];
+	}
+
+	typedef MTLRasterizationRateMapDescriptor* RasterizationRateMapDescriptor;
+
+	inline MTLRasterizationRateMapDescriptor* newRasterizationRateMapDescriptor()
+	{
+		return [MTLRasterizationRateMapDescriptor new];
+	}
+
 	//helper functions
 	inline void release(NSObject* _obj)
 	{

+ 36 - 1
src/renderer_mtl.mm

@@ -344,6 +344,18 @@ BX_PRAGMA_DIAGNOSTIC_POP();
 		16,
 	};
 
+	static float s_shadingRate[] =
+	{
+		1.0f,
+		0.75f,
+		0.75f,
+		0.5f,
+		0.5f,
+		0.5f,
+		0.25f,
+	};
+	static_assert(ShadingRate::Count == BX_COUNTOF(s_shadingRate) );
+
 	static UniformType::Enum convertMtlType(MTLDataType _type)
 	{
 		switch (_type)
@@ -472,6 +484,7 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
 			, m_rtMsaa(false)
 			, m_capture(NULL)
 			, m_captureSize(0)
+			, m_variableRateShadingSupported(false)
 		{
 			bx::memSet(&m_windows, 0xff, sizeof(m_windows) );
 		}
@@ -576,6 +589,8 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
 			}
 #endif // BX_PLATFORM_OSX
 
+			m_variableRateShadingSupported = false; //m_device.supportsVariableRasterizationRate();
+
 			g_caps.numGPUs = 1;
 			g_caps.gpu[0].vendorId = g_caps.vendorId;
 			g_caps.gpu[0].deviceId = g_caps.deviceId;
@@ -621,6 +636,11 @@ static_assert(BX_COUNTOF(s_accessNames) == Access::Count, "Invalid s_accessNames
 			// It is decremented by 1 because 1 entry is used for uniforms.
 			g_caps.limits.maxComputeBindings = bx::uint32_min(30, BGFX_MAX_COMPUTE_BINDINGS);
 
+			g_caps.supported |= m_variableRateShadingSupported
+				? BGFX_CAPS_VARIABLE_RATE_SHADING
+				: 0
+				;
+
 			CHECK_FEATURE_AVAILABLE(
 				  m_hasPixelFormatDepth32Float_Stencil8
 				, iOS 9.0
@@ -2721,6 +2741,8 @@ BX_PRAGMA_DIAGNOSTIC_POP();
 		void* m_capture;
 		uint32_t m_captureSize;
 
+		bool m_variableRateShadingSupported;
+
 		// descriptors
 		RenderPipelineDescriptor m_renderPipelineDescriptor;
 		DepthStencilDescriptor   m_depthStencilDescriptor;
@@ -4323,7 +4345,7 @@ BX_PRAGMA_DIAGNOSTIC_POP();
 						viewScissorRect = viewHasScissor ? scissorRect : viewState.m_rect;
 						Clear& clr = _render->m_view[view].m_clear;
 
-						Rect viewRect = viewState.m_rect;
+						const Rect viewRect = viewState.m_rect;
 						bool clearWithRenderPass = false;
 
 						if (NULL == m_renderCommandEncoder
@@ -4475,6 +4497,19 @@ BX_PRAGMA_DIAGNOSTIC_POP();
 								}
 							}
 
+							if (m_variableRateShadingSupported)
+							{
+								RasterizationRateLayerDescriptor rrld = newRasterizationRateLayerDescriptor(s_shadingRate[_render->m_view[view].m_shadingRate]);
+								RasterizationRateMapDescriptor   rrmd = newRasterizationRateMapDescriptor();
+								rrmd.screenSize = MTLSizeMake(viewRect.m_width, viewRect.m_height, 0);
+								[rrmd
+									setLayer: rrld
+									atIndex: 0
+								];
+
+								renderPassDescriptor.rasterizationRateMap = m_device.newRasterizationRateMapWithDescriptor(rrmd);
+							}
+
 							rce = m_commandBuffer.renderCommandEncoderWithDescriptor(renderPassDescriptor);
 							m_renderCommandEncoder = rce;
 							m_renderCommandEncoderFrameBufferHandle = fbh;

+ 122 - 39
src/renderer_vk.cpp

@@ -57,6 +57,24 @@ namespace bgfx { namespace vk
 		{ 16, VK_SAMPLE_COUNT_16_BIT },
 	};
 
+	struct ShadingRateVk
+	{
+		VkExtent2D fragmentSize;
+		const VkExtent2D initFragmentSize;
+	};
+
+	static ShadingRateVk s_shadingRate[] =
+	{
+		{ { 1, 1 }, { 1, 1 } },
+		{ { 1, 2 }, { 1, 2 } },
+		{ { 2, 1 }, { 2, 1 } },
+		{ { 2, 2 }, { 2, 2 } },
+		{ { 2, 4 }, { 2, 4 } },
+		{ { 4, 2 }, { 4, 2 } },
+		{ { 4, 4 }, { 4, 4 } },
+	};
+	static_assert(ShadingRate::Count == BX_COUNTOF(s_shadingRate) );
+
 	static const VkBlendFactor s_blendFactor[][2] =
 	{
 		{ VkBlendFactor(0),                         VkBlendFactor(0)                         }, // ignored
@@ -360,6 +378,7 @@ VK_IMPORT_DEVICE
 			EXT_memory_budget,
 			EXT_shader_viewport_index_layer,
 			KHR_draw_indirect_count,
+			KHR_fragment_shading_rate,
 			KHR_get_physical_device_properties2,
 
 #	if BX_PLATFORM_ANDROID
@@ -399,6 +418,7 @@ VK_IMPORT_DEVICE
 		{ "VK_EXT_memory_budget",                   1, false, false, true,                                                          Layer::Count },
 		{ "VK_EXT_shader_viewport_index_layer",     1, false, false, true,                                                          Layer::Count },
 		{ "VK_KHR_draw_indirect_count",             1, false, false, true,                                                          Layer::Count },
+		{ "VK_KHR_fragment_shading_rate",           1, false, false, true,                                                          Layer::Count },
 		{ "VK_KHR_get_physical_device_properties2", 1, false, false, true,                                                          Layer::Count },
 #	if BX_PLATFORM_ANDROID
 		{ VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,    1, false, false, true,                                                          Layer::Count },
@@ -1144,6 +1164,7 @@ VK_IMPORT_DEVICE
 			, m_captureBuffer(VK_NULL_HANDLE)
 			, m_captureMemory()
 			, m_captureSize(0)
+			, m_variableRateShadingSupported(false)
 		{
 		}
 
@@ -1173,11 +1194,10 @@ VK_IMPORT_DEVICE
 			const bool headless = NULL == g_platformData.nwh;
 
 			const void* nextFeatures = NULL;
-			VkPhysicalDeviceLineRasterizationFeaturesEXT lineRasterizationFeatures;
-			VkPhysicalDeviceCustomBorderColorFeaturesEXT customBorderColorFeatures;
 
-			bx::memSet(&lineRasterizationFeatures, 0, sizeof(lineRasterizationFeatures) );
-			bx::memSet(&customBorderColorFeatures, 0, sizeof(customBorderColorFeatures) );
+			VkPhysicalDeviceLineRasterizationFeaturesEXT lineRasterizationFeatures = {};
+			VkPhysicalDeviceCustomBorderColorFeaturesEXT customBorderColorFeatures = {};
+			VkPhysicalDeviceFragmentShadingRateFeaturesKHR fragmentShadingRate = {};
 
 			m_fbh = BGFX_INVALID_HANDLE;
 			bx::memSet(m_uniforms, 0, sizeof(m_uniforms) );
@@ -1243,9 +1263,10 @@ VK_IMPORT
 
 				s_extension[Extension::EXT_debug_report].m_initialize = _init.debug;
 
-				s_extension[Extension::EXT_shader_viewport_index_layer].m_initialize = !!(_init.capabilities & BGFX_CAPS_VIEWPORT_LAYER_ARRAY);
-				s_extension[Extension::EXT_conservative_rasterization ].m_initialize = !!(_init.capabilities & BGFX_CAPS_CONSERVATIVE_RASTER );
-				s_extension[Extension::KHR_draw_indirect_count        ].m_initialize = !!(_init.capabilities & BGFX_CAPS_DRAW_INDIRECT_COUNT );
+				s_extension[Extension::EXT_conservative_rasterization ].m_initialize = !!(_init.capabilities & BGFX_CAPS_CONSERVATIVE_RASTER  );
+				s_extension[Extension::EXT_shader_viewport_index_layer].m_initialize = !!(_init.capabilities & BGFX_CAPS_VIEWPORT_LAYER_ARRAY );
+				s_extension[Extension::KHR_draw_indirect_count        ].m_initialize = !!(_init.capabilities & BGFX_CAPS_DRAW_INDIRECT_COUNT  );
+				s_extension[Extension::KHR_fragment_shading_rate      ].m_initialize = !!(_init.capabilities & BGFX_CAPS_VARIABLE_RATE_SHADING);
 
 				dumpExtensions(VK_NULL_HANDLE, s_extension);
 
@@ -1455,8 +1476,12 @@ VK_IMPORT_INSTANCE
 
 				for (uint32_t ii = 0; ii < numPhysicalDevices; ++ii)
 				{
-					VkPhysicalDeviceProperties pdp;
-					vkGetPhysicalDeviceProperties(physicalDevices[ii], &pdp);
+					VkPhysicalDeviceProperties2 pdp2;
+					pdp2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
+					pdp2.pNext = NULL;
+					vkGetPhysicalDeviceProperties2(physicalDevices[ii], &pdp2);
+
+					VkPhysicalDeviceProperties& pdp = pdp2.properties;
 
 					BX_TRACE("Physical device %d:", ii);
 					BX_TRACE("\t          Name: %s", pdp.deviceName);
@@ -1545,11 +1570,19 @@ VK_IMPORT_INSTANCE
 
 				bx::memCopy(&s_extension[0], &physicalDeviceExtensions[physicalDeviceIdx][0], sizeof(s_extension) );
 
-				vkGetPhysicalDeviceProperties(m_physicalDevice, &m_deviceProperties);
-				g_caps.vendorId = uint16_t(m_deviceProperties.vendorID);
-				g_caps.deviceId = uint16_t(m_deviceProperties.deviceID);
+				m_deviceShadingRateImageProperties = {};
+				m_deviceShadingRateImageProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR;
+
+				m_deviceProperties = {};
+				m_deviceProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
+				m_deviceProperties.pNext = &m_deviceShadingRateImageProperties;
 
-				BX_TRACE("Using physical device %d: %s", physicalDeviceIdx, m_deviceProperties.deviceName);
+				vkGetPhysicalDeviceProperties2(m_physicalDevice, &m_deviceProperties);
+
+				g_caps.vendorId = uint16_t(m_deviceProperties.properties.vendorID);
+				g_caps.deviceId = uint16_t(m_deviceProperties.properties.deviceID);
+
+				BX_TRACE("Using physical device %d: %s", physicalDeviceIdx, m_deviceProperties.properties.deviceName);
 
 				VkPhysicalDeviceFeatures supportedFeatures;
 
@@ -1587,6 +1620,17 @@ VK_IMPORT_INSTANCE
 					vkGetPhysicalDeviceFeatures(m_physicalDevice, &supportedFeatures);
 				}
 
+				if (s_extension[Extension::KHR_fragment_shading_rate].m_supported)
+				{
+					fragmentShadingRate.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR;
+					fragmentShadingRate.pNext = (VkBaseOutStructure*)nextFeatures;
+					fragmentShadingRate.pipelineFragmentShadingRate = VK_TRUE;
+					fragmentShadingRate.primitiveFragmentShadingRate = VK_TRUE;
+					fragmentShadingRate.attachmentFragmentShadingRate = VK_FALSE;
+
+					nextFeatures = &fragmentShadingRate;
+				}
+
 				bx::memSet(&m_deviceFeatures, 0, sizeof(m_deviceFeatures) );
 
 				m_deviceFeatures.fullDrawIndexUint32               = supportedFeatures.fullDrawIndexUint32;
@@ -1619,7 +1663,7 @@ VK_IMPORT_INSTANCE
 					&& customBorderColorFeatures.customBorderColors
 					;
 
-				m_timerQuerySupport = m_deviceProperties.limits.timestampComputeAndGraphics;
+				m_timerQuerySupport = m_deviceProperties.properties.limits.timestampComputeAndGraphics;
 
 				const bool indirectDrawSupport = true
 					&& m_deviceFeatures.multiDrawIndirect
@@ -1653,24 +1697,30 @@ VK_IMPORT_INSTANCE
 					| (s_extension[Extension::EXT_conservative_rasterization ].m_supported ? BGFX_CAPS_CONSERVATIVE_RASTER  : 0)
 					| (s_extension[Extension::EXT_shader_viewport_index_layer].m_supported ? BGFX_CAPS_VIEWPORT_LAYER_ARRAY : 0)
 					| (s_extension[Extension::KHR_draw_indirect_count        ].m_supported && indirectDrawSupport ? BGFX_CAPS_DRAW_INDIRECT_COUNT : 0)
+					| (s_extension[Extension::KHR_fragment_shading_rate      ].m_supported ? BGFX_CAPS_VARIABLE_RATE_SHADING : 0)
 					;
 
-				const uint32_t maxAttachments = bx::min<uint32_t>(m_deviceProperties.limits.maxFragmentOutputAttachments, m_deviceProperties.limits.maxColorAttachments);
+				m_variableRateShadingSupported = s_extension[Extension::KHR_fragment_shading_rate].m_supported;
+
+				const uint32_t maxAttachments = bx::min<uint32_t>(
+					  m_deviceProperties.properties.limits.maxFragmentOutputAttachments
+					, m_deviceProperties.properties.limits.maxColorAttachments
+					);
 
-				g_caps.limits.maxTextureSize     = m_deviceProperties.limits.maxImageDimension2D;
-				g_caps.limits.maxTextureLayers   = m_deviceProperties.limits.maxImageArrayLayers;
+				g_caps.limits.maxTextureSize     = m_deviceProperties.properties.limits.maxImageDimension2D;
+				g_caps.limits.maxTextureLayers   = m_deviceProperties.properties.limits.maxImageArrayLayers;
 				g_caps.limits.maxFBAttachments   = bx::min<uint32_t>(maxAttachments, BGFX_CONFIG_MAX_FRAME_BUFFER_ATTACHMENTS);
-				g_caps.limits.maxTextureSamplers = bx::min<uint32_t>(m_deviceProperties.limits.maxPerStageResources, BGFX_CONFIG_MAX_TEXTURE_SAMPLERS);
-				g_caps.limits.maxComputeBindings = bx::min<uint32_t>(m_deviceProperties.limits.maxPerStageResources, BGFX_MAX_COMPUTE_BINDINGS);
-				g_caps.limits.maxVertexStreams   = bx::min<uint32_t>(m_deviceProperties.limits.maxVertexInputBindings, BGFX_CONFIG_MAX_VERTEX_STREAMS);
+				g_caps.limits.maxTextureSamplers = bx::min<uint32_t>(m_deviceProperties.properties.limits.maxPerStageResources, BGFX_CONFIG_MAX_TEXTURE_SAMPLERS);
+				g_caps.limits.maxComputeBindings = bx::min<uint32_t>(m_deviceProperties.properties.limits.maxPerStageResources, BGFX_MAX_COMPUTE_BINDINGS);
+				g_caps.limits.maxVertexStreams   = bx::min<uint32_t>(m_deviceProperties.properties.limits.maxVertexInputBindings, BGFX_CONFIG_MAX_VERTEX_STREAMS);
 
 				{
 					const VkSampleCountFlags sampleMask = ~0
-						& m_deviceProperties.limits.framebufferColorSampleCounts
-						& m_deviceProperties.limits.framebufferDepthSampleCounts
+						& m_deviceProperties.properties.limits.framebufferColorSampleCounts
+						& m_deviceProperties.properties.limits.framebufferDepthSampleCounts
 						;
 
-					for (uint16_t ii = 0, last = 0; ii < BX_COUNTOF(s_msaa); ii++)
+					for (uint16_t ii = 0, last = 0; ii < BX_COUNTOF(s_msaa); ++ii)
 					{
 						const VkSampleCountFlags sampleBit = s_msaa[ii].Sample;
 
@@ -1685,6 +1735,17 @@ VK_IMPORT_INSTANCE
 					}
 				}
 
+				{
+					const VkExtent2D maxFragmentSize = m_deviceShadingRateImageProperties.maxFragmentSize;
+
+					for (uint32_t ii = 0; ii < BX_COUNTOF(s_shadingRate); ++ii)
+					{
+						ShadingRateVk& shadingRate = s_shadingRate[ii];
+						shadingRate.fragmentSize.width  = bx::min(shadingRate.initFragmentSize.width,  maxFragmentSize.width);
+						shadingRate.fragmentSize.height = bx::min(shadingRate.initFragmentSize.height, maxFragmentSize.height);
+					}
+				}
+
 				for (uint32_t ii = 0; ii < TextureFormat::Count; ++ii)
 				{
 					uint16_t support = BGFX_CAPS_FORMAT_TEXTURE_NONE;
@@ -2792,7 +2853,7 @@ VK_IMPORT_DEVICE
 			float maxAnisotropy = 1.0f;
 			if (!!(_resolution.reset & BGFX_RESET_MAXANISOTROPY) )
 			{
-				maxAnisotropy = m_deviceProperties.limits.maxSamplerAnisotropy;
+				maxAnisotropy = m_deviceProperties.properties.limits.maxSamplerAnisotropy;
 			}
 
 			if (m_maxAnisotropy != maxAnisotropy)
@@ -3490,7 +3551,7 @@ VK_IMPORT_DEVICE
 
 			const uint32_t cmpFunc = (_flags&BGFX_SAMPLER_COMPARE_MASK)>>BGFX_SAMPLER_COMPARE_SHIFT;
 
-			const float maxLodBias = m_deviceProperties.limits.maxSamplerLodBias;
+			const float maxLodBias = m_deviceProperties.properties.limits.maxSamplerLodBias;
 			const float lodBias = bx::clamp(float(BGFX_CONFIG_MIP_LOD_BIAS), -maxLodBias, maxLodBias);
 
 			VkSamplerCreateInfo sci;
@@ -3724,14 +3785,17 @@ VK_IMPORT_DEVICE
 				VK_DYNAMIC_STATE_SCISSOR,
 				VK_DYNAMIC_STATE_BLEND_CONSTANTS,
 				VK_DYNAMIC_STATE_STENCIL_REFERENCE,
+				VK_DYNAMIC_STATE_FRAGMENT_SHADING_RATE_KHR, // optional
 			};
 
 			VkPipelineDynamicStateCreateInfo dynamicState;
 			dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
 			dynamicState.pNext = NULL;
 			dynamicState.flags = 0;
-			dynamicState.dynamicStateCount = BX_COUNTOF(dynamicStates);
-			dynamicState.pDynamicStates    = dynamicStates;
+			dynamicState.dynamicStateCount = BX_COUNTOF(dynamicStates) -
+				(m_variableRateShadingSupported ? 0 : 1)
+				;
+			dynamicState.pDynamicStates = dynamicStates;
 
 			VkPipelineShaderStageCreateInfo shaderStages[2];
 			shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
@@ -4577,9 +4641,10 @@ VK_IMPORT_DEVICE
 		VkPhysicalDevice m_physicalDevice;
 		uint32_t         m_instanceApiVersion;
 
-		VkPhysicalDeviceProperties       m_deviceProperties;
-		VkPhysicalDeviceMemoryProperties m_memoryProperties;
-		VkPhysicalDeviceFeatures         m_deviceFeatures;
+		VkPhysicalDeviceFragmentShadingRatePropertiesKHR m_deviceShadingRateImageProperties;
+		VkPhysicalDeviceProperties2       m_deviceProperties;
+		VkPhysicalDeviceMemoryProperties  m_memoryProperties;
+		VkPhysicalDeviceFeatures          m_deviceFeatures;
 
 		bool m_lineAASupport;
 		bool m_borderColorSupport;
@@ -4641,6 +4706,8 @@ VK_IMPORT_DEVICE
 		DeviceMemoryAllocationVK m_captureMemory;
 		uint32_t m_captureSize;
 
+		bool m_variableRateShadingSupported;
+
 		TextVideoMem m_textVideoMem;
 
 		uint8_t m_fsScratch[64<<10];
@@ -4871,7 +4938,7 @@ VK_DESTROY
 
 	void ScratchBufferVK::createUniform(uint32_t _size, uint32_t _count)
 	{
-		const VkPhysicalDeviceLimits& deviceLimits = s_renderVK->m_deviceProperties.limits;
+		const VkPhysicalDeviceLimits& deviceLimits = s_renderVK->m_deviceProperties.properties.limits;
 		const uint32_t align = uint32_t(deviceLimits.minUniformBufferOffsetAlignment);
 
 		create(_size, _count, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, align);
@@ -4879,7 +4946,7 @@ VK_DESTROY
 
 	void ScratchBufferVK::createStaging(uint32_t _size)
 	{
-		const VkPhysicalDeviceLimits& deviceLimits = s_renderVK->m_deviceProperties.limits;
+		const VkPhysicalDeviceLimits& deviceLimits = s_renderVK->m_deviceProperties.properties.limits;
 		const uint32_t align = uint32_t(deviceLimits.optimalBufferCopyOffsetAlignment);
 
 		create(_size, 1, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, align);
@@ -4924,7 +4991,7 @@ VK_DESTROY
 
 	void ScratchBufferVK::flush(bool _reset)
 	{
-		const VkPhysicalDeviceLimits& deviceLimits = s_renderVK->m_deviceProperties.limits;
+		const VkPhysicalDeviceLimits& deviceLimits = s_renderVK->m_deviceProperties.properties.limits;
 		VkDevice device = s_renderVK->m_device;
 
 		const uint32_t align = uint32_t(deviceLimits.nonCoherentAtomSize);
@@ -5498,7 +5565,7 @@ VK_DESTROY
 
 				if (NULL != m_fsh)
 				{
-					for (uint16_t ii = 0; ii < m_fsh->m_numBindings; ii++)
+					for (uint16_t ii = 0; ii < m_fsh->m_numBindings; ++ii)
 					{
 						const VkDescriptorSetLayoutBinding& fsBinding = m_fsh->m_bindings[ii];
 						uint16_t vsBindingIdx = UINT16_MAX;
@@ -5613,7 +5680,7 @@ VK_DESTROY
 			return result;
 		}
 
-		m_frequency = uint64_t(1000000000.0 / double(s_renderVK->m_deviceProperties.limits.timestampPeriod) );
+		m_frequency = uint64_t(1000000000.0 / double(s_renderVK->m_deviceProperties.properties.limits.timestampPeriod) );
 
 		for (uint32_t ii = 0; ii < BX_COUNTOF(m_result); ++ii)
 		{
@@ -7798,7 +7865,7 @@ VK_DESTROY
 			TextureFormat::RGBA8,
 		};
 
-		for (uint32_t ii = 0; ii < BX_COUNTOF(requestedFormats) && TextureFormat::Count == selectedFormat; ii++)
+		for (uint32_t ii = 0; ii < BX_COUNTOF(requestedFormats) && TextureFormat::Count == selectedFormat; ++ii)
 		{
 			const TextureFormat::Enum requested = requestedFormats[ii];
 			const VkFormat requestedVkFormat = _srgb
@@ -8685,7 +8752,7 @@ VK_DESTROY
 			renderDocTriggerCapture();
 		}
 
-		BGFX_VK_PROFILER_BEGIN_LITERAL("rendererSubmit", kColorView);
+		BGFX_VK_PROFILER_BEGIN_LITERAL("rendererSubmit", kColorFrame);
 
 		int64_t timeBegin = bx::getHPCounter();
 		int64_t captureElapsed = 0;
@@ -8829,7 +8896,8 @@ VK_DESTROY
 					}
 				}
 
-				if(!isCompute && (viewChanged || wasCompute) )
+				if (!isCompute
+				&& (viewChanged || wasCompute) )
 				{
 					if (wasCompute)
 					{
@@ -9028,6 +9096,21 @@ VK_DESTROY
 
 							}
 						}
+
+						if (m_variableRateShadingSupported)
+						{
+							VkFragmentShadingRateCombinerOpKHR combinerOp[] =
+							{
+								VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR,
+								VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR
+							};
+
+							vkCmdSetFragmentShadingRateKHR(
+								  m_commandBuffer
+								, &s_shadingRate[_render->m_view[view].m_shadingRate].fragmentSize
+								, combinerOp
+								);
+						}
 					}
 				}
 
@@ -9724,7 +9807,7 @@ VK_DESTROY
 					, BGFX_REV_NUMBER
 					);
 
-				const VkPhysicalDeviceProperties& pdp = m_deviceProperties;
+				const VkPhysicalDeviceProperties& pdp = m_deviceProperties.properties;
 				tvm.printf(0, pos++, 0x8f, " Device: %s (%s)"
 					, pdp.deviceName
 					, getName(pdp.deviceType)

+ 112 - 108
src/renderer_vk.h

@@ -92,7 +92,7 @@
 			VK_IMPORT_INSTANCE_FUNC(false, vkEnumeratePhysicalDevices);                \
 			VK_IMPORT_INSTANCE_FUNC(false, vkEnumerateDeviceExtensionProperties);      \
 			VK_IMPORT_INSTANCE_FUNC(false, vkEnumerateDeviceLayerProperties);          \
-			VK_IMPORT_INSTANCE_FUNC(false, vkGetPhysicalDeviceProperties);             \
+			VK_IMPORT_INSTANCE_FUNC(false, vkGetPhysicalDeviceProperties2);            \
 			VK_IMPORT_INSTANCE_FUNC(false, vkGetPhysicalDeviceFormatProperties);       \
 			VK_IMPORT_INSTANCE_FUNC(false, vkGetPhysicalDeviceFeatures);               \
 			VK_IMPORT_INSTANCE_FUNC(false, vkGetPhysicalDeviceImageFormatProperties);  \
@@ -112,115 +112,119 @@
 			/* VK_EXT_debug_report */                                                  \
 			VK_IMPORT_INSTANCE_FUNC(true,  vkCreateDebugReportCallbackEXT);            \
 			VK_IMPORT_INSTANCE_FUNC(true,  vkDestroyDebugReportCallbackEXT);           \
+			/* VK_KHR_fragment_shading_rate */                                         \
+			VK_IMPORT_INSTANCE_FUNC(true, vkGetPhysicalDeviceFragmentShadingRatesKHR); \
 			VK_IMPORT_INSTANCE_PLATFORM
 
-#define VK_IMPORT_DEVICE                                                    \
-			VK_IMPORT_DEVICE_FUNC(false, vkGetDeviceQueue);                 \
-			VK_IMPORT_DEVICE_FUNC(false, vkCreateFence);                    \
-			VK_IMPORT_DEVICE_FUNC(false, vkDestroyFence);                   \
-			VK_IMPORT_DEVICE_FUNC(false, vkCreateSemaphore);                \
-			VK_IMPORT_DEVICE_FUNC(false, vkDestroySemaphore);               \
-			VK_IMPORT_DEVICE_FUNC(false, vkResetFences);                    \
-			VK_IMPORT_DEVICE_FUNC(false, vkCreateCommandPool);              \
-			VK_IMPORT_DEVICE_FUNC(false, vkDestroyCommandPool);             \
-			VK_IMPORT_DEVICE_FUNC(false, vkResetCommandPool);               \
-			VK_IMPORT_DEVICE_FUNC(false, vkAllocateCommandBuffers);         \
-			VK_IMPORT_DEVICE_FUNC(false, vkFreeCommandBuffers);             \
-			VK_IMPORT_DEVICE_FUNC(false, vkGetBufferMemoryRequirements);    \
-			VK_IMPORT_DEVICE_FUNC(false, vkGetImageMemoryRequirements);     \
-			VK_IMPORT_DEVICE_FUNC(false, vkGetImageSubresourceLayout);      \
-			VK_IMPORT_DEVICE_FUNC(false, vkAllocateMemory);                 \
-			VK_IMPORT_DEVICE_FUNC(false, vkFreeMemory);                     \
-			VK_IMPORT_DEVICE_FUNC(false, vkCreateImage);                    \
-			VK_IMPORT_DEVICE_FUNC(false, vkDestroyImage);                   \
-			VK_IMPORT_DEVICE_FUNC(false, vkCreateImageView);                \
-			VK_IMPORT_DEVICE_FUNC(false, vkDestroyImageView);               \
-			VK_IMPORT_DEVICE_FUNC(false, vkCreateBuffer);                   \
-			VK_IMPORT_DEVICE_FUNC(false, vkDestroyBuffer);                  \
-			VK_IMPORT_DEVICE_FUNC(false, vkCreateFramebuffer);              \
-			VK_IMPORT_DEVICE_FUNC(false, vkDestroyFramebuffer);             \
-			VK_IMPORT_DEVICE_FUNC(false, vkCreateRenderPass);               \
-			VK_IMPORT_DEVICE_FUNC(false, vkDestroyRenderPass);              \
-			VK_IMPORT_DEVICE_FUNC(false, vkCreateShaderModule);             \
-			VK_IMPORT_DEVICE_FUNC(false, vkDestroyShaderModule);            \
-			VK_IMPORT_DEVICE_FUNC(false, vkCreatePipelineCache);            \
-			VK_IMPORT_DEVICE_FUNC(false, vkDestroyPipelineCache);           \
-			VK_IMPORT_DEVICE_FUNC(false, vkGetPipelineCacheData);           \
-			VK_IMPORT_DEVICE_FUNC(false, vkMergePipelineCaches);            \
-			VK_IMPORT_DEVICE_FUNC(false, vkCreateGraphicsPipelines);        \
-			VK_IMPORT_DEVICE_FUNC(false, vkCreateComputePipelines);         \
-			VK_IMPORT_DEVICE_FUNC(false, vkDestroyPipeline);                \
-			VK_IMPORT_DEVICE_FUNC(false, vkCreatePipelineLayout);           \
-			VK_IMPORT_DEVICE_FUNC(false, vkDestroyPipelineLayout);          \
-			VK_IMPORT_DEVICE_FUNC(false, vkCreateSampler);                  \
-			VK_IMPORT_DEVICE_FUNC(false, vkDestroySampler);                 \
-			VK_IMPORT_DEVICE_FUNC(false, vkCreateDescriptorSetLayout);      \
-			VK_IMPORT_DEVICE_FUNC(false, vkDestroyDescriptorSetLayout);     \
-			VK_IMPORT_DEVICE_FUNC(false, vkCreateDescriptorPool);           \
-			VK_IMPORT_DEVICE_FUNC(false, vkDestroyDescriptorPool);          \
-			VK_IMPORT_DEVICE_FUNC(false, vkResetDescriptorPool);            \
-			VK_IMPORT_DEVICE_FUNC(false, vkAllocateDescriptorSets);         \
-			VK_IMPORT_DEVICE_FUNC(false, vkFreeDescriptorSets);             \
-			VK_IMPORT_DEVICE_FUNC(false, vkUpdateDescriptorSets);           \
-			VK_IMPORT_DEVICE_FUNC(false, vkCreateQueryPool);                \
-			VK_IMPORT_DEVICE_FUNC(false, vkDestroyQueryPool);               \
-			VK_IMPORT_DEVICE_FUNC(false, vkQueueSubmit);                    \
-			VK_IMPORT_DEVICE_FUNC(false, vkQueueWaitIdle);                  \
-			VK_IMPORT_DEVICE_FUNC(false, vkDeviceWaitIdle);                 \
-			VK_IMPORT_DEVICE_FUNC(false, vkWaitForFences);                  \
-			VK_IMPORT_DEVICE_FUNC(false, vkBeginCommandBuffer);             \
-			VK_IMPORT_DEVICE_FUNC(false, vkEndCommandBuffer);               \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdPipelineBarrier);             \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdBeginRenderPass);             \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdEndRenderPass);               \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdSetViewport);                 \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdDraw);                        \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdDrawIndexed);                 \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdDrawIndirect);                \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdDrawIndexedIndirect);         \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdDispatch);                    \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdDispatchIndirect);            \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdBindPipeline);                \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdSetStencilReference);         \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdSetBlendConstants);           \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdSetScissor);                  \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdBindDescriptorSets);          \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdBindIndexBuffer);             \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdBindVertexBuffers);           \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdClearColorImage);             \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdClearDepthStencilImage);      \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdClearAttachments);            \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdResolveImage);                \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdCopyBuffer);                  \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdCopyBufferToImage);           \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdCopyImage);                   \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdCopyImageToBuffer);           \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdBlitImage);                   \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdResetQueryPool);              \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdWriteTimestamp);              \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdBeginQuery);                  \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdEndQuery);                    \
-			VK_IMPORT_DEVICE_FUNC(false, vkCmdCopyQueryPoolResults);        \
-			VK_IMPORT_DEVICE_FUNC(false, vkMapMemory);                      \
-			VK_IMPORT_DEVICE_FUNC(false, vkUnmapMemory);                    \
-			VK_IMPORT_DEVICE_FUNC(false, vkFlushMappedMemoryRanges);        \
-			VK_IMPORT_DEVICE_FUNC(false, vkInvalidateMappedMemoryRanges);   \
-			VK_IMPORT_DEVICE_FUNC(false, vkBindBufferMemory);               \
-			VK_IMPORT_DEVICE_FUNC(false, vkBindImageMemory);                \
-			/* VK_KHR_swapchain */                                          \
-			VK_IMPORT_DEVICE_FUNC(true,  vkCreateSwapchainKHR);             \
-			VK_IMPORT_DEVICE_FUNC(true,  vkDestroySwapchainKHR);            \
-			VK_IMPORT_DEVICE_FUNC(true,  vkGetSwapchainImagesKHR);          \
-			VK_IMPORT_DEVICE_FUNC(true,  vkAcquireNextImageKHR);            \
-			VK_IMPORT_DEVICE_FUNC(true,  vkQueuePresentKHR);                \
-			/* VK_EXT_debug_utils */                                        \
-			VK_IMPORT_DEVICE_FUNC(true,  vkSetDebugUtilsObjectNameEXT);     \
-			VK_IMPORT_DEVICE_FUNC(true,  vkCmdBeginDebugUtilsLabelEXT);     \
-			VK_IMPORT_DEVICE_FUNC(true,  vkCmdEndDebugUtilsLabelEXT);       \
-			VK_IMPORT_DEVICE_FUNC(true,  vkCmdInsertDebugUtilsLabelEXT);    \
-			/* VK_KHR_draw_indirect_count */                                \
-			VK_IMPORT_DEVICE_FUNC(true,  vkCmdDrawIndirectCountKHR);        \
-			VK_IMPORT_DEVICE_FUNC(true,  vkCmdDrawIndexedIndirectCountKHR); \
+#define VK_IMPORT_DEVICE                                                              \
+			VK_IMPORT_DEVICE_FUNC(false, vkGetDeviceQueue);                           \
+			VK_IMPORT_DEVICE_FUNC(false, vkCreateFence);                              \
+			VK_IMPORT_DEVICE_FUNC(false, vkDestroyFence);                             \
+			VK_IMPORT_DEVICE_FUNC(false, vkCreateSemaphore);                          \
+			VK_IMPORT_DEVICE_FUNC(false, vkDestroySemaphore);                         \
+			VK_IMPORT_DEVICE_FUNC(false, vkResetFences);                              \
+			VK_IMPORT_DEVICE_FUNC(false, vkCreateCommandPool);                        \
+			VK_IMPORT_DEVICE_FUNC(false, vkDestroyCommandPool);                       \
+			VK_IMPORT_DEVICE_FUNC(false, vkResetCommandPool);                         \
+			VK_IMPORT_DEVICE_FUNC(false, vkAllocateCommandBuffers);                   \
+			VK_IMPORT_DEVICE_FUNC(false, vkFreeCommandBuffers);                       \
+			VK_IMPORT_DEVICE_FUNC(false, vkGetBufferMemoryRequirements);              \
+			VK_IMPORT_DEVICE_FUNC(false, vkGetImageMemoryRequirements);               \
+			VK_IMPORT_DEVICE_FUNC(false, vkGetImageSubresourceLayout);                \
+			VK_IMPORT_DEVICE_FUNC(false, vkAllocateMemory);                           \
+			VK_IMPORT_DEVICE_FUNC(false, vkFreeMemory);                               \
+			VK_IMPORT_DEVICE_FUNC(false, vkCreateImage);                              \
+			VK_IMPORT_DEVICE_FUNC(false, vkDestroyImage);                             \
+			VK_IMPORT_DEVICE_FUNC(false, vkCreateImageView);                          \
+			VK_IMPORT_DEVICE_FUNC(false, vkDestroyImageView);                         \
+			VK_IMPORT_DEVICE_FUNC(false, vkCreateBuffer);                             \
+			VK_IMPORT_DEVICE_FUNC(false, vkDestroyBuffer);                            \
+			VK_IMPORT_DEVICE_FUNC(false, vkCreateFramebuffer);                        \
+			VK_IMPORT_DEVICE_FUNC(false, vkDestroyFramebuffer);                       \
+			VK_IMPORT_DEVICE_FUNC(false, vkCreateRenderPass);                         \
+			VK_IMPORT_DEVICE_FUNC(false, vkDestroyRenderPass);                        \
+			VK_IMPORT_DEVICE_FUNC(false, vkCreateShaderModule);                       \
+			VK_IMPORT_DEVICE_FUNC(false, vkDestroyShaderModule);                      \
+			VK_IMPORT_DEVICE_FUNC(false, vkCreatePipelineCache);                      \
+			VK_IMPORT_DEVICE_FUNC(false, vkDestroyPipelineCache);                     \
+			VK_IMPORT_DEVICE_FUNC(false, vkGetPipelineCacheData);                     \
+			VK_IMPORT_DEVICE_FUNC(false, vkMergePipelineCaches);                      \
+			VK_IMPORT_DEVICE_FUNC(false, vkCreateGraphicsPipelines);                  \
+			VK_IMPORT_DEVICE_FUNC(false, vkCreateComputePipelines);                   \
+			VK_IMPORT_DEVICE_FUNC(false, vkDestroyPipeline);                          \
+			VK_IMPORT_DEVICE_FUNC(false, vkCreatePipelineLayout);                     \
+			VK_IMPORT_DEVICE_FUNC(false, vkDestroyPipelineLayout);                    \
+			VK_IMPORT_DEVICE_FUNC(false, vkCreateSampler);                            \
+			VK_IMPORT_DEVICE_FUNC(false, vkDestroySampler);                           \
+			VK_IMPORT_DEVICE_FUNC(false, vkCreateDescriptorSetLayout);                \
+			VK_IMPORT_DEVICE_FUNC(false, vkDestroyDescriptorSetLayout);               \
+			VK_IMPORT_DEVICE_FUNC(false, vkCreateDescriptorPool);                     \
+			VK_IMPORT_DEVICE_FUNC(false, vkDestroyDescriptorPool);                    \
+			VK_IMPORT_DEVICE_FUNC(false, vkResetDescriptorPool);                      \
+			VK_IMPORT_DEVICE_FUNC(false, vkAllocateDescriptorSets);                   \
+			VK_IMPORT_DEVICE_FUNC(false, vkFreeDescriptorSets);                       \
+			VK_IMPORT_DEVICE_FUNC(false, vkUpdateDescriptorSets);                     \
+			VK_IMPORT_DEVICE_FUNC(false, vkCreateQueryPool);                          \
+			VK_IMPORT_DEVICE_FUNC(false, vkDestroyQueryPool);                         \
+			VK_IMPORT_DEVICE_FUNC(false, vkQueueSubmit);                              \
+			VK_IMPORT_DEVICE_FUNC(false, vkQueueWaitIdle);                            \
+			VK_IMPORT_DEVICE_FUNC(false, vkDeviceWaitIdle);                           \
+			VK_IMPORT_DEVICE_FUNC(false, vkWaitForFences);                            \
+			VK_IMPORT_DEVICE_FUNC(false, vkBeginCommandBuffer);                       \
+			VK_IMPORT_DEVICE_FUNC(false, vkEndCommandBuffer);                         \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdPipelineBarrier);                       \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdBeginRenderPass);                       \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdEndRenderPass);                         \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdSetViewport);                           \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdDraw);                                  \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdDrawIndexed);                           \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdDrawIndirect);                          \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdDrawIndexedIndirect);                   \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdDispatch);                              \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdDispatchIndirect);                      \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdBindPipeline);                          \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdSetStencilReference);                   \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdSetBlendConstants);                     \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdSetScissor);                            \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdBindDescriptorSets);                    \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdBindIndexBuffer);                       \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdBindVertexBuffers);                     \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdClearColorImage);                       \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdClearDepthStencilImage);                \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdClearAttachments);                      \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdResolveImage);                          \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdCopyBuffer);                            \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdCopyBufferToImage);                     \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdCopyImage);                             \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdCopyImageToBuffer);                     \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdBlitImage);                             \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdResetQueryPool);                        \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdWriteTimestamp);                        \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdBeginQuery);                            \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdEndQuery);                              \
+			VK_IMPORT_DEVICE_FUNC(false, vkCmdCopyQueryPoolResults);                  \
+			VK_IMPORT_DEVICE_FUNC(false, vkMapMemory);                                \
+			VK_IMPORT_DEVICE_FUNC(false, vkUnmapMemory);                              \
+			VK_IMPORT_DEVICE_FUNC(false, vkFlushMappedMemoryRanges);                  \
+			VK_IMPORT_DEVICE_FUNC(false, vkInvalidateMappedMemoryRanges);             \
+			VK_IMPORT_DEVICE_FUNC(false, vkBindBufferMemory);                         \
+			VK_IMPORT_DEVICE_FUNC(false, vkBindImageMemory);                          \
+			/* VK_KHR_swapchain */                                                    \
+			VK_IMPORT_DEVICE_FUNC(true,  vkCreateSwapchainKHR);                       \
+			VK_IMPORT_DEVICE_FUNC(true,  vkDestroySwapchainKHR);                      \
+			VK_IMPORT_DEVICE_FUNC(true,  vkGetSwapchainImagesKHR);                    \
+			VK_IMPORT_DEVICE_FUNC(true,  vkAcquireNextImageKHR);                      \
+			VK_IMPORT_DEVICE_FUNC(true,  vkQueuePresentKHR);                          \
+			/* VK_EXT_debug_utils */                                                  \
+			VK_IMPORT_DEVICE_FUNC(true,  vkSetDebugUtilsObjectNameEXT);               \
+			VK_IMPORT_DEVICE_FUNC(true,  vkCmdBeginDebugUtilsLabelEXT);               \
+			VK_IMPORT_DEVICE_FUNC(true,  vkCmdEndDebugUtilsLabelEXT);                 \
+			VK_IMPORT_DEVICE_FUNC(true,  vkCmdInsertDebugUtilsLabelEXT);              \
+			/* VK_KHR_draw_indirect_count */                                          \
+			VK_IMPORT_DEVICE_FUNC(true,  vkCmdDrawIndirectCountKHR);                  \
+			VK_IMPORT_DEVICE_FUNC(true,  vkCmdDrawIndexedIndirectCountKHR);           \
+			/* VK_KHR_fragment_shading_rate */                                        \
+			VK_IMPORT_DEVICE_FUNC(true, vkCmdSetFragmentShadingRateKHR);              \
 
 #define VK_DESTROY                                \
 			VK_DESTROY_FUNC(Buffer);              \