HLSL: Add location offsets per resource type
This PR adds the ability to offset sampler, texture, and UBO bindings
from provided base bindings, and to auto-number bindings that are not
provided with explicit register numbers. The mechanism works as
follows:
- Offsets may be given on the command line for all stages, or
individually for one or more single stages, in which case the
offset will be auto-selected according to the stage being
compiled. There is also an API to set them. The new command line
options are --shift-sampler-binding, --shift-texture-binding, and
--shift-UBO-binding.
- Uniforms which are not given explicit bindings in the source code
are auto-numbered if and only if they are in live code as
determined by the algorithm used to build the reflection
database, and the --auto-map-bindings option is given. This auto-numbering
avoids using any binding slots which were explicitly provided in
the code, whether or not that explicit use was live. E.g, "uniform
Texture1D foo : register(t3);" with --shift-texture-binding 10 will
reserve binding 13, whether or not foo is used in live code.
- Shorter synonyms for the command line options are available. See
the --help output.
The testing infrastructure is slightly extended to allow use of the
binding offset API, and two new tests spv.register.(no)autoassign.frag are
added for comparing the resulting SPIR-V.
diff --git a/StandAlone/StandAlone.cpp b/StandAlone/StandAlone.cpp
index 606f247..063e416 100644
--- a/StandAlone/StandAlone.cpp
+++ b/StandAlone/StandAlone.cpp
@@ -1,6 +1,6 @@
//
//Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
-//Copyright (C) 2013 LunarG, Inc.
+//Copyright (C) 2013-2016 LunarG, Inc.
//
//All rights reserved.
//
@@ -48,7 +48,9 @@
#include "../SPIRV/disassemble.h"
#include <cstring>
#include <cstdlib>
+#include <cctype>
#include <cmath>
+#include <array>
#include "../glslang/OSDependent/osinclude.h"
@@ -78,6 +80,7 @@
EOptionOutputHexadecimal = (1 << 16),
EOptionReadHlsl = (1 << 17),
EOptionCascadingErrors = (1 << 18),
+ EOptionAutoMapBindings = (1 << 19),
};
//
@@ -96,7 +99,7 @@
//
// Forward declarations.
//
-EShLanguage FindLanguage(const std::string& name);
+EShLanguage FindLanguage(const std::string& name, bool parseSuffix=true);
void CompileFile(const char* fileName, ShHandle);
void usage();
void FreeFileData(char** data);
@@ -157,6 +160,10 @@
const char* entryPointName = nullptr;
const char* shaderStageName = nullptr;
+std::array<unsigned int, EShLangCount> baseSamplerBinding;
+std::array<unsigned int, EShLangCount> baseTextureBinding;
+std::array<unsigned int, EShLangCount> baseUboBinding;
+
//
// Create the default name for saving a binary if -o is not provided.
//
@@ -205,6 +212,35 @@
}
//
+// Process an optional binding base of the form:
+// --argname [stage] base
+// Where stage is one of the forms accepted by FindLanguage, and base is an integer
+//
+void ProcessBindingBase(int& argc, char**& argv, std::array<unsigned int, EShLangCount>& base)
+{
+ if (argc < 2)
+ usage();
+
+ if (!isdigit(argv[1][0])) {
+ if (argc < 3) // this form needs one more argument
+ usage();
+
+ // Parse form: --argname stage base
+ const EShLanguage lang = FindLanguage(argv[1], false);
+ base[lang] = atoi(argv[2]);
+ argc-= 2;
+ argv+= 2;
+ } else {
+ // Parse form: --argname base
+ for (int lang=0; lang<EShLangCount; ++lang)
+ base[lang] = atoi(argv[1]);
+
+ argc--;
+ argv++;
+ }
+}
+
+//
// Do all command-line argument parsing. This includes building up the work-items
// to be processed later, and saving all the command-line options.
//
@@ -212,6 +248,10 @@
//
void ProcessArguments(int argc, char* argv[])
{
+ baseSamplerBinding.fill(0);
+ baseTextureBinding.fill(0);
+ baseUboBinding.fill(0);
+
ExecutableName = argv[0];
NumWorkItems = argc; // will include some empties where the '-' options were, but it doesn't matter, they'll be 0
Work = new glslang::TWorkItem*[NumWorkItems];
@@ -223,6 +263,33 @@
for (; argc >= 1; argc--, argv++) {
if (argv[0][0] == '-') {
switch (argv[0][1]) {
+ case '-':
+ {
+ std::string lowerword(argv[0]+2);
+ std::transform(lowerword.begin(), lowerword.end(), lowerword.begin(), ::tolower);
+
+ // handle --word style options
+ if (lowerword == "shift-sampler-bindings" || // synonyms
+ lowerword == "shift-sampler-binding" ||
+ lowerword == "ssb") {
+ ProcessBindingBase(argc, argv, baseSamplerBinding);
+ } else if (lowerword == "shift-texture-bindings" || // synonyms
+ lowerword == "shift-texture-binding" ||
+ lowerword == "stb") {
+ ProcessBindingBase(argc, argv, baseTextureBinding);
+ } else if (lowerword == "shift-ubo-bindings" || // synonyms
+ lowerword == "shift-ubo-binding" ||
+ lowerword == "sub") {
+ ProcessBindingBase(argc, argv, baseUboBinding);
+ } else if (lowerword == "auto-map-bindings" || // synonyms
+ lowerword == "auto-map-binding" ||
+ lowerword == "amb") {
+ Options |= EOptionAutoMapBindings;
+ } else {
+ usage();
+ }
+ }
+ break;
case 'H':
Options |= EOptionHumanReadableSpv;
if ((Options & EOptionSpv) == 0) {
@@ -461,6 +528,14 @@
shader->setStringsWithLengthsAndNames(compUnit.text, NULL, compUnit.fileNameList, 1);
if (entryPointName) // HLSL todo: this needs to be tracked per compUnits
shader->setEntryPoint(entryPointName);
+
+ shader->setShiftSamplerBinding(baseSamplerBinding[compUnit.stage]);
+ shader->setShiftTextureBinding(baseTextureBinding[compUnit.stage]);
+ shader->setShiftUboBinding(baseUboBinding[compUnit.stage]);
+
+ if (Options & EOptionAutoMapBindings)
+ shader->setAutoMapBindings(true);
+
shaders.push_back(shader);
const int defaultVersion = Options & EOptionDefaultDesktop? 110: 100;
@@ -506,6 +581,10 @@
PutsIfNonEmpty(program.getInfoDebugLog());
}
+ // Map IO
+ if (Options & EOptionSpv)
+ program.mapIO();
+
// Reflect
if (Options & EOptionDumpReflection) {
program.buildReflection();
@@ -705,15 +784,22 @@
// .frag = fragment
// .comp = compute
//
-EShLanguage FindLanguage(const std::string& name)
+EShLanguage FindLanguage(const std::string& name, bool parseSuffix)
{
- size_t ext = name.rfind('.');
- if (ext == std::string::npos) {
- usage();
- return EShLangVertex;
+ size_t ext = 0;
+
+ // Search for a suffix on a filename: e.g, "myfile.frag". If given
+ // the suffix directly, we skip looking the '.'
+ if (parseSuffix) {
+ ext = name.rfind('.');
+ if (ext == std::string::npos) {
+ usage();
+ return EShLangVertex;
+ }
+ ++ext;
}
- std::string suffix = name.substr(ext + 1, std::string::npos);
+ std::string suffix = name.substr(ext, std::string::npos);
if (shaderStageName)
suffix = shaderStageName;
@@ -831,6 +917,19 @@
" -v print version strings\n"
" -w suppress warnings (except as required by #extension : warn)\n"
" -x save 32-bit hexadecimal numbers as text, requires a binary option (e.g., -V)\n"
+ "\n"
+ " --shift-sampler-binding [stage] num set base binding number for samplers\n"
+ " --ssb [stage] num synonym for --shift-sampler-binding\n"
+ "\n"
+ " --shift-texture-binding [stage] num set base binding number for textures\n"
+ " --stb [stage] num synonym for --shift-texture-binding\n"
+ "\n"
+ " --shift-UBO-binding [stage] num set base binding number for UBOs\n"
+ " --sub [stage] num synonym for --shift-UBO-binding\n"
+ "\n"
+ " --auto-map-bindings automatically bind uniform variables without\n"
+ " explicit bindings.\n"
+ " --amb synonym for --auto-map-bindings\n"
);
exit(EFailUsage);