Pārlūkot izejas kodu

Vulkan: validate and parse SPIR-V, match up vertex input interface

rdb 9 gadi atpakaļ
vecāks
revīzija
e138ecd8cc

+ 1 - 1
makepanda/makepanda.py

@@ -3543,7 +3543,7 @@ if (not RUNTIME):
 #
 #
 
 
 if (not RUNTIME):
 if (not RUNTIME):
-  OPTS=['DIR:panda/src/gobj', 'BUILDING:PANDA',  'NVIDIACG', 'ZLIB', 'SQUISH']
+  OPTS=['DIR:panda/src/gobj', 'BUILDING:PANDA',  'NVIDIACG', 'ZLIB', 'SQUISH', 'VULKAN']
   TargetAdd('p3gobj_composite1.obj', opts=OPTS, input='p3gobj_composite1.cxx')
   TargetAdd('p3gobj_composite1.obj', opts=OPTS, input='p3gobj_composite1.cxx')
   TargetAdd('p3gobj_composite2.obj', opts=OPTS, input='p3gobj_composite2.cxx')
   TargetAdd('p3gobj_composite2.obj', opts=OPTS, input='p3gobj_composite2.cxx')
 
 

+ 239 - 0
panda/src/gobj/shader.cxx

@@ -26,6 +26,9 @@
 #include <Cg/cg.h>
 #include <Cg/cg.h>
 #endif
 #endif
 
 
+#include <vulkan/spirv.h>
+#include <stdint.h>
+
 TypeHandle Shader::_type_handle;
 TypeHandle Shader::_type_handle;
 Shader::ShaderTable Shader::_load_table;
 Shader::ShaderTable Shader::_load_table;
 Shader::ShaderTable Shader::_make_table;
 Shader::ShaderTable Shader::_make_table;
@@ -2280,6 +2283,11 @@ do_read_source(string &into, const Filename &fn, BamCacheRecord *record) {
     _last_modified = max(_last_modified, vf->get_timestamp());
     _last_modified = max(_last_modified, vf->get_timestamp());
     _source_files.push_back(vf->get_filename());
     _source_files.push_back(vf->get_filename());
   }
   }
+
+  if (_language == SL_SPIR_V) {
+    return spirv_analyze_shader(into);
+  }
+
   return true;
   return true;
 }
 }
 
 
@@ -2468,6 +2476,237 @@ check_modified() const {
   return false;
   return false;
 }
 }
 
 
+/**
+ * Parses a SPIR-V shader to figure out which parameters it uses.
+ */
+bool Shader::
+spirv_analyze_shader(const string &data) {
+  //const string &data = get_text(type);
+  const uint32_t *words = (const uint32_t *)data.data();
+  const size_t length = data.size() >> 2;
+  const uint32_t *end = words + length;
+
+  if (length < 5) {
+    shader_cat.error()
+      << "Invalid SPIR-V file: too short.\n";
+    return false;
+  }
+
+  if (*words++ != SpvMagicNumber) {
+    shader_cat.error()
+      << "Invalid SPIR-V file: wrong magic number.\n";
+    return false;
+  }
+
+  ++words; // version
+  ++words; // generator
+  uint32_t bound = *words++;
+  ++words; // schema (reserved)
+
+  // Stores information on all of the types and variables encountered.
+  struct ShaderVar {
+    string _name;
+    ShaderArgType _type;
+    int _location;
+  } def_var = {string(), SAT_unknown, -1};
+
+  pvector<ShaderVar> vars(bound, def_var);
+
+  ShaderType shader_type = ST_none;
+
+  while (words < end) {
+    uint16_t wcount = words[0] >> 16;
+    SpvOp opcode = (SpvOp)(words[0] & 0xffff);
+
+    switch (opcode) {
+    case SpvOpMemoryModel:
+      nassertr(wcount == 3, false);
+      if (words[1] != SpvAddressingModelLogical) {
+        shader_cat.error()
+          << "Invalid SPIR-V shader: addressing model Logical must be used.\n";
+        return false;
+      }
+      if (words[2] != SpvMemoryModelGLSL450) {
+        shader_cat.error()
+          << "Invalid SPIR-V shader: memory model GLSL450 must be used.\n";
+        return false;
+      }
+      break;
+
+    case SpvOpEntryPoint:
+      switch ((SpvExecutionModel)words[1]) {
+      case SpvExecutionModelVertex:
+        shader_type = ST_vertex;
+        break;
+      case SpvExecutionModelTessellationControl:
+        shader_type = ST_tess_control;
+        break;
+      case SpvExecutionModelTessellationEvaluation:
+        shader_type = ST_tess_evaluation;
+        break;
+      case SpvExecutionModelGeometry:
+        shader_type = ST_geometry;
+        break;
+      case SpvExecutionModelFragment:
+        shader_type = ST_fragment;
+        break;
+      default:
+        break;
+      }
+      break;
+
+    case SpvOpName:
+      vars[words[1]]._name = string((const char *)&words[2]);
+      break;
+
+    case SpvOpTypeVoid:
+      vars[words[1]]._type = SAT_unknown;
+      break;
+
+    case SpvOpTypeFloat:
+      vars[words[1]]._type = SAT_scalar;
+      break;
+
+    case SpvOpTypeVector:
+      vars[words[1]]._type = (ShaderArgType)(SAT_vec1 + (words[3] - 1));
+      break;
+
+    case SpvOpTypeMatrix:
+      vars[words[1]]._type = (ShaderArgType)(SAT_mat1x1 + (words[3] - 1) * 5);
+      break;
+
+    case SpvOpTypePointer:
+      // A type and type pointer isn't the same, but this is how types of
+      // variables are referenced, and it's convenient to do this since
+      // the "Logical" address model doesn't support using pointers anyway.
+      vars[words[1]]._type = vars[words[3]]._type;
+      break;
+
+    case SpvOpTypeImage:
+      switch ((SpvDim)words[3]) {
+      case SpvDim1D:
+        vars[words[1]]._type = SAT_sampler1d;
+        break;
+      case SpvDim2D:
+        if (words[5]) {
+          vars[words[1]]._type = SAT_sampler2d_array;
+        } else {
+          vars[words[1]]._type = SAT_sampler2d;
+        }
+        break;
+      case SpvDim3D:
+        vars[words[1]]._type = SAT_sampler3d;
+        break;
+      case SpvDimCube:
+        if (words[5]) {
+          vars[words[1]]._type = SAT_sampler_cube_array;
+        } else {
+          vars[words[1]]._type = SAT_sampler_cube;
+        }
+        break;
+      case SpvDimRect:
+        shader_cat.error()
+          << "imageRect shader inputs are not supported.\n";
+        return false;
+      case SpvDimBuffer:
+        vars[words[1]]._type = SAT_sampler_buffer;
+        break;
+      case SpvDimSubpassData:
+        shader_cat.error()
+          << "subpassInput shader inputs are not supported.\n";
+        return false;
+      }
+      break;
+
+    case SpvOpTypeSampler:
+      // A sampler that's not bound to a particular image.
+      vars[words[1]]._type = SAT_sampler;
+      break;
+
+    case SpvOpTypeSampledImage:
+      // We don't currently distinguish between samplers and images.
+      vars[words[1]]._type = vars[words[2]]._type;
+      break;
+
+    case SpvOpVariable:
+      // A variable definition - check the storage class.
+      switch ((SpvStorageClass)words[3]) {
+      case SpvStorageClassUniformConstant:
+        {
+          if (vars[words[1]]._type < SAT_sampler1d) {
+            break;
+          }
+          const ShaderVar &var = vars[words[2]];
+          ShaderTexSpec spec;
+          spec._id._name = var._name;
+          spec._id._type = shader_type;
+          spec._id._seqno = var._location;
+          spec._part = STO_named_input;
+          spec._stage = -1;
+          spec._desired_type = -1;
+          _tex_spec.push_back(spec);
+        }
+        break;
+      case SpvStorageClassInput:
+        if (shader_type == ST_vertex) {
+          const ShaderVar &var = vars[words[2]];
+          if (var._name.empty()) {
+            shader_cat.error()
+              << "Shader vertex input requires a name.\n";
+            break;
+          }
+          if (var._location < 0) {
+            //TODO: just jam one in here somewhere?
+            shader_cat.error()
+              << "Shader vertex input requires location decoration.\n";
+            break;
+          }
+          ShaderVarSpec spec;
+          spec._id._name = var._name;
+          spec._id._type = shader_type;
+          spec._id._seqno = var._location;
+          spec._name = InternalName::make(var._name);
+          spec._append_uv = false;
+          spec._elements = 1;
+          spec._integer = false;
+          _var_spec.push_back(spec);
+        }
+        break;
+      case SpvStorageClassUniform:
+      case SpvStorageClassOutput:
+      case SpvStorageClassWorkgroup:
+      case SpvStorageClassCrossWorkgroup:
+      case SpvStorageClassPrivate:
+      case SpvStorageClassFunction:
+      case SpvStorageClassGeneric:
+      case SpvStorageClassPushConstant:
+      case SpvStorageClassAtomicCounter:
+      case SpvStorageClassImage:
+        break;
+      }
+      break;
+
+    case SpvOpDecorate:
+      // Use the same field to store location/binding, since I don't believe
+      // that variables can specify both.
+      if (words[2] == SpvDecorationLocation || words[2] == SpvDecorationBinding) {
+        vars[words[1]]._location = words[3];
+      }
+      break;
+    }
+
+    words += wcount;
+  }
+
+  if (shader_type == ST_none) {
+    shader_cat.error()
+      << "No valid entry point found in SPIR-V shader.\n";
+    return false;
+  }
+
+  return true;
+}
+
 #ifdef HAVE_CG
 #ifdef HAVE_CG
 /**
 /**
  * Determines the appropriate active shader profile settings based on any
  * Determines the appropriate active shader profile settings based on any

+ 4 - 1
panda/src/gobj/shader.h

@@ -225,6 +225,7 @@ public:
   };
   };
 
 
   enum ShaderArgType {
   enum ShaderArgType {
+    SAT_unknown,
     SAT_scalar,
     SAT_scalar,
     SAT_vec1,
     SAT_vec1,
     SAT_vec2,
     SAT_vec2,
@@ -246,6 +247,7 @@ public:
     SAT_mat4x2,
     SAT_mat4x2,
     SAT_mat4x3,
     SAT_mat4x3,
     SAT_mat4x4,
     SAT_mat4x4,
+    SAT_sampler,
     SAT_sampler1d,
     SAT_sampler1d,
     SAT_sampler2d,
     SAT_sampler2d,
     SAT_sampler3d,
     SAT_sampler3d,
@@ -253,7 +255,6 @@ public:
     SAT_sampler_cube,
     SAT_sampler_cube,
     SAT_sampler_buffer,
     SAT_sampler_buffer,
     SAT_sampler_cube_array,
     SAT_sampler_cube_array,
-    SAT_unknown
 };
 };
 
 
   enum ShaderArgDir {
   enum ShaderArgDir {
@@ -517,6 +518,8 @@ public:
   bool get_compiled(unsigned int &format, string &binary) const;
   bool get_compiled(unsigned int &format, string &binary) const;
 
 
 private:
 private:
+  bool spirv_analyze_shader(const string &data);
+
 #ifdef HAVE_CG
 #ifdef HAVE_CG
   ShaderArgClass cg_parameter_class(CGparameter p);
   ShaderArgClass cg_parameter_class(CGparameter p);
   ShaderArgType cg_parameter_type(CGparameter p);
   ShaderArgType cg_parameter_type(CGparameter p);

+ 20 - 6
panda/src/vulkandisplay/vulkanGraphicsStateGuardian.cxx

@@ -1682,13 +1682,26 @@ make_pipeline(const RenderState *state, const GeomVertexFormat *format,
   }
   }
 
 
   // Now describe each vertex attribute (ie. GeomVertexColumn).
   // Now describe each vertex attribute (ie. GeomVertexColumn).
+  const Shader *shader = _default_sc->get_shader();
+  pvector<Shader::ShaderVarSpec>::const_iterator it;
+
   VkVertexInputAttributeDescription *attrib_desc = (VkVertexInputAttributeDescription *)
   VkVertexInputAttributeDescription *attrib_desc = (VkVertexInputAttributeDescription *)
-    alloca(sizeof(VkVertexInputAttributeDescription) * format->get_num_columns());
+    alloca(sizeof(VkVertexInputAttributeDescription) * shader->_var_spec.size());
+  size_t i = 0;
+
+  for (it = shader->_var_spec.begin(); it != shader->_var_spec.end(); ++it) {
+    const Shader::ShaderVarSpec &spec = *it;
+    int array_index;
+    const GeomVertexColumn *column;
+
+    if (!format->get_array_info(spec._name, array_index, column)) {
+      vulkandisplay_cat.error()
+        << "Shader references non-existent vertex column " << *spec._name << "\n";
+      continue;
+    }
 
 
-  for (size_t i = 0; i < format->get_num_columns(); ++i) {
-    const GeomVertexColumn *column = format->get_column(i);
-    attrib_desc[i].location = i;
-    attrib_desc[i].binding = format->get_array_with(i);
+    attrib_desc[i].location = spec._id._seqno;
+    attrib_desc[i].binding = array_index;
     attrib_desc[i].offset = column->get_start();
     attrib_desc[i].offset = column->get_start();
 
 
     // Determine which Vulkan format to map this column to.  The formats are
     // Determine which Vulkan format to map this column to.  The formats are
@@ -1738,6 +1751,7 @@ make_pipeline(const RenderState *state, const GeomVertexFormat *format,
       attrib_desc[i].format = VK_FORMAT_B10G11R11_UFLOAT_PACK32;
       attrib_desc[i].format = VK_FORMAT_B10G11R11_UFLOAT_PACK32;
       break;
       break;
     }
     }
+    ++i;
   }
   }
 
 
   VkPipelineVertexInputStateCreateInfo vertex_info;
   VkPipelineVertexInputStateCreateInfo vertex_info;
@@ -1746,7 +1760,7 @@ make_pipeline(const RenderState *state, const GeomVertexFormat *format,
   vertex_info.flags = 0;
   vertex_info.flags = 0;
   vertex_info.vertexBindingDescriptionCount = format->get_num_arrays();
   vertex_info.vertexBindingDescriptionCount = format->get_num_arrays();
   vertex_info.pVertexBindingDescriptions = binding_desc;
   vertex_info.pVertexBindingDescriptions = binding_desc;
-  vertex_info.vertexAttributeDescriptionCount = format->get_num_columns();
+  vertex_info.vertexAttributeDescriptionCount = i;
   vertex_info.pVertexAttributeDescriptions = attrib_desc;
   vertex_info.pVertexAttributeDescriptions = attrib_desc;
 
 
   VkPipelineInputAssemblyStateCreateInfo assembly_info;
   VkPipelineInputAssemblyStateCreateInfo assembly_info;