[spirv] Add support for VK_EXT_scalar_block_layout (#1716)

Ref: https://github.com/KhronosGroup/Vulkan-Docs/issues/854
This commit is contained in:
Lei Zhang 2018-11-20 11:24:57 -05:00 коммит произвёл GitHub
Родитель e9dac9bd79
Коммит 7a2a95e36c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
13 изменённых файлов: 170 добавлений и 40 удалений

Просмотреть файл

@ -803,7 +803,7 @@ To know more about the Vulkan buffer types, please refer to the Vulkan spec
Memory layout rules
~~~~~~~~~~~~~~~~~~~
SPIR-V CodeGen supports three sets of memory layout rules for buffer resources
SPIR-V CodeGen supports four sets of memory layout rules for buffer resources
right now:
1. Vector-relaxed OpenGL ``std140`` for uniform buffers and vector-relaxed
@ -817,6 +817,14 @@ right now:
3. Strict OpenGL ``std140`` for uniform buffers and strict OpenGL ``std430``
for storage buffers: they allow packing data on the application side that
can be shared with OpenGL. They can be enabled by ``-fvk-use-gl-layout``.
4. Scalar layout rules introduced via `VK_EXT_scalar_block_layout`, which
basically aligns all aggregrate types according to their elements'
natural alignment. They can be enabled by ``-fvk-use-scalar-layout``.
To use scalar layout, the application side need to request
``VK_EXT_scalar_block_layout``. This is also true for using DirectX memory
layout since there is no dedicated DirectX layout extension for Vulkan
(at least for now). So we must request something more permissive.
In the above, "vector-relaxed OpenGL ``std140``/``std430``" rules mean OpenGL
``std140``/``std430`` rules with the following modification for vector type
@ -846,19 +854,19 @@ As an exmaple, for the following HLSL definition:
We will have the following offsets for each member:
============== ====== ====== ====== ====== ====== ======
HLSL Uniform Buffer Storage Buffer
-------------- -------------------- --------------------
Member 1 (VK) 2 (DX) 3 (GL) 1 (VK) 2 (DX) 3 (GL)
============== ====== ====== ====== ====== ====== ======
``a_float`` 0 0 0 0 0 0
``b_float3`` 4 4 16 4 4 16
``c_S_float3`` 16 16 32 16 16 32
``d_float2x3`` 32 32 48 32 28 48
``e_float2x3`` 80 80 96 64 52 80
``f_int_3`` 112 112 128 96 76 112
``g_float2_2`` 160 160 176 112 88 128
============== ====== ====== ====== ====== ====== ======
============== ====== ====== ====== ========== ====== ====== ====== ==========
HLSL Uniform Buffer Storage Buffer
-------------- ------------------------------- -------------------------------
Member 1 (VK) 2 (DX) 3 (GL) 4 (Scalar) 1 (VK) 2 (DX) 3 (GL) 4 (Scalar)
============== ====== ====== ====== ========== ====== ====== ====== ==========
``a_float`` 0 0 0 0 0 0 0 0
``b_float3`` 4 4 16 4 4 4 16 4
``c_S_float3`` 16 16 32 16 16 16 32 16
``d_float2x3`` 32 32 48 28 32 28 48 28
``e_float2x3`` 80 80 96 52 64 52 80 52
``f_int_3`` 112 112 128 76 96 76 112 76
``g_float2_2`` 160 160 176 88 112 88 128 88
============== ====== ====== ====== ========== ====== ====== ====== ==========
If the above layout rules do not satisfy your needs and you want to manually
control the layout of struct members, you can use either

Просмотреть файл

@ -263,6 +263,8 @@ def fvk_use_gl_layout: Flag<["-"], "fvk-use-gl-layout">, Group<spirv_Group>, Fla
HelpText<"Use strict OpenGL std140/std430 memory layout for Vulkan resources">;
def fvk_use_dx_layout: Flag<["-"], "fvk-use-dx-layout">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
HelpText<"Use DirectX memory layout for Vulkan resources">;
def fvk_use_scalar_layout: Flag<["-"], "fvk-use-scalar-layout">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
HelpText<"Use scalar memory layout for Vulkan resources">;
def fspv_reflect: Flag<["-"], "fspv-reflect">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,
HelpText<"Emit additional SPIR-V instructions to aid reflection">;
def fspv_debug_EQ : Joined<["-"], "fspv-debug=">, Group<spirv_Group>, Flags<[CoreOption, DriverOption]>,

Просмотреть файл

@ -31,6 +31,7 @@ enum class SpirvLayoutRule {
RelaxedGLSLStd430, // std430 with relaxed vector layout
FxcCTBuffer, // fxc.exe layout rule for cbuffer/tbuffer
FxcSBuffer, // fxc.exe layout rule for structured buffers
Scalar, // VK_EXT_scalar_block_layout
};
struct SpirvCodeGenOptions {
@ -50,6 +51,7 @@ struct SpirvCodeGenOptions {
bool noWarnIgnoredFeatures;
bool useDxLayout;
bool useGlLayout;
bool useScalarLayout;
SpirvLayoutRule cBufferLayoutRule;
SpirvLayoutRule sBufferLayoutRule;
SpirvLayoutRule tBufferLayoutRule;

Просмотреть файл

@ -636,6 +636,7 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
opts.SpirvOptions.invertW = Args.hasFlag(OPT_fvk_use_dx_position_w, OPT_INVALID, false);
opts.SpirvOptions.useGlLayout = Args.hasFlag(OPT_fvk_use_gl_layout, OPT_INVALID, false);
opts.SpirvOptions.useDxLayout = Args.hasFlag(OPT_fvk_use_dx_layout, OPT_INVALID, false);
opts.SpirvOptions.useScalarLayout = Args.hasFlag(OPT_fvk_use_scalar_layout, OPT_INVALID, false);
opts.SpirvOptions.enableReflect = Args.hasFlag(OPT_fspv_reflect, OPT_INVALID, false);
opts.SpirvOptions.noWarnIgnoredFeatures = Args.hasFlag(OPT_Wno_vk_ignored_features, OPT_INVALID, false);
opts.SpirvOptions.noWarnEmulatedFeatures = Args.hasFlag(OPT_Wno_vk_emulated_features, OPT_INVALID, false);
@ -711,6 +712,7 @@ int ReadDxcOpts(const OptTable *optionTable, unsigned flagsToInclude,
Args.hasFlag(OPT_fvk_use_dx_position_w, OPT_INVALID, false) ||
Args.hasFlag(OPT_fvk_use_gl_layout, OPT_INVALID, false) ||
Args.hasFlag(OPT_fvk_use_dx_layout, OPT_INVALID, false) ||
Args.hasFlag(OPT_fvk_use_scalar_layout, OPT_INVALID, false) ||
Args.hasFlag(OPT_fspv_reflect, OPT_INVALID, false) ||
Args.hasFlag(OPT_Wno_vk_ignored_features, OPT_INVALID, false) ||
Args.hasFlag(OPT_Wno_vk_emulated_features, OPT_INVALID, false) ||

Просмотреть файл

@ -242,9 +242,9 @@ bool spirvToolsOptimize(spv_target_env env, std::vector<uint32_t> *module,
return optimizer.Run(module->data(), module->size(), module, options);
}
bool spirvToolsValidate(spv_target_env env, std::vector<uint32_t> *module,
std::string *messages, bool relaxLogicalPointer,
bool glLayout, bool dxLayout) {
bool spirvToolsValidate(spv_target_env env, const SpirvCodeGenOptions &opts,
bool relaxLogicalPointer, std::vector<uint32_t> *module,
std::string *messages) {
spvtools::SpirvTools tools(env);
tools.SetMessageConsumer(
@ -257,8 +257,13 @@ bool spirvToolsValidate(spv_target_env env, std::vector<uint32_t> *module,
// GL: strict block layout rules
// VK: relaxed block layout rules
// DX: Skip block layout rules
options.SetRelaxBlockLayout(!glLayout && !dxLayout);
options.SetSkipBlockLayout(dxLayout);
if (opts.useScalarLayout || opts.useDxLayout) {
options.SetSkipBlockLayout(true);
} else if (opts.useGlLayout) {
// spirv-val by default checks this.
} else {
options.SetRelaxBlockLayout(true);
}
return tools.Validate(module->data(), module->size(), options);
}
@ -625,6 +630,10 @@ SPIRVEmitter::SPIRVEmitter(CompilerInstance &ci)
spirvOptions.cBufferLayoutRule = SpirvLayoutRule::GLSLStd140;
spirvOptions.tBufferLayoutRule = SpirvLayoutRule::GLSLStd430;
spirvOptions.sBufferLayoutRule = SpirvLayoutRule::GLSLStd430;
} else if (spirvOptions.useScalarLayout) {
spirvOptions.cBufferLayoutRule = SpirvLayoutRule::Scalar;
spirvOptions.tBufferLayoutRule = SpirvLayoutRule::Scalar;
spirvOptions.sBufferLayoutRule = SpirvLayoutRule::Scalar;
} else {
spirvOptions.cBufferLayoutRule = SpirvLayoutRule::RelaxedGLSLStd140;
spirvOptions.tBufferLayoutRule = SpirvLayoutRule::RelaxedGLSLStd430;
@ -736,9 +745,9 @@ void SPIRVEmitter::HandleTranslationUnit(ASTContext &context) {
// Validate the generated SPIR-V code
if (!spirvOptions.disableValidation) {
std::string messages;
if (!spirvToolsValidate(
targetEnv, &m, &messages, declIdMapper.requiresLegalization(),
spirvOptions.useGlLayout, spirvOptions.useDxLayout)) {
if (!spirvToolsValidate(targetEnv, spirvOptions,
declIdMapper.requiresLegalization(), &m,
&messages)) {
emitFatalError("generated SPIR-V is invalid: %0", {}) << messages;
emitNote("please file a bug report on "
"https://github.com/Microsoft/DirectXShaderCompiler/issues "

Просмотреть файл

@ -1190,6 +1190,7 @@ llvm::SmallVector<const Decoration *, 4> TypeTranslator::getLayoutDecorations(
offset = roundToPow2(offset, memberAlignment);
}
// The vk::offset attribute takes precedence over all.
if (const auto *offsetAttr = decl->getAttr<VKOffsetAttr>()) {
offset = offsetAttr->getOffset();
@ -1713,9 +1714,10 @@ TypeTranslator::getAlignmentAndSize(QualType type, SpirvLayoutRule rule,
if (isVectorType(type, &elemType, &elemCount)) {
uint32_t alignment = 0, size = 0;
std::tie(alignment, size) = getAlignmentAndSize(elemType, rule, stride);
// Use element alignment for fxc rules
// Use element alignment for fxc rules and VK_EXT_scalar_block_layout
if (rule != SpirvLayoutRule::FxcCTBuffer &&
rule != SpirvLayoutRule::FxcSBuffer)
rule != SpirvLayoutRule::FxcSBuffer &&
rule != SpirvLayoutRule::Scalar)
alignment = (elemCount == 3 ? 4 : elemCount) * size;
return {alignment, elemCount * size};
@ -1737,9 +1739,11 @@ TypeTranslator::getAlignmentAndSize(QualType type, SpirvLayoutRule rule,
const uint32_t vecStorageSize = isRowMajor ? colCount : rowCount;
if (rule == SpirvLayoutRule::FxcSBuffer) {
if (rule == SpirvLayoutRule::FxcSBuffer ||
rule == SpirvLayoutRule::Scalar) {
*stride = vecStorageSize * size;
// Use element alignment for fxc structured buffers
// Use element alignment for fxc structured buffers and
// VK_EXT_scalar_block_layout
return {alignment, rowCount * colCount * size};
}
@ -1794,6 +1798,12 @@ TypeTranslator::getAlignmentAndSize(QualType type, SpirvLayoutRule rule,
structSize += memberSize;
}
if (rule == SpirvLayoutRule::Scalar) {
// A structure has a scalar alignment equal to the largest scalar
// alignment of any of its members in VK_EXT_scalar_block_layout.
return {maxAlignment, structSize};
}
if (rule == SpirvLayoutRule::GLSLStd140 ||
rule == SpirvLayoutRule::RelaxedGLSLStd140 ||
rule == SpirvLayoutRule::FxcCTBuffer) {
@ -1817,9 +1827,11 @@ TypeTranslator::getAlignmentAndSize(QualType type, SpirvLayoutRule rule,
std::tie(alignment, size) =
getAlignmentAndSize(arrayType->getElementType(), rule, stride);
if (rule == SpirvLayoutRule::FxcSBuffer) {
if (rule == SpirvLayoutRule::FxcSBuffer ||
rule == SpirvLayoutRule::Scalar) {
*stride = size;
// Use element alignment for fxc structured buffers
// Use element alignment for fxc structured buffers and
// VK_EXT_scalar_block_layout
return {alignment, size * elemCount};
}

Просмотреть файл

@ -0,0 +1,79 @@
// Run: %dxc -T vs_6_2 -E main -fvk-use-scalar-layout -enable-16bit-types
struct R { // Alignment Offset Size Next
double rf; // 8 -> 0 + 8 = 8
}; // 8 8
struct S { // Alignment Offset Size Next
R sf1; // 8 -> 0 + 8 = 8
float sf2; // 4 -> 8 + 4 = 12
float3 sf3; // 4 -> 12 + 4 * 3 = 24
float sf4; // 4 -> 24 + 4 = 28
}; // 8(max) 28
struct T { // Alignment Offset Size = Next
int tf1; // 4 -> 0 + 4 = 4
R tf2[3]; // 8 -> 8 (4 round up to R alignment) + 3 * stride(8) = 32
float3x2 tf3; // 4 -> 32 + 4 * 3 * 2 = 56
S tf4; // 8 -> 56 + 28 = 84
float16_t tf5; // 2 -> 84 + 2 = 86
float tf6; // 4 -> 88 (86 round up to float align) + 4 = 92
}; // 8(max) 92
cbuffer MyCBuffer { // Alignment Offset Size Next
bool a; // 4 -> 0 + 4 = 4
uint1 b; // 4 -> 4 + 4 = 8
float3 c; // 4 -> 8 + 3 * 4 = 20
row_major float2x3 d; // 4 -> 20 + 4 * 2 * 3 = 44
column_major float2x3 e; // 4 -> 44 + 4 * 2 * 3 = 68
float2x1 f; // 4 -> 68 + 4 * 2 = 76
row_major float2x3 g[3]; // 4 -> 76 + 4 * 2 * 3 * 3 = 148
column_major float2x2 h[4]; // 4 -> 148 + 4 * 2 * 2 * 4 = 212
T t; // 8 -> 216 (212 round up to T alignment) + 92 = 308
float z; // 4 -> 308
};
// CHECK: OpDecorate %_arr_mat2v3float_uint_3 ArrayStride 24
// CHECK: OpDecorate %_arr_mat2v2float_uint_4 ArrayStride 16
// CHECK: OpMemberDecorate %R 0 Offset 0
// CHECK: OpDecorate %_arr_R_uint_3 ArrayStride 8
// CHECK: OpMemberDecorate %S 0 Offset 0
// CHECK-NEXT: OpMemberDecorate %S 1 Offset 8
// CHECK-NEXT: OpMemberDecorate %S 2 Offset 12
// CHECK-NEXT: OpMemberDecorate %S 3 Offset 24
// CHECK: OpMemberDecorate %T 0 Offset 0
// CHECK-NEXT: OpMemberDecorate %T 1 Offset 8
// CHECK-NEXT: OpMemberDecorate %T 2 Offset 32
// CHECK-NEXT: OpMemberDecorate %T 2 MatrixStride 12
// CHECK-NEXT: OpMemberDecorate %T 2 RowMajor
// CHECK-NEXT: OpMemberDecorate %T 3 Offset 56
// CHECK-NEXT: OpMemberDecorate %T 4 Offset 84
// CHECK-NEXT: OpMemberDecorate %T 5 Offset 88
// CHECK: OpMemberDecorate %type_MyCBuffer 0 Offset 0
// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 1 Offset 4
// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 2 Offset 8
// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 3 Offset 20
// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 3 MatrixStride 12
// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 3 ColMajor
// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 4 Offset 44
// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 4 MatrixStride 8
// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 4 RowMajor
// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 5 Offset 68
// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 6 Offset 76
// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 6 MatrixStride 12
// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 6 ColMajor
// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 7 Offset 148
// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 7 MatrixStride 8
// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 7 RowMajor
// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 8 Offset 216
// CHECK-NEXT: OpMemberDecorate %type_MyCBuffer 9 Offset 308
// CHECK-NEXT: OpDecorate %type_MyCBuffer Block
float main() : A {
return 1.0;
}

Просмотреть файл

@ -1623,6 +1623,12 @@ TEST_F(FileTest, VulkanLayoutFxcRulesCBuffer1) {
runFileTest("vk.layout.cbuffer.fxc.1.hlsl");
}
TEST_F(FileTest, VulkanLayoutCBufferScalar) {
// VK_EXT_scalar_block_layout
setScalarLayout();
runFileTest("vk.layout.cbuffer.scalar.hlsl");
}
TEST_F(FileTest, VulkanSubpassInput) { runFileTest("vk.subpass-input.hlsl"); }
TEST_F(FileTest, VulkanSubpassInputBinding) {
runFileTest("vk.subpass-input.binding.hlsl");

Просмотреть файл

@ -102,8 +102,9 @@ void FileTest::runFileTest(llvm::StringRef filename, Expect expect,
ASSERT_EQ(result.status(), effcee::Result::Status::Ok);
if (runValidation)
EXPECT_TRUE(utils::validateSpirvBinary(
targetEnv, generatedBinary, relaxLogicalPointer, glLayout, dxLayout));
EXPECT_TRUE(utils::validateSpirvBinary(targetEnv, generatedBinary,
relaxLogicalPointer, glLayout,
dxLayout, scalarLayout));
} else if (expect == Expect::Warning) {
ASSERT_TRUE(compileOk);
@ -127,8 +128,9 @@ void FileTest::runFileTest(llvm::StringRef filename, Expect expect,
ASSERT_EQ(result.status(), effcee::Result::Status::Ok);
if (runValidation)
EXPECT_TRUE(utils::validateSpirvBinary(
targetEnv, generatedBinary, relaxLogicalPointer, glLayout, dxLayout));
EXPECT_TRUE(utils::validateSpirvBinary(targetEnv, generatedBinary,
relaxLogicalPointer, glLayout,
dxLayout, scalarLayout));
} else if (expect == Expect::Failure) {
ASSERT_FALSE(compileOk);
@ -154,9 +156,9 @@ void FileTest::runFileTest(llvm::StringRef filename, Expect expect,
generatedBinary, &generatedSpirvAsm, true /* generateHeader */));
std::string valMessages;
EXPECT_FALSE(utils::validateSpirvBinary(targetEnv, generatedBinary,
relaxLogicalPointer, glLayout,
dxLayout, &valMessages));
EXPECT_FALSE(utils::validateSpirvBinary(
targetEnv, generatedBinary, relaxLogicalPointer, glLayout, dxLayout,
scalarLayout, &valMessages));
auto options = effcee::Options()
.SetChecksName(filename.str())
.SetInputName("<val-message>");

Просмотреть файл

@ -35,6 +35,7 @@ public:
void setRelaxLogicalPointer() { relaxLogicalPointer = true; }
void setGlLayout() { glLayout = true; }
void setDxLayout() { dxLayout = true; }
void setScalarLayout() { scalarLayout = true; }
/// \brief Runs a File Test! (See class description for more info)
void runFileTest(llvm::StringRef path, Expect expect = Expect::Success,
@ -55,6 +56,7 @@ private:
bool relaxLogicalPointer;
bool glLayout;
bool dxLayout;
bool scalarLayout;
};
} // end namespace spirv

Просмотреть файл

@ -36,11 +36,16 @@ bool disassembleSpirvBinary(std::vector<uint32_t> &binary,
bool validateSpirvBinary(spv_target_env env, std::vector<uint32_t> &binary,
bool relaxLogicalPointer, bool glLayout, bool dxLayout,
std::string *message) {
bool scalarLayout, std::string *message) {
spvtools::ValidatorOptions options;
options.SetRelaxLogicalPointer(relaxLogicalPointer);
options.SetRelaxBlockLayout(!glLayout && !dxLayout);
options.SetSkipBlockLayout(dxLayout);
if (dxLayout || scalarLayout) {
options.SetSkipBlockLayout(true);
} else if (glLayout) {
// The default for spirv-val.
} else {
options.SetRelaxBlockLayout(true);
}
spvtools::SpirvTools spirvTools(env);
spirvTools.SetMessageConsumer([message](spv_message_level_t, const char *,
const spv_position_t &,

Просмотреть файл

@ -34,7 +34,7 @@ bool disassembleSpirvBinary(std::vector<uint32_t> &binary,
/// Returns true if validation is successful; false otherwise.
bool validateSpirvBinary(spv_target_env, std::vector<uint32_t> &binary,
bool relaxLogicalPointer, bool glLayout, bool dxLayout,
std::string *message = nullptr);
bool scalarLayout, std::string *message = nullptr);
/// \brief Parses the Target Profile and Entry Point from the Run command
/// Returns the target profile, entry point, and the rest via arguments.

Просмотреть файл

@ -109,7 +109,8 @@ void WholeFileTest::runWholeFileTest(llvm::StringRef filename,
if (runSpirvValidation) {
EXPECT_TRUE(utils::validateSpirvBinary(
targetEnv, generatedBinary,
/*relaxLogicalPointer=*/false, /*glLayout=*/false, /*dxLayout=*/false));
/*relaxLogicalPointer=*/false, /*glLayout=*/false, /*dxLayout=*/false,
/*scalarLayout=*/false));
}
}