validate_mode_setting.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. // Copyright (c) 2018 Google LLC.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. //
  15. #include "source/val/validate.h"
  16. #include <algorithm>
  17. #include "source/opcode.h"
  18. #include "source/spirv_target_env.h"
  19. #include "source/val/instruction.h"
  20. #include "source/val/validation_state.h"
  21. namespace spvtools {
  22. namespace val {
  23. namespace {
  24. spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) {
  25. const auto entry_point_id = inst->GetOperandAs<uint32_t>(1);
  26. auto entry_point = _.FindDef(entry_point_id);
  27. if (!entry_point || SpvOpFunction != entry_point->opcode()) {
  28. return _.diag(SPV_ERROR_INVALID_ID, inst)
  29. << "OpEntryPoint Entry Point <id> '" << _.getIdName(entry_point_id)
  30. << "' is not a function.";
  31. }
  32. // don't check kernel function signatures
  33. const SpvExecutionModel execution_model =
  34. inst->GetOperandAs<SpvExecutionModel>(0);
  35. if (execution_model != SpvExecutionModelKernel) {
  36. // TODO: Check the entry point signature is void main(void), may be subject
  37. // to change
  38. const auto entry_point_type_id = entry_point->GetOperandAs<uint32_t>(3);
  39. const auto entry_point_type = _.FindDef(entry_point_type_id);
  40. if (!entry_point_type || 3 != entry_point_type->words().size()) {
  41. return _.diag(SPV_ERROR_INVALID_ID, inst)
  42. << "OpEntryPoint Entry Point <id> '" << _.getIdName(entry_point_id)
  43. << "'s function parameter count is not zero.";
  44. }
  45. }
  46. auto return_type = _.FindDef(entry_point->type_id());
  47. if (!return_type || SpvOpTypeVoid != return_type->opcode()) {
  48. return _.diag(SPV_ERROR_INVALID_ID, inst)
  49. << "OpEntryPoint Entry Point <id> '" << _.getIdName(entry_point_id)
  50. << "'s function return type is not void.";
  51. }
  52. const auto* execution_modes = _.GetExecutionModes(entry_point_id);
  53. if (_.HasCapability(SpvCapabilityShader)) {
  54. switch (execution_model) {
  55. case SpvExecutionModelFragment:
  56. if (execution_modes &&
  57. execution_modes->count(SpvExecutionModeOriginUpperLeft) &&
  58. execution_modes->count(SpvExecutionModeOriginLowerLeft)) {
  59. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  60. << "Fragment execution model entry points can only specify "
  61. "one of OriginUpperLeft or OriginLowerLeft execution "
  62. "modes.";
  63. }
  64. if (!execution_modes ||
  65. (!execution_modes->count(SpvExecutionModeOriginUpperLeft) &&
  66. !execution_modes->count(SpvExecutionModeOriginLowerLeft))) {
  67. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  68. << "Fragment execution model entry points require either an "
  69. "OriginUpperLeft or OriginLowerLeft execution mode.";
  70. }
  71. if (execution_modes &&
  72. 1 < std::count_if(execution_modes->begin(), execution_modes->end(),
  73. [](const SpvExecutionMode& mode) {
  74. switch (mode) {
  75. case SpvExecutionModeDepthGreater:
  76. case SpvExecutionModeDepthLess:
  77. case SpvExecutionModeDepthUnchanged:
  78. return true;
  79. default:
  80. return false;
  81. }
  82. })) {
  83. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  84. << "Fragment execution model entry points can specify at most "
  85. "one of DepthGreater, DepthLess or DepthUnchanged "
  86. "execution modes.";
  87. }
  88. break;
  89. case SpvExecutionModelTessellationControl:
  90. case SpvExecutionModelTessellationEvaluation:
  91. if (execution_modes &&
  92. 1 < std::count_if(execution_modes->begin(), execution_modes->end(),
  93. [](const SpvExecutionMode& mode) {
  94. switch (mode) {
  95. case SpvExecutionModeSpacingEqual:
  96. case SpvExecutionModeSpacingFractionalEven:
  97. case SpvExecutionModeSpacingFractionalOdd:
  98. return true;
  99. default:
  100. return false;
  101. }
  102. })) {
  103. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  104. << "Tessellation execution model entry points can specify at "
  105. "most one of SpacingEqual, SpacingFractionalOdd or "
  106. "SpacingFractionalEven execution modes.";
  107. }
  108. if (execution_modes &&
  109. 1 < std::count_if(execution_modes->begin(), execution_modes->end(),
  110. [](const SpvExecutionMode& mode) {
  111. switch (mode) {
  112. case SpvExecutionModeTriangles:
  113. case SpvExecutionModeQuads:
  114. case SpvExecutionModeIsolines:
  115. return true;
  116. default:
  117. return false;
  118. }
  119. })) {
  120. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  121. << "Tessellation execution model entry points can specify at "
  122. "most one of Triangles, Quads or Isolines execution modes.";
  123. }
  124. if (execution_modes &&
  125. 1 < std::count_if(execution_modes->begin(), execution_modes->end(),
  126. [](const SpvExecutionMode& mode) {
  127. switch (mode) {
  128. case SpvExecutionModeVertexOrderCw:
  129. case SpvExecutionModeVertexOrderCcw:
  130. return true;
  131. default:
  132. return false;
  133. }
  134. })) {
  135. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  136. << "Tessellation execution model entry points can specify at "
  137. "most one of VertexOrderCw or VertexOrderCcw execution "
  138. "modes.";
  139. }
  140. break;
  141. case SpvExecutionModelGeometry:
  142. if (!execution_modes ||
  143. 1 != std::count_if(execution_modes->begin(), execution_modes->end(),
  144. [](const SpvExecutionMode& mode) {
  145. switch (mode) {
  146. case SpvExecutionModeInputPoints:
  147. case SpvExecutionModeInputLines:
  148. case SpvExecutionModeInputLinesAdjacency:
  149. case SpvExecutionModeTriangles:
  150. case SpvExecutionModeInputTrianglesAdjacency:
  151. return true;
  152. default:
  153. return false;
  154. }
  155. })) {
  156. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  157. << "Geometry execution model entry points must specify "
  158. "exactly one of InputPoints, InputLines, "
  159. "InputLinesAdjacency, Triangles or InputTrianglesAdjacency "
  160. "execution modes.";
  161. }
  162. if (!execution_modes ||
  163. 1 != std::count_if(execution_modes->begin(), execution_modes->end(),
  164. [](const SpvExecutionMode& mode) {
  165. switch (mode) {
  166. case SpvExecutionModeOutputPoints:
  167. case SpvExecutionModeOutputLineStrip:
  168. case SpvExecutionModeOutputTriangleStrip:
  169. return true;
  170. default:
  171. return false;
  172. }
  173. })) {
  174. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  175. << "Geometry execution model entry points must specify "
  176. "exactly one of OutputPoints, OutputLineStrip or "
  177. "OutputTriangleStrip execution modes.";
  178. }
  179. break;
  180. default:
  181. break;
  182. }
  183. }
  184. if (spvIsVulkanEnv(_.context()->target_env)) {
  185. switch (execution_model) {
  186. case SpvExecutionModelGLCompute:
  187. if (!execution_modes ||
  188. !execution_modes->count(SpvExecutionModeLocalSize)) {
  189. bool ok = false;
  190. for (auto& i : _.ordered_instructions()) {
  191. if (i.opcode() == SpvOpDecorate) {
  192. if (i.operands().size() > 2) {
  193. if (i.GetOperandAs<SpvDecoration>(1) == SpvDecorationBuiltIn &&
  194. i.GetOperandAs<SpvBuiltIn>(2) == SpvBuiltInWorkgroupSize) {
  195. ok = true;
  196. break;
  197. }
  198. }
  199. }
  200. }
  201. if (!ok) {
  202. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  203. << "In the Vulkan environment, GLCompute execution model "
  204. "entry points require either the LocalSize execution "
  205. "mode or an object decorated with WorkgroupSize must be "
  206. "specified.";
  207. }
  208. }
  209. break;
  210. default:
  211. break;
  212. }
  213. }
  214. return SPV_SUCCESS;
  215. }
  216. spv_result_t ValidateExecutionMode(ValidationState_t& _,
  217. const Instruction* inst) {
  218. const auto entry_point_id = inst->GetOperandAs<uint32_t>(0);
  219. const auto found = std::find(_.entry_points().cbegin(),
  220. _.entry_points().cend(), entry_point_id);
  221. if (found == _.entry_points().cend()) {
  222. return _.diag(SPV_ERROR_INVALID_ID, inst)
  223. << "OpExecutionMode Entry Point <id> '"
  224. << _.getIdName(entry_point_id)
  225. << "' is not the Entry Point "
  226. "operand of an OpEntryPoint.";
  227. }
  228. const auto mode = inst->GetOperandAs<SpvExecutionMode>(1);
  229. const auto* models = _.GetExecutionModels(entry_point_id);
  230. switch (mode) {
  231. case SpvExecutionModeInvocations:
  232. case SpvExecutionModeInputPoints:
  233. case SpvExecutionModeInputLines:
  234. case SpvExecutionModeInputLinesAdjacency:
  235. case SpvExecutionModeInputTrianglesAdjacency:
  236. case SpvExecutionModeOutputLineStrip:
  237. case SpvExecutionModeOutputTriangleStrip:
  238. if (!std::all_of(models->begin(), models->end(),
  239. [](const SpvExecutionModel& model) {
  240. return model == SpvExecutionModelGeometry;
  241. })) {
  242. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  243. << "Execution mode can only be used with the Geometry execution "
  244. "model.";
  245. }
  246. break;
  247. case SpvExecutionModeOutputPoints:
  248. if (!std::all_of(models->begin(), models->end(),
  249. [&_](const SpvExecutionModel& model) {
  250. switch (model) {
  251. case SpvExecutionModelGeometry:
  252. return true;
  253. case SpvExecutionModelMeshNV:
  254. return _.HasCapability(SpvCapabilityMeshShadingNV);
  255. default:
  256. return false;
  257. }
  258. })) {
  259. if (_.HasCapability(SpvCapabilityMeshShadingNV)) {
  260. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  261. << "Execution mode can only be used with the Geometry or "
  262. "MeshNV execution model.";
  263. } else {
  264. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  265. << "Execution mode can only be used with the Geometry "
  266. "execution "
  267. "model.";
  268. }
  269. }
  270. break;
  271. case SpvExecutionModeSpacingEqual:
  272. case SpvExecutionModeSpacingFractionalEven:
  273. case SpvExecutionModeSpacingFractionalOdd:
  274. case SpvExecutionModeVertexOrderCw:
  275. case SpvExecutionModeVertexOrderCcw:
  276. case SpvExecutionModePointMode:
  277. case SpvExecutionModeQuads:
  278. case SpvExecutionModeIsolines:
  279. if (!std::all_of(
  280. models->begin(), models->end(),
  281. [](const SpvExecutionModel& model) {
  282. return (model == SpvExecutionModelTessellationControl) ||
  283. (model == SpvExecutionModelTessellationEvaluation);
  284. })) {
  285. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  286. << "Execution mode can only be used with a tessellation "
  287. "execution model.";
  288. }
  289. break;
  290. case SpvExecutionModeTriangles:
  291. if (!std::all_of(models->begin(), models->end(),
  292. [](const SpvExecutionModel& model) {
  293. switch (model) {
  294. case SpvExecutionModelGeometry:
  295. case SpvExecutionModelTessellationControl:
  296. case SpvExecutionModelTessellationEvaluation:
  297. return true;
  298. default:
  299. return false;
  300. }
  301. })) {
  302. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  303. << "Execution mode can only be used with a Geometry or "
  304. "tessellation execution model.";
  305. }
  306. break;
  307. case SpvExecutionModeOutputVertices:
  308. if (!std::all_of(models->begin(), models->end(),
  309. [&_](const SpvExecutionModel& model) {
  310. switch (model) {
  311. case SpvExecutionModelGeometry:
  312. case SpvExecutionModelTessellationControl:
  313. case SpvExecutionModelTessellationEvaluation:
  314. return true;
  315. case SpvExecutionModelMeshNV:
  316. return _.HasCapability(SpvCapabilityMeshShadingNV);
  317. default:
  318. return false;
  319. }
  320. })) {
  321. if (_.HasCapability(SpvCapabilityMeshShadingNV)) {
  322. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  323. << "Execution mode can only be used with a Geometry, "
  324. "tessellation or MeshNV execution model.";
  325. } else {
  326. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  327. << "Execution mode can only be used with a Geometry or "
  328. "tessellation execution model.";
  329. }
  330. }
  331. break;
  332. case SpvExecutionModePixelCenterInteger:
  333. case SpvExecutionModeOriginUpperLeft:
  334. case SpvExecutionModeOriginLowerLeft:
  335. case SpvExecutionModeEarlyFragmentTests:
  336. case SpvExecutionModeDepthReplacing:
  337. case SpvExecutionModeDepthGreater:
  338. case SpvExecutionModeDepthLess:
  339. case SpvExecutionModeDepthUnchanged:
  340. if (!std::all_of(models->begin(), models->end(),
  341. [](const SpvExecutionModel& model) {
  342. return model == SpvExecutionModelFragment;
  343. })) {
  344. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  345. << "Execution mode can only be used with the Fragment execution "
  346. "model.";
  347. }
  348. break;
  349. case SpvExecutionModeLocalSizeHint:
  350. case SpvExecutionModeVecTypeHint:
  351. case SpvExecutionModeContractionOff:
  352. case SpvExecutionModeLocalSizeHintId:
  353. if (!std::all_of(models->begin(), models->end(),
  354. [](const SpvExecutionModel& model) {
  355. return model == SpvExecutionModelKernel;
  356. })) {
  357. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  358. << "Execution mode can only be used with the Kernel execution "
  359. "model.";
  360. }
  361. break;
  362. case SpvExecutionModeLocalSize:
  363. case SpvExecutionModeLocalSizeId:
  364. if (!std::all_of(models->begin(), models->end(),
  365. [&_](const SpvExecutionModel& model) {
  366. switch (model) {
  367. case SpvExecutionModelKernel:
  368. case SpvExecutionModelGLCompute:
  369. return true;
  370. case SpvExecutionModelTaskNV:
  371. case SpvExecutionModelMeshNV:
  372. return _.HasCapability(SpvCapabilityMeshShadingNV);
  373. default:
  374. return false;
  375. }
  376. })) {
  377. if (_.HasCapability(SpvCapabilityMeshShadingNV)) {
  378. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  379. << "Execution mode can only be used with a Kernel, GLCompute, "
  380. "MeshNV, or TaskNV execution model.";
  381. } else {
  382. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  383. << "Execution mode can only be used with a Kernel or "
  384. "GLCompute "
  385. "execution model.";
  386. }
  387. }
  388. default:
  389. break;
  390. }
  391. if (spvIsVulkanEnv(_.context()->target_env)) {
  392. if (mode == SpvExecutionModeOriginLowerLeft) {
  393. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  394. << "In the Vulkan environment, the OriginLowerLeft execution mode "
  395. "must not be used.";
  396. }
  397. if (mode == SpvExecutionModePixelCenterInteger) {
  398. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  399. << "In the Vulkan environment, the PixelCenterInteger execution "
  400. "mode must not be used.";
  401. }
  402. }
  403. if (spvIsWebGPUEnv(_.context()->target_env)) {
  404. if (mode != SpvExecutionModeOriginUpperLeft &&
  405. mode != SpvExecutionModeDepthReplacing &&
  406. mode != SpvExecutionModeDepthGreater &&
  407. mode != SpvExecutionModeDepthLess &&
  408. mode != SpvExecutionModeDepthUnchanged &&
  409. mode != SpvExecutionModeLocalSize &&
  410. mode != SpvExecutionModeLocalSizeHint) {
  411. return _.diag(SPV_ERROR_INVALID_DATA, inst)
  412. << "Execution mode must be one of OriginUpperLeft, "
  413. "DepthReplacing, DepthGreater, DepthLess, DepthUnchanged, "
  414. "LocalSize, or LocalSizeHint for WebGPU environment.";
  415. }
  416. }
  417. return SPV_SUCCESS;
  418. }
  419. } // namespace
  420. spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst) {
  421. switch (inst->opcode()) {
  422. case SpvOpEntryPoint:
  423. if (auto error = ValidateEntryPoint(_, inst)) return error;
  424. break;
  425. case SpvOpExecutionMode:
  426. case SpvOpExecutionModeId:
  427. if (auto error = ValidateExecutionMode(_, inst)) return error;
  428. break;
  429. default:
  430. break;
  431. }
  432. return SPV_SUCCESS;
  433. }
  434. } // namespace val
  435. } // namespace spvtools