GeometryShaderGen.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. // Copyright 2014 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "VideoCommon/GeometryShaderGen.h"
  4. #include <cmath>
  5. #include "Common/CommonTypes.h"
  6. #include "Common/EnumMap.h"
  7. #include "VideoCommon/DriverDetails.h"
  8. #include "VideoCommon/LightingShaderGen.h"
  9. #include "VideoCommon/VideoCommon.h"
  10. #include "VideoCommon/VideoConfig.h"
  11. #include "VideoCommon/XFMemory.h"
  12. constexpr Common::EnumMap<const char*, PrimitiveType::TriangleStrip> primitives_ogl{
  13. "points",
  14. "lines",
  15. "triangles",
  16. "triangles",
  17. };
  18. constexpr Common::EnumMap<const char*, PrimitiveType::TriangleStrip> primitives_d3d{
  19. "point",
  20. "line",
  21. "triangle",
  22. "triangle",
  23. };
  24. constexpr Common::EnumMap<u32, PrimitiveType::TriangleStrip> vertex_in_map{1u, 2u, 3u, 3u};
  25. constexpr Common::EnumMap<u32, PrimitiveType::TriangleStrip> vertex_out_map{4u, 4u, 4u, 3u};
  26. bool geometry_shader_uid_data::IsPassthrough() const
  27. {
  28. const bool stereo = g_ActiveConfig.stereo_mode != StereoMode::Off;
  29. const bool wireframe = g_ActiveConfig.bWireFrame;
  30. return primitive_type >= static_cast<u32>(PrimitiveType::Triangles) && !stereo && !wireframe;
  31. }
  32. GeometryShaderUid GetGeometryShaderUid(PrimitiveType primitive_type)
  33. {
  34. GeometryShaderUid out;
  35. geometry_shader_uid_data* const uid_data = out.GetUidData();
  36. uid_data->primitive_type = static_cast<u32>(primitive_type);
  37. uid_data->numTexGens = xfmem.numTexGen.numTexGens;
  38. return out;
  39. }
  40. static void EmitVertex(ShaderCode& out, const ShaderHostConfig& host_config,
  41. const geometry_shader_uid_data* uid_data, const char* vertex,
  42. APIType api_type, bool wireframe, bool stereo, bool first_vertex = false);
  43. static void EndPrimitive(ShaderCode& out, const ShaderHostConfig& host_config,
  44. const geometry_shader_uid_data* uid_data, APIType api_type, bool wireframe,
  45. bool stereo);
  46. ShaderCode GenerateGeometryShaderCode(APIType api_type, const ShaderHostConfig& host_config,
  47. const geometry_shader_uid_data* uid_data)
  48. {
  49. ShaderCode out;
  50. // Non-uid template parameters will write to the dummy data (=> gets optimized out)
  51. const bool wireframe = host_config.wireframe;
  52. const bool msaa = host_config.msaa;
  53. const bool ssaa = host_config.ssaa;
  54. const bool stereo = host_config.stereo;
  55. const auto primitive_type = static_cast<PrimitiveType>(uid_data->primitive_type);
  56. const u32 vertex_in = vertex_in_map[primitive_type];
  57. u32 vertex_out = vertex_out_map[primitive_type];
  58. if (wireframe)
  59. vertex_out++;
  60. if (api_type == APIType::OpenGL || api_type == APIType::Vulkan)
  61. {
  62. // Insert layout parameters
  63. if (host_config.backend_gs_instancing)
  64. {
  65. out.Write("layout({}, invocations = {}) in;\n", primitives_ogl[primitive_type],
  66. stereo ? 2 : 1);
  67. out.Write("layout({}_strip, max_vertices = {}) out;\n", wireframe ? "line" : "triangle",
  68. vertex_out);
  69. }
  70. else
  71. {
  72. out.Write("layout({}) in;\n", primitives_ogl[primitive_type]);
  73. out.Write("layout({}_strip, max_vertices = {}) out;\n", wireframe ? "line" : "triangle",
  74. stereo ? vertex_out * 2 : vertex_out);
  75. }
  76. }
  77. out.Write("{}", s_lighting_struct);
  78. // uniforms
  79. if (api_type == APIType::OpenGL || api_type == APIType::Vulkan)
  80. out.Write("UBO_BINDING(std140, 4) uniform GSBlock {{\n");
  81. else
  82. out.Write("cbuffer GSBlock {{\n");
  83. out.Write("{}", s_geometry_shader_uniforms);
  84. out.Write("}};\n");
  85. out.Write("struct VS_OUTPUT {{\n");
  86. GenerateVSOutputMembers(out, api_type, uid_data->numTexGens, host_config, "",
  87. ShaderStage::Geometry);
  88. out.Write("}};\n");
  89. if (api_type == APIType::OpenGL || api_type == APIType::Vulkan)
  90. {
  91. if (host_config.backend_gs_instancing)
  92. out.Write("#define InstanceID gl_InvocationID\n");
  93. out.Write("VARYING_LOCATION(0) in VertexData {{\n");
  94. GenerateVSOutputMembers(out, api_type, uid_data->numTexGens, host_config,
  95. GetInterpolationQualifier(msaa, ssaa, true, true),
  96. ShaderStage::Geometry);
  97. out.Write("}} vs[{}];\n", vertex_in);
  98. out.Write("VARYING_LOCATION(0) out VertexData {{\n");
  99. GenerateVSOutputMembers(out, api_type, uid_data->numTexGens, host_config,
  100. GetInterpolationQualifier(msaa, ssaa, true, false),
  101. ShaderStage::Geometry);
  102. out.Write("}} ps;\n");
  103. if (stereo && !host_config.backend_gl_layer_in_fs)
  104. out.Write("flat out int layer;");
  105. out.Write("void main()\n{{\n");
  106. }
  107. else // D3D
  108. {
  109. out.Write("struct VertexData {{\n");
  110. out.Write("\tVS_OUTPUT o;\n");
  111. if (stereo)
  112. {
  113. out.Write("\tuint layer : SV_RenderTargetArrayIndex;\n");
  114. }
  115. out.Write("\tfloat4 posout : SV_Position;\n");
  116. out.Write("}};\n");
  117. if (host_config.backend_gs_instancing)
  118. {
  119. out.Write("[maxvertexcount({})]\n[instance({})]\n", vertex_out, stereo ? 2 : 1);
  120. out.Write("void main({} VS_OUTPUT o[{}], inout {}Stream<VertexData> output, in uint "
  121. "InstanceID : SV_GSInstanceID)\n{{\n",
  122. primitives_d3d[primitive_type], vertex_in, wireframe ? "Line" : "Triangle");
  123. }
  124. else
  125. {
  126. out.Write("[maxvertexcount({})]\n", stereo ? vertex_out * 2 : vertex_out);
  127. out.Write("void main({} VS_OUTPUT o[{}], inout {}Stream<VertexData> output)\n{{\n",
  128. primitives_d3d[primitive_type], vertex_in, wireframe ? "Line" : "Triangle");
  129. }
  130. out.Write("\tVertexData ps;\n");
  131. }
  132. if (primitive_type == PrimitiveType::Lines)
  133. {
  134. if (api_type == APIType::OpenGL || api_type == APIType::Vulkan)
  135. {
  136. out.Write("\tVS_OUTPUT start, end;\n");
  137. AssignVSOutputMembers(out, "start", "vs[0]", uid_data->numTexGens, host_config);
  138. AssignVSOutputMembers(out, "end", "vs[1]", uid_data->numTexGens, host_config);
  139. }
  140. else
  141. {
  142. out.Write("\tVS_OUTPUT start = o[0];\n"
  143. "\tVS_OUTPUT end = o[1];\n");
  144. }
  145. GenerateLineOffset(out, "\t", "\t\t", "end.pos", "start.pos", "");
  146. }
  147. else if (primitive_type == PrimitiveType::Points)
  148. {
  149. if (api_type == APIType::OpenGL || api_type == APIType::Vulkan)
  150. {
  151. out.Write("\tVS_OUTPUT center;\n");
  152. AssignVSOutputMembers(out, "center", "vs[0]", uid_data->numTexGens, host_config);
  153. }
  154. else
  155. {
  156. out.Write("\tVS_OUTPUT center = o[0];\n");
  157. }
  158. // Offset from center to upper right vertex
  159. // Lerp PointSize/2 from [0,0..VpWidth,VpHeight] to [-1,1..1,-1]
  160. out.Write("\tfloat2 offset = float2(" I_LINEPTPARAMS ".w / " I_LINEPTPARAMS
  161. ".x, -" I_LINEPTPARAMS ".w / " I_LINEPTPARAMS ".y) * center.pos.w;\n");
  162. }
  163. if (stereo)
  164. {
  165. // If the GPU supports invocation we don't need a for loop and can simply use the
  166. // invocation identifier to determine which layer we're rendering.
  167. if (host_config.backend_gs_instancing)
  168. out.Write("\tint eye = InstanceID;\n");
  169. else
  170. out.Write("\tfor (int eye = 0; eye < 2; ++eye) {{\n");
  171. }
  172. if (wireframe)
  173. out.Write("\tVS_OUTPUT first;\n");
  174. // Avoid D3D warning about forced unrolling of single-iteration loop
  175. if (vertex_in > 1)
  176. out.Write("\tfor (int i = 0; i < {}; ++i) {{\n", vertex_in);
  177. else
  178. out.Write("\tint i = 0;\n");
  179. if (api_type == APIType::OpenGL || api_type == APIType::Vulkan)
  180. {
  181. out.Write("\tVS_OUTPUT f;\n");
  182. AssignVSOutputMembers(out, "f", "vs[i]", uid_data->numTexGens, host_config);
  183. if (host_config.backend_depth_clamp &&
  184. DriverDetails::HasBug(DriverDetails::BUG_BROKEN_CLIP_DISTANCE))
  185. {
  186. // On certain GPUs we have to consume the clip distance from the vertex shader
  187. // or else the other vertex shader outputs will get corrupted.
  188. out.Write("\tf.clipDist0 = gl_in[i].gl_ClipDistance[0];\n"
  189. "\tf.clipDist1 = gl_in[i].gl_ClipDistance[1];\n");
  190. }
  191. }
  192. else
  193. {
  194. out.Write("\tVS_OUTPUT f = o[i];\n");
  195. }
  196. if (stereo)
  197. {
  198. // For stereoscopy add a small horizontal offset in Normalized Device Coordinates proportional
  199. // to the depth of the vertex. We retrieve the depth value from the w-component of the projected
  200. // vertex which contains the negated z-component of the original vertex.
  201. // For negative parallax (out-of-screen effects) we subtract a convergence value from
  202. // the depth value. This results in objects at a distance smaller than the convergence
  203. // distance to seemingly appear in front of the screen.
  204. // This formula is based on page 13 of the "Nvidia 3D Vision Automatic, Best Practices Guide"
  205. out.Write("\tfloat hoffset = (eye == 0) ? " I_STEREOPARAMS ".x : " I_STEREOPARAMS ".y;\n");
  206. out.Write("\tf.pos.x += hoffset * (f.pos.w - " I_STEREOPARAMS ".z);\n");
  207. }
  208. if (primitive_type == PrimitiveType::Lines)
  209. {
  210. out.Write("\tVS_OUTPUT l = f;\n"
  211. "\tVS_OUTPUT r = f;\n");
  212. out.Write("\tl.pos.xy -= offset * l.pos.w;\n"
  213. "\tr.pos.xy += offset * r.pos.w;\n");
  214. out.Write("\tif (" I_TEXOFFSET "[2] != 0) {{\n");
  215. out.Write("\tfloat texOffset = 1.0 / float(" I_TEXOFFSET "[2]);\n");
  216. for (u32 i = 0; i < uid_data->numTexGens; ++i)
  217. {
  218. out.Write("\tif (((" I_TEXOFFSET "[0] >> {}) & 0x1) != 0)\n", i);
  219. out.Write("\t\tr.tex{}.x += texOffset;\n", i);
  220. }
  221. out.Write("\t}}\n");
  222. EmitVertex(out, host_config, uid_data, "l", api_type, wireframe, stereo, true);
  223. EmitVertex(out, host_config, uid_data, "r", api_type, wireframe, stereo);
  224. }
  225. else if (primitive_type == PrimitiveType::Points)
  226. {
  227. out.Write("\tVS_OUTPUT ll = f;\n"
  228. "\tVS_OUTPUT lr = f;\n"
  229. "\tVS_OUTPUT ul = f;\n"
  230. "\tVS_OUTPUT ur = f;\n");
  231. out.Write("\tll.pos.xy += float2(-1,-1) * offset;\n"
  232. "\tlr.pos.xy += float2(1,-1) * offset;\n"
  233. "\tul.pos.xy += float2(-1,1) * offset;\n"
  234. "\tur.pos.xy += offset;\n");
  235. out.Write("\tif (" I_TEXOFFSET "[3] != 0) {{\n");
  236. out.Write("\tfloat2 texOffset = float2(1.0 / float(" I_TEXOFFSET
  237. "[3]), 1.0 / float(" I_TEXOFFSET "[3]));\n");
  238. for (u32 i = 0; i < uid_data->numTexGens; ++i)
  239. {
  240. out.Write("\tif (((" I_TEXOFFSET "[1] >> {}) & 0x1) != 0) {{\n", i);
  241. out.Write("\t\tul.tex{}.xy += float2(0,1) * texOffset;\n", i);
  242. out.Write("\t\tur.tex{}.xy += texOffset;\n", i);
  243. out.Write("\t\tlr.tex{}.xy += float2(1,0) * texOffset;\n", i);
  244. out.Write("\t}}\n");
  245. }
  246. out.Write("\t}}\n");
  247. EmitVertex(out, host_config, uid_data, "ll", api_type, wireframe, stereo, true);
  248. EmitVertex(out, host_config, uid_data, "lr", api_type, wireframe, stereo);
  249. EmitVertex(out, host_config, uid_data, "ul", api_type, wireframe, stereo);
  250. EmitVertex(out, host_config, uid_data, "ur", api_type, wireframe, stereo);
  251. }
  252. else
  253. {
  254. EmitVertex(out, host_config, uid_data, "f", api_type, wireframe, stereo, true);
  255. }
  256. // Only close loop if previous code was in one (See D3D warning above)
  257. if (vertex_in > 1)
  258. out.Write("\t}}\n");
  259. EndPrimitive(out, host_config, uid_data, api_type, wireframe, stereo);
  260. if (stereo && !host_config.backend_gs_instancing)
  261. out.Write("\t}}\n");
  262. out.Write("}}\n");
  263. return out;
  264. }
  265. static void EmitVertex(ShaderCode& out, const ShaderHostConfig& host_config,
  266. const geometry_shader_uid_data* uid_data, const char* vertex,
  267. APIType api_type, bool wireframe, bool stereo, bool first_vertex)
  268. {
  269. if (wireframe && first_vertex)
  270. out.Write("\tif (i == 0) first = {};\n", vertex);
  271. if (api_type == APIType::OpenGL || api_type == APIType::Vulkan)
  272. {
  273. // Vulkan NDC space has Y pointing down (right-handed NDC space).
  274. if (api_type == APIType::Vulkan)
  275. out.Write("\tgl_Position = float4({0}.pos.x, -{0}.pos.y, {0}.pos.z, {0}.pos.w);\n", vertex);
  276. else
  277. out.Write("\tgl_Position = {}.pos;\n", vertex);
  278. if (host_config.backend_depth_clamp)
  279. {
  280. out.Write("\tgl_ClipDistance[0] = {}.clipDist0;\n", vertex);
  281. out.Write("\tgl_ClipDistance[1] = {}.clipDist1;\n", vertex);
  282. }
  283. AssignVSOutputMembers(out, "ps", vertex, uid_data->numTexGens, host_config);
  284. }
  285. else
  286. {
  287. out.Write("\tps.o = {};\n", vertex);
  288. out.Write("\tps.posout = {}.pos;\n", vertex);
  289. }
  290. if (stereo)
  291. {
  292. // Select the output layer
  293. if (api_type == APIType::OpenGL || api_type == APIType::Vulkan)
  294. out.Write("\tgl_Layer = eye;\n");
  295. else
  296. {
  297. out.Write("\tps.layer = eye;\n");
  298. }
  299. if (!host_config.backend_gl_layer_in_fs)
  300. out.Write("\tlayer = eye;\n");
  301. }
  302. if (api_type == APIType::OpenGL || api_type == APIType::Vulkan)
  303. out.Write("\tEmitVertex();\n");
  304. else
  305. out.Write("\toutput.Append(ps);\n");
  306. }
  307. static void EndPrimitive(ShaderCode& out, const ShaderHostConfig& host_config,
  308. const geometry_shader_uid_data* uid_data, APIType api_type, bool wireframe,
  309. bool stereo)
  310. {
  311. if (wireframe)
  312. EmitVertex(out, host_config, uid_data, "first", api_type, wireframe, stereo);
  313. if (api_type == APIType::OpenGL || api_type == APIType::Vulkan)
  314. out.Write("\tEndPrimitive();\n");
  315. else
  316. out.Write("\toutput.RestartStrip();\n");
  317. }
  318. void EnumerateGeometryShaderUids(const std::function<void(const GeometryShaderUid&)>& callback)
  319. {
  320. GeometryShaderUid uid;
  321. const std::array<PrimitiveType, 3> primitive_lut = {
  322. {g_ActiveConfig.backend_info.bSupportsPrimitiveRestart ? PrimitiveType::TriangleStrip :
  323. PrimitiveType::Triangles,
  324. PrimitiveType::Lines, PrimitiveType::Points}};
  325. for (PrimitiveType primitive : primitive_lut)
  326. {
  327. geometry_shader_uid_data* const guid = uid.GetUidData();
  328. guid->primitive_type = static_cast<u32>(primitive);
  329. for (u32 texgens = 0; texgens <= 8; texgens++)
  330. {
  331. guid->numTexGens = texgens;
  332. callback(uid);
  333. }
  334. }
  335. }