Bläddra i källkod

shaderpipeline: Implement emulation of samplerCubeShadow

This is not supported until GL 3 so is worth emulating if it is not supported, this is trivial to do
rdb 1 år sedan
förälder
incheckning
e08866e436

+ 1 - 1
panda/src/glstuff/glShaderContext_src.cxx

@@ -2800,7 +2800,7 @@ create_shader(GLuint program, const ShaderModule *module, size_t mi,
         _emulated_caps |= emulate_caps;
 
         SpirVTransformer transformer(spv->_instructions);
-        SpirVEmulateTextureQueriesPass pass;
+        SpirVEmulateTextureQueriesPass pass(emulate_caps);
         transformer.run(pass);
         size_var_ids = std::move(pass._size_var_ids);
         stream = transformer.get_result();

+ 1 - 1
panda/src/shaderpipeline/shaderModuleSpirV.cxx

@@ -471,7 +471,7 @@ ShaderModuleSpirV(Stage stage, std::vector<uint32_t> words, BamCacheRecord *reco
           const ShaderType::SampledImage *sampler = sampler_def._type->as_sampled_image();
           if (sampler != nullptr &&
               sampler->get_texture_type() == Texture::TT_cube_map) {
-            _used_caps |= C_sampler_cube_shadow;
+            _emulatable_caps |= C_sampler_cube_shadow;
           }
         }
       }

+ 44 - 4
panda/src/shaderpipeline/spirVEmulateTextureQueriesPass.cxx

@@ -19,12 +19,22 @@
 bool SpirVEmulateTextureQueriesPass::
 transform_definition_op(Instruction op) {
   switch (op.opcode) {
+  case spv::OpTypeImage:
+    if (_emulate_caps & Shader::C_sampler_cube_shadow) {
+      if (op.args[2] == spv::DimCube && op.args[3] > 0) {
+        op.args[3] = 0;
+      }
+    }
+    break;
+
   case spv::OpVariable:
     if (op.nargs >= 3) {
-      uint32_t var_id = op.args[1];
-      const Definition &var_def = _db.get_definition(var_id);
-      if (var_def._flags & SpirVResultDatabase::DF_queried_image_size_levels) {
-        _access_chains.insert({var_id, AccessChain(var_id)});
+      if (_emulate_caps & (Shader::C_image_query_size | Shader::C_texture_query_size | Shader::C_texture_query_levels)) {
+        uint32_t var_id = op.args[1];
+        const Definition &var_def = _db.get_definition(var_id);
+        if (var_def._flags & SpirVResultDatabase::DF_queried_image_size_levels) {
+          _access_chains.insert({var_id, AccessChain(var_id)});
+        }
       }
       return true;
     }
@@ -89,10 +99,40 @@ transform_function_op(Instruction op) {
     }
     break;
 
+  case spv::OpImageSampleDrefImplicitLod:
+  case spv::OpImageSampleDrefExplicitLod:
+    if (_emulate_caps & Shader::C_sampler_cube_shadow) {
+      if (_float_one_id == 0) {
+        _float_one_id = define_float_constant(1.0f);
+      }
+      if (_float_zero_id == 0) {
+        _float_zero_id = define_float_constant(0.0f);
+      }
+
+      uint32_t sample = op_image_sample(op.args[2], op.args[3], op.nargs >= 6 ? op.args[5] : 0u, op.args + 6);
+      uint32_t depth = op_composite_extract(sample, {0});
+      uint32_t cmp = op_compare(spv::OpFOrdGreaterThan, depth, op.args[4]);
+      push_id(op.args[1]);
+      op_select(cmp, _float_one_id, _float_zero_id);
+      return false;
+    }
+    break;
+
   case spv::OpImageQuerySize:
   case spv::OpImageQuerySizeLod:
+    if ((_emulate_caps & (Shader::C_texture_query_size | Shader::C_image_query_size)) == 0) {
+      return true;
+    }
+    // fall through
+
   case spv::OpImageQueryLevels:
     if (op.nargs >= 3) {
+      if (op.opcode == spv::OpImageQueryLevels) {
+        if ((_emulate_caps & Shader::C_texture_query_levels) == 0) {
+          return true;
+        }
+      }
+
       auto acit = _access_chains.find(op.args[2]);
       if (acit == _access_chains.end()) {
         return true;

+ 8 - 2
panda/src/shaderpipeline/spirVEmulateTextureQueriesPass.h

@@ -17,17 +17,23 @@
 #include "spirVTransformPass.h"
 
 /**
- * Emulates textureSize, imageSize and textureQueryLevels ops.
+ * Emulates textureSize, imageSize and textureQueryLevels ops, as well as depth
+ * comparison samples of a cube map texture.
  */
 class EXPCL_PANDA_SHADERPIPELINE SpirVEmulateTextureQueriesPass final : public SpirVTransformPass {
 public:
-  SpirVEmulateTextureQueriesPass() = default;
+  SpirVEmulateTextureQueriesPass(uint64_t emulate_caps) :
+    _emulate_caps(emulate_caps) {}
 
   virtual bool transform_definition_op(Instruction op);
   virtual bool transform_function_op(Instruction op);
 
 private:
+  const uint64_t _emulate_caps;
+
   pmap<uint32_t, AccessChain> _access_chains;
+  uint32_t _float_one_id = 0;
+  uint32_t _float_zero_id = 0;
 
 public:
   // access chain to size var id

+ 13 - 0
panda/src/shaderpipeline/spirVTransformPass.I

@@ -156,6 +156,19 @@ decorate(uint32_t id, spv::Decoration decoration, uint32_t value) {
   }
 }
 
+/**
+ * Defines a new constant of type float.
+ */
+INLINE uint32_t SpirVTransformPass::
+define_float_constant(float constant) {
+  union {
+    float f;
+    uint32_t ui;
+  } u;
+  u.f = constant;
+  return define_constant(ShaderType::float_type, u.ui);
+}
+
 /**
  * Returns true if the given id has already been defined during this pass.
  */

+ 36 - 0
panda/src/shaderpipeline/spirVTransformPass.cxx

@@ -12,6 +12,7 @@
  */
 
 #include "spirVTransformPass.h"
+#include "pbitops.h"
 
 /**
  *
@@ -1339,6 +1340,41 @@ op_convert(ShaderType::ScalarType new_scalar_type, uint32_t value) {
   return id;
 }
 
+/**
+ * Inserts an OpImageSampleExplicitLod or OpImageSampleImplicitLod, depending
+ * on whether a Lod is included.
+ */
+uint32_t SpirVTransformPass::
+op_image_sample(uint32_t image, uint32_t coord, uint32_t operands, const uint32_t *ids) {
+  uint32_t id = allocate_id();
+
+  const ShaderType::SampledImage *sampled_image = resolve_type(get_type_id(image))->as_sampled_image();
+  nassertr(sampled_image != nullptr, 0u);
+
+  const ShaderType *type = ShaderType::register_type(ShaderType::Vector(sampled_image->get_sampled_type(), 4));
+  uint32_t type_id = define_type(type);
+
+  spv::Op opcode;
+  if (operands & spv::ImageOperandsLodMask) {
+    opcode = spv::OpImageSampleExplicitLod;
+  } else {
+    opcode = spv::OpImageSampleImplicitLod;
+  }
+
+  uint32_t num_ids = ids != nullptr ? ::count_bits_in_word(operands) : 0u;
+  _new_functions.insert(_new_functions.end(), {((num_ids + 6u) << spv::WordCountShift) | opcode, type_id, id, image, coord, operands});
+  if (num_ids > 0) {
+    _new_functions.insert(_new_functions.end(), ids, ids + num_ids);
+  }
+
+  Definition &def = _db.modify_definition(id);
+  def._type_id = type_id;
+  def._type = type;
+
+  mark_defined(id);
+  return id;
+}
+
 /**
  * Inserts an OpKill.
  */

+ 2 - 0
panda/src/shaderpipeline/spirVTransformPass.h

@@ -76,6 +76,7 @@ public:
   uint32_t define_pointer_type(const ShaderType *type, spv::StorageClass storage_class);
   uint32_t define_type(const ShaderType *type);
   uint32_t define_int_constant(int32_t constant);
+  INLINE uint32_t define_float_constant(float constant);
   uint32_t define_null_constant(const ShaderType *type);
   uint32_t define_constant(const ShaderType *type, uint32_t constant);
   uint32_t define_spec_constant(const ShaderType *type, uint32_t def_value);
@@ -133,6 +134,7 @@ protected:
   uint32_t op_composite_extract(uint32_t obj_id, std::initializer_list<uint32_t>);
   uint32_t op_compare(spv::Op opcode, uint32_t obj1, uint32_t obj2);
   uint32_t op_convert(ShaderType::ScalarType to_scalar_type, uint32_t value);
+  uint32_t op_image_sample(uint32_t image, uint32_t coord, uint32_t operands = 0u, const uint32_t *ids = nullptr);
   void op_kill();
 
   uint32_t branch_if(uint32_t cond);

+ 2 - 1
tests/shaderpipeline/test_glsl_caps.py

@@ -312,6 +312,7 @@ def test_glsl_caps_texture_query_size():
 
 
 def test_glsl_caps_sampler_cube_shadow():
+    # Can be emulated, so does not get the cap for now
     assert compile_and_get_caps(Stage.FRAGMENT, """
     #version 330
 
@@ -320,7 +321,7 @@ def test_glsl_caps_sampler_cube_shadow():
     void main() {
         gl_FragColor = vec4(texture(a, vec4(0.0)), 0.0, 0.0, 1.0);
     }
-    """) == Shader.C_shadow_samplers | Shader.C_sampler_cube_shadow
+    """) == Shader.C_shadow_samplers# | Shader.C_sampler_cube_shadow
 
 
 def test_glsl_caps_vertex_id():