GenerateShaderVariantListUtil.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. """
  2. Copyright (c) Contributors to the Open 3D Engine Project.
  3. For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. SPDX-License-Identifier: Apache-2.0 OR MIT
  5. """
  6. from PySide2 import QtWidgets
  7. import azlmbr.asset as asset
  8. import azlmbr.atom
  9. import azlmbr.bus
  10. import azlmbr.math as math
  11. import azlmbr.name
  12. import azlmbr.paths
  13. import azlmbr.shader
  14. import azlmbr.shadermanagementconsole
  15. import json
  16. import os
  17. # Make a copy of shaderVariants, update target option value and return copy, accumulate stableId
  18. def updateOptionValue(shaderVariants, targetOptionName, targetValue, stableId):
  19. tempShaderVariants = []
  20. for variantInfo in shaderVariants:
  21. tempVariantInfo = azlmbr.shader.ShaderVariantInfo()
  22. options = {}
  23. for optionName in variantInfo.options:
  24. if targetOptionName == optionName:
  25. options[optionName] = targetValue
  26. else:
  27. options[optionName] = variantInfo.options[optionName]
  28. tempVariantInfo.options = options
  29. tempVariantInfo.stableId = stableId
  30. tempShaderVariants.append(tempVariantInfo)
  31. stableId += 1
  32. return tempShaderVariants, stableId
  33. # alters the option group passed in argument by clearing internal options if predicate passes
  34. def clearOptions(optionGroup, predicateTakingOptionName_clearIfYes):
  35. descriptors = optionGroup.GetShaderOptionDescriptors()
  36. for optDesc in descriptors:
  37. optionName = optDesc.GetName()
  38. if predicateTakingOptionName_clearIfYes(optionName):
  39. optionGroup.ClearValue(optionName)
  40. # Input is one .shader
  41. def create_shadervariantlist_for_shader(filename):
  42. print(f"Creating shader variant list for {filename}")
  43. # Get info such as relative path of the file and asset id
  44. shaderAssetInfo = azlmbr.shadermanagementconsole.ShaderManagementConsoleRequestBus(
  45. azlmbr.bus.Broadcast,
  46. 'GetSourceAssetInfo',
  47. filename
  48. )
  49. # retrieves a list of all material source files that use the shader. Note that materials inherit from materialtype files, which are actual files that refer to shader files.
  50. materialAssetIds = azlmbr.shadermanagementconsole.ShaderManagementConsoleRequestBus(
  51. azlmbr.bus.Broadcast,
  52. 'FindMaterialAssetsUsingShader',
  53. shaderAssetInfo.relativePath
  54. )
  55. print(f"Found {len(materialAssetIds)} material assets referencing shader")
  56. shaderVariantList = azlmbr.shader.ShaderVariantListSourceData()
  57. shaderVariantList.shaderFilePath = shaderAssetInfo.relativePath
  58. if len(materialAssetIds) == 0:
  59. # No material is using this shader, so we can't get ShaderOptionDescriptor in the script
  60. # Return early and handle user assigned system option after shaderAsset is loaded
  61. return shaderVariantList
  62. # This loop collects all uniquely-identified shader items used by the materials based on its shader variant id.
  63. shader_file = os.path.basename(filename)
  64. shaderVariantIds = []
  65. shaderOptionGroups = []
  66. progressDialog = QtWidgets.QProgressDialog(f"Generating .shadervariantlist file for:\n{shader_file}", "Cancel", 0, len(materialAssetIds))
  67. progressDialog.setMaximumWidth(400)
  68. progressDialog.setMaximumHeight(100)
  69. progressDialog.setModal(True)
  70. progressDialog.setWindowTitle("Generating Shader Variant List")
  71. for i, materialAssetId in enumerate(materialAssetIds):
  72. materialInstanceShaderItems = azlmbr.shadermanagementconsole.ShaderManagementConsoleRequestBus(azlmbr.bus.Broadcast, 'GetMaterialInstanceShaderItems', materialAssetId)
  73. for shaderItem in materialInstanceShaderItems:
  74. shaderAssetId = shaderItem.GetShaderAsset().get_id()
  75. if shaderAssetInfo.assetId == shaderAssetId:
  76. shaderVariantId = shaderItem.GetShaderVariantId()
  77. if not shaderVariantId.IsEmpty():
  78. # Check for repeat shader variant ids. We are using a list here
  79. # instead of a set to check for duplicates on shaderVariantIds because
  80. # shaderVariantId is not hashed by the ID like it is in the C++ side.
  81. has_repeat = False
  82. for variantId in shaderVariantIds:
  83. if shaderVariantId == variantId:
  84. has_repeat = True
  85. break
  86. if has_repeat:
  87. continue
  88. shaderVariantIds.append(shaderVariantId)
  89. optionGroup = shaderItem.GetShaderOptionGroup()
  90. # clear the group from spuriously defaulted options by reducing it to a lean set:
  91. clearOptions(optionGroup, lambda optName_Query: not shaderItem.MaterialOwnsShaderOption(optName_Query))
  92. shaderOptionGroups.append(optionGroup)
  93. progressDialog.setValue(i)
  94. # processing events to update UI after progress bar changes
  95. QtWidgets.QApplication.processEvents()
  96. # Allowing the application to process idle events for one frame to update systems and garbage collect graphics resources
  97. azlmbr.atomtools.general.idle_wait_frames(1)
  98. if progressDialog.wasCanceled():
  99. return
  100. progressDialog.close()
  101. # Read from shaderPath.systemoptions to get user assigned system option value
  102. pre, ext = os.path.splitext(filename)
  103. systemOptionFilePath = f'{pre}.systemoptions'
  104. systemOptionFilePath = systemOptionFilePath.replace("\\", "/")
  105. systemOptionDict = {}
  106. if os.path.isfile(systemOptionFilePath):
  107. with open(systemOptionFilePath, "r") as systemOptionFile:
  108. systemOptionDict = json.load(systemOptionFile)
  109. # Generate the shader variant list data by collecting shader option name-value pairs.
  110. shaderVariants = []
  111. systemOptionDescriptor = {}
  112. stableId = 1
  113. for shaderOptionGroup in shaderOptionGroups:
  114. variantInfo = azlmbr.shader.ShaderVariantInfo()
  115. variantInfo.stableId = stableId
  116. options = {}
  117. shaderOptionDescriptors = shaderOptionGroup.GetShaderOptionDescriptors()
  118. for shaderOptionDescriptor in shaderOptionDescriptors:
  119. optionName = shaderOptionDescriptor.GetName()
  120. optionValue = shaderOptionGroup.GetValueByOptionName(optionName)
  121. if not optionValue.IsValid():
  122. continue
  123. valueName = shaderOptionDescriptor.GetValueName(optionValue)
  124. options[optionName] = valueName
  125. # Check user assigned value
  126. optionNameString = optionName.ToString()
  127. if optionNameString in systemOptionDict:
  128. if systemOptionDict[optionNameString] != "":
  129. options[optionName] = azlmbr.name.Name(systemOptionDict[optionNameString])
  130. else:
  131. # Value is unset, expansion handling later
  132. systemOptionDescriptor[optionName] = shaderOptionDescriptor
  133. if len(options) != 0:
  134. variantInfo.options = options
  135. shaderVariants.append(variantInfo)
  136. stableId += 1
  137. shaderVariantList.materialOptionsHint = set(options.keys()) - set(systemOptionDict.keys())
  138. # Expand the unset system option
  139. for systemOptionName in systemOptionDescriptor:
  140. optionDescriptor = systemOptionDescriptor[systemOptionName]
  141. defaultValue = optionDescriptor.GetDefaultValue()
  142. valueMin = optionDescriptor.GetMinValue()
  143. valueMax = optionDescriptor.GetMaxValue()
  144. totalShaderVariants = []
  145. for index in range(valueMin.GetIndex(), valueMax.GetIndex() + 1):
  146. optionValue = optionDescriptor.GetValueNameByIndex(index)
  147. if optionValue != defaultValue:
  148. tempShaderVariants, stableId = updateOptionValue(shaderVariants, systemOptionName, optionValue, stableId)
  149. totalShaderVariants.extend(tempShaderVariants)
  150. shaderVariants.extend(totalShaderVariants)
  151. shaderVariantList.shaderVariants = shaderVariants
  152. return shaderVariantList