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.
This commit is contained in:
steve-lunarg
2016-09-07 15:20:19 -06:00
parent a1e2d4952e
commit 7f7c2ed780
13 changed files with 1191 additions and 10 deletions

View File

@@ -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 @@ enum TOptions {
EOptionOutputHexadecimal = (1 << 16),
EOptionReadHlsl = (1 << 17),
EOptionCascadingErrors = (1 << 18),
EOptionAutoMapBindings = (1 << 19),
};
//
@@ -96,7 +99,7 @@ enum TFailCode {
//
// 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* binaryFileName = nullptr;
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.
//
@@ -204,6 +211,35 @@ void Error(const char* message)
exit(EFailUsage);
}
//
// 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 Error(const char* message)
//
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 @@ void ProcessArguments(int argc, char* argv[])
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 @@ void CompileAndLinkShaderUnits(std::vector<ShaderCompUnit> compUnits)
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 @@ void CompileAndLinkShaderUnits(std::vector<ShaderCompUnit> compUnits)
PutsIfNonEmpty(program.getInfoDebugLog());
}
// Map IO
if (Options & EOptionSpv)
program.mapIO();
// Reflect
if (Options & EOptionDumpReflection) {
program.buildReflection();
@@ -705,15 +784,22 @@ int C_DECL main(int argc, char* argv[])
// .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 @@ void usage()
" -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);