Browse Source

shaderpipeline: cleanup of glslang front-end, flesh out Cg preamble

Support for cube maps is commented out for now pending KhronosGroup/glslang#2265
rdb 5 years ago
parent
commit
f92f074663

+ 6 - 0
makepanda/makepanda.py

@@ -3723,6 +3723,11 @@ PyTargetAdd('p3display_ext_composite.obj', opts=OPTS, input='p3display_ext_compo
 OPTS=['DIR:panda/src/shaderpipeline', 'BUILDING:PANDA', 'GLSLANG', 'SPIRV-TOOLS']
 TargetAdd('p3shaderpipeline_composite1.obj', opts=OPTS, input='p3shaderpipeline_composite1.cxx')
 
+cg_preamble = WriteEmbeddedStringFile('cg_preamble', inputs=[
+    'panda/src/shaderpipeline/cg_preamble.hlsl',
+])
+TargetAdd('p3shaderpipeline_cg_preamble.obj', opts=OPTS, input=cg_preamble)
+
 OPTS=['DIR:panda/src/shaderpipeline']
 IGATEFILES=GetDirectoryContents('panda/src/shaderpipeline', ["*.h", "*_composite*.cxx"])
 TargetAdd('libp3shaderpipeline.in', opts=OPTS, input=IGATEFILES)
@@ -3927,6 +3932,7 @@ TargetAdd('libpanda.dll', input='p3pgraph_composite4.obj')
 TargetAdd('libpanda.dll', input='p3cull_composite1.obj')
 TargetAdd('libpanda.dll', input='p3cull_composite2.obj')
 TargetAdd('libpanda.dll', input='p3shaderpipeline_composite1.obj')
+TargetAdd('libpanda.dll', input='p3shaderpipeline_cg_preamble.obj')
 TargetAdd('libpanda.dll', input='p3movies_composite1.obj')
 TargetAdd('libpanda.dll', input='p3grutil_multitexReducer.obj')
 TargetAdd('libpanda.dll', input='p3grutil_composite1.obj')

+ 132 - 0
panda/src/shaderpipeline/cg_preamble.hlsl

@@ -0,0 +1,132 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file cg_preamble.hlsl
+ * @author rdb
+ * @date 2020-06-10
+ */
+
+/**
+ * This file gets loaded as preamble to any Cg shader to be able to compile it
+ * through the HLSL front-end of the glslang compiler.  It defines functions
+ * and types that were supported in Cg but are not available in HLSL.
+ */
+
+typedef sampler2D sampler2DShadow;
+
+float4 shadow2D(sampler2D samp, float3 s) {
+  return tex2D(samp, s.xy).r > s.z;
+}
+
+float4 shadow2DProj(sampler2D samp, float4 s) {
+  return tex2Dproj(samp, s).r > s.z / s.w;
+}
+
+float4 f4tex1D(sampler1D samp, float s) {
+  return tex1D(samp, s);
+}
+
+float4 f4tex1D(sampler1D samp, float2 s) {
+  return tex1D(samp, s.x).r > s.y;
+}
+
+float4 f4tex1D(sampler1D samp, float s, float dx, float dy) {
+  return tex1D(samp, s, dx, dy);
+}
+
+float4 f4tex1D(sampler1D samp, float2 s, float dx, float dy) {
+  return tex1D(samp, s.x, dx, dy).r > s.y;
+}
+
+float4 f4tex1Dproj(sampler1D samp, float2 s) {
+  return tex1Dproj(samp, s.xxxy);
+}
+
+float4 f4tex1Dproj(sampler1D samp, float3 s) {
+  return tex1Dproj(samp, s.xxxz).r > s.y / s.z;
+}
+
+float4 f4tex1Dproj(sampler1D samp, float2 s, float dx, float dy) {
+  return tex1D(samp, s.x / s.y, dx, dy);
+}
+
+float4 f4tex1Dproj(sampler1D samp, float3 s, float dx, float dy) {
+  return tex1D(samp, s.x / s.z).r > s.y / s.z;
+}
+
+float4 f4tex2D(sampler2D samp, float2 s) {
+  return tex2D(samp, s.xy);
+}
+
+float4 f4tex2D(sampler2D samp, float3 s) {
+  return tex2D(samp, s.xy).r > s.z;
+}
+
+float4 f4tex2D(sampler2D samp, float2 s, float2 dx, float2 dy) {
+  return tex2D(samp, s.xy, dx, dy);
+}
+
+float4 f4tex2D(sampler2D samp, float3 s, float2 dx, float2 dy) {
+  return tex2D(samp, s.xy, dx, dy).r > s.z;
+}
+
+float4 f4tex2Dproj(sampler2D samp, float3 s) {
+  return tex2Dproj(samp, s.xyzz);
+}
+
+float4 f4tex2Dproj(sampler2D samp, float4 s) {
+  return tex2Dproj(samp, s).r > s.z / s.w;
+}
+
+float4 f4tex2Dproj(sampler2D samp, float3 s, float2 dx, float2 dy) {
+  return tex2D(samp, s.xyyz, dx, dy);
+}
+
+float4 f4tex2Dproj(sampler2D samp, float4 s, float2 dx, float2 dy) {
+  return tex2D(samp, s.xy / s.w, dx, dy).r > s.z / s.w;
+}
+
+/*
+float4 f4texCUBE(samplerCUBE samp, float3 s) {
+  return texCUBE(samp, s);
+}
+
+float4 f4texCUBE(samplerCUBE samp, float4 s) {
+  return texCUBE(samp, s.xyz).r > s.w;
+}
+
+float4 f4texCUBE(samplerCUBE samp, float3 s, float3 dx, float3 dy) {
+  return texCUBE(samp, s, dx, dy);
+}
+
+float4 f4texCUBE(samplerCUBE samp, float4 s, float3 dx, float3 dy) {
+  return texCUBE(samp, s.xyz, dx, dy).r > s.w;
+}
+*/
+
+#define f1tex1D(x, y) (f4tex1D((x), (y)).r)
+#define f3tex1D(x, y) (f4tex1D((x), (y)).rgb)
+#define tex1D f4tex1D
+
+#define f1tex1Dproj(x, y) (f4tex1Dproj((x), (y)).r)
+#define f3tex1Dproj(x, y) (f4tex1Dproj((x), (y)).rgb)
+#define tex1Dproj f4tex1Dproj
+
+#define f1tex2D(x, y) (f4tex2D((x), (y)).r)
+#define f3tex2D(x, y) (f4tex2D((x), (y)).rgb)
+#define tex2D f4tex2D
+
+#define f1tex2Dproj(x, y) (f4tex2Dproj((x), (y)).r)
+#define f3tex2Dproj(x, y) (f4tex2Dproj((x), (y)).rgb)
+#define tex2Dproj f4tex2Dproj
+
+/*
+#define f1texCUBE(x, y) (f4texCUBE((x), (y)).r)
+#define f3texCUBE(x, y) (f4texCUBE((x), (y)).rgb)
+#define texCUBE f4texCUBE
+*/

+ 79 - 55
panda/src/shaderpipeline/shaderCompilerGlslang.cxx

@@ -285,7 +285,8 @@ compile_now(ShaderModule::Stage stage, std::istream &in,
   int glsl_version = 110;
 
   // Is this a Cg shader or a GLSL shader?
-  if (code.size() >= 5 && strncmp((const char *)&code[0], "//Cg\n", 5) == 0) {
+  if (code.size() >= 5 &&
+      strncmp((const char *)&code[0], "//Cg", 4) == 0 && isspace(code[4])) {
     is_cg = true;
   }
   else if (!preprocess_glsl(code, glsl_version, add_include_directive)) {
@@ -298,94 +299,123 @@ compile_now(ShaderModule::Stage stage, std::istream &in,
     is_initialized = true;
   }
 
+  EShMessages messages = (EShMessages)(EShMsgDefault | EShMsgSpvRules);
+  EShLanguage language;
+  switch (stage) {
+  case ShaderModule::Stage::vertex:
+    language = EShLangVertex;
+    break;
+  case ShaderModule::Stage::tess_control:
+    language = EShLangTessControl;
+    break;
+  case ShaderModule::Stage::tess_evaluation:
+    language = EShLangTessEvaluation;
+    break;
+  case ShaderModule::Stage::geometry:
+    language = EShLangGeometry;
+    break;
+  case ShaderModule::Stage::fragment:
+    language = EShLangFragment;
+    break;
+  default:
+    shader_cat.error()
+      << "glslang compiler does not support " << stage << " shaders.\n";
+    return nullptr;
+  }
+
+  glslang::TShader shader(language);
+
   const char *string = (const char *)code.data();
   const int length = (int)code.size();
   const char *fname = filename.c_str();
-
-  EShMessages messages = (EShMessages)(EShMsgDefault | EShMsgSpvRules);
-
-  glslang::TShader *shader = new glslang::TShader((EShLanguage)stage);
-  shader->setStringsWithLengthsAndNames(&string, &length, &fname, 1);
-  shader->setEntryPoint("main");
+  shader.setStringsWithLengthsAndNames(&string, &length, &fname, 1);
+  shader.setEntryPoint("main");
 
   // If it's marked as a Cg shader, we compile it with the HLSL front-end.
   if (is_cg) {
-    shader->setEnvInput(glslang::EShSource::EShSourceHlsl, (EShLanguage)stage, glslang::EShClient::EShClientOpenGL, 120);
+    messages = (EShMessages)(messages | EShMsgHlslDX9Compatible | EShMsgHlslLegalization);
+
+    const char *source_entry_point;
     switch (stage) {
     case ShaderModule::Stage::vertex:
-      shader->setSourceEntryPoint("vshader");
+      source_entry_point = "vshader";
       break;
     case ShaderModule::Stage::geometry:
-      shader->setSourceEntryPoint("gshader");
+      source_entry_point = "gshader";
       break;
     case ShaderModule::Stage::fragment:
-      shader->setSourceEntryPoint("fshader");
+      source_entry_point = "fshader";
       break;
     default:
       shader_cat.error()
         << "Cg does not support " << stage << " shaders.\n";
       return nullptr;
     }
+    shader.setSourceEntryPoint(source_entry_point);
 
-    shader->setPreamble(
-      "#define f1tex2D(x, y) (tex2D(x, y).r)\n"
-      "#define sampler2DShadow sampler2D\n"
-      "#define shadow2D(s, tc) (float4(tex2D(s, tc) > tc.z))\n"
-      "#define shadow2DProj(s, tc) (float4(tex2Dproj(s, tc) > tc.z / tc.w))\n"
-    );
-    messages = (EShMessages)(messages | EShMsgHlslDX9Compatible | EShMsgHlslLegalization);
+    // Generate a special preamble to define some functions that Cg defines but
+    // HLSL doesn't.  This is sourced from cg_preamble.hlsl.
+    extern const char cg_preamble[];
+    shader.setPreamble(cg_preamble);
+
+    shader.setEnvInput(glslang::EShSource::EShSourceHlsl, (EShLanguage)stage, glslang::EShClient::EShClientOpenGL, 120);
   } else {
-    shader->setEnvInput(glslang::EShSource::EShSourceGlsl, (EShLanguage)stage, glslang::EShClient::EShClientOpenGL, 450);
+    shader.setEnvInput(glslang::EShSource::EShSourceGlsl, (EShLanguage)stage, glslang::EShClient::EShClientOpenGL, 450);
 
     if (add_include_directive) {
-      shader->setPreamble(
+      shader.setPreamble(
         "#extension GL_GOOGLE_include_directive : require\n"
       );
     }
   }
-  shader->setEnvClient(glslang::EShClient::EShClientOpenGL, glslang::EShTargetOpenGL_450);
-  shader->setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_0);
+  shader.setEnvClient(glslang::EShClient::EShClientOpenGL, glslang::EShTargetOpenGL_450);
+  shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_0);
 
   // This will squelch the warnings about missing bindings and locations, since
   // we can assign those ourselves.
-  shader->setAutoMapBindings(true);
-  shader->setAutoMapLocations(true);
+  shader.setAutoMapBindings(true);
+  shader.setAutoMapLocations(true);
 
   Includer includer(record);
-  if (!shader->parse(&resource_limits, 110, false, messages, includer)) {
-    std::cerr << "failed to parse " << filename << ":\n";
-    std::cerr << shader->getInfoLog() << "\n";
-    std::cerr << shader->getInfoDebugLog() << "\n";
+  if (!shader.parse(&resource_limits, 110, false, messages, includer)) {
+    shader_cat.error()
+      << "Failed to parse " << filename << ":\n"
+      << shader.getInfoLog();
     return nullptr;
   }
 
   // I don't know why we need to pretend to link it into a program.  One would
   // think one can just do shader->getIntermediate() and convert that to SPIR-V,
   // but that generates shaders that are ever-so-subtly wrong.
-  glslang::TProgram program;
-  program.addShader(shader);
+  ShaderModuleSpirV::InstructionStream stream;
+  {
+    glslang::TProgram program;
+    program.addShader(&shader);
+    if (!program.link(messages)) {
+      shader_cat.error()
+        << "Failed to link " << filename << "\n";
+      return nullptr;
+    }
 
-  if (!program.link(messages)) {
-    std::cerr << "failed to link " << filename << "\n";
-    return nullptr;
-  }
+    glslang::TIntermediate *ir = program.getIntermediate((EShLanguage)stage);
+    nassertr(ir != nullptr, nullptr);
 
-  glslang::TIntermediate *ir = program.getIntermediate((EShLanguage)stage);
-  if (!ir) {
-    std::cerr << "failed to obtain IR " << filename << ":\n";
-    return nullptr;
-  }
+    spv::SpvBuildLogger logger;
+    glslang::SpvOptions spvOptions;
+    spvOptions.generateDebugInfo = true;
+    spvOptions.disableOptimizer = false;
+    spvOptions.optimizeSize = false;
+    spvOptions.disassemble = false;
+    spvOptions.validate = true;
+    glslang::GlslangToSpv(*ir, stream, &logger, &spvOptions);
 
-  ShaderModuleSpirV::InstructionStream stream;
-  std::string warningsErrors;
-  spv::SpvBuildLogger logger;
-  glslang::SpvOptions spvOptions;
-  spvOptions.generateDebugInfo = true;
-  spvOptions.disableOptimizer = false;
-  spvOptions.optimizeSize = false;
-  spvOptions.disassemble = false;
-  spvOptions.validate = true;
-  glslang::GlslangToSpv(*ir, stream, &logger, &spvOptions);
+    std::string messages = logger.getAllMessages();
+    if (!messages.empty()) {
+      shader_cat.warning()
+        << "Compilation to SPIR-V produced the following messages:\n"
+        << messages;
+    }
+  }
 
   if (!stream.validate_header()) {
     return nullptr;
@@ -396,12 +426,6 @@ compile_now(ShaderModule::Stage stage, std::istream &in,
     return nullptr;
   }
 
-  printf("%s", logger.getAllMessages().c_str());
-  /*if (Options & EOptionOutputHexadecimal) {
-  } else {
-      glslang::OutputSpvBin(spirv, GetBinaryName((EShLanguage)stage));
-  }*/
-
   // Run it through the optimizer.
   spvtools::Optimizer opt(SPV_ENV_UNIVERSAL_1_0);
   opt.SetMessageConsumer(log_message);