Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 22 additions & 14 deletions docs/SPIR-V.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions include/dxc/Support/HLSLOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -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]>,
Expand Down
2 changes: 2 additions & 0 deletions include/dxc/Support/SPIRVOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -50,6 +51,7 @@ struct SpirvCodeGenOptions {
bool noWarnIgnoredFeatures;
bool useDxLayout;
bool useGlLayout;
bool useScalarLayout;
SpirvLayoutRule cBufferLayoutRule;
SpirvLayoutRule sBufferLayoutRule;
SpirvLayoutRule tBufferLayoutRule;
Expand Down
2 changes: 2 additions & 0 deletions lib/DxcSupport/HLSLOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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) ||
Expand Down
25 changes: 17 additions & 8 deletions tools/clang/lib/SPIRV/SPIRVEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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);
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 "
Expand Down
24 changes: 18 additions & 6 deletions tools/clang/lib/SPIRV/TypeTranslator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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};
Expand All @@ -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};
}

Expand Down Expand Up @@ -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) {
Expand All @@ -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};
}

Expand Down
79 changes: 79 additions & 0 deletions tools/clang/test/CodeGenSPIRV/vk.layout.cbuffer.scalar.hlsl
Original file line number Diff line number Diff line change
@@ -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;
}
6 changes: 6 additions & 0 deletions tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
16 changes: 9 additions & 7 deletions tools/clang/unittests/SPIRV/FileTestFixture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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);

Expand All @@ -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>");
Expand Down
2 changes: 2 additions & 0 deletions tools/clang/unittests/SPIRV/FileTestFixture.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class FileTest : public ::testing::Test {
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,
Expand All @@ -55,6 +56,7 @@ class FileTest : public ::testing::Test {
bool relaxLogicalPointer;
bool glLayout;
bool dxLayout;
bool scalarLayout;
};

} // end namespace spirv
Expand Down
11 changes: 8 additions & 3 deletions tools/clang/unittests/SPIRV/FileTestUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 &,
Expand Down
Loading