Add support for GL_ARB_enhanced_layouts.

This commit is contained in:
Hans-Kristian Arntzen 2017-10-10 11:05:46 +02:00
Родитель 1079e7930b
Коммит 5a89606f26
2 изменённых файлов: 95 добавлений и 24 удалений

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

@ -101,7 +101,9 @@ struct BufferRange
enum BufferPackingStandard enum BufferPackingStandard
{ {
BufferPackingStd140, BufferPackingStd140,
BufferPackingStd430 BufferPackingStd430,
BufferPackingStd140EnhancedLayout,
BufferPackingStd430EnhancedLayout
}; };
class Compiler class Compiler

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

@ -656,6 +656,11 @@ string CompilerGLSL::layout_for_member(const SPIRType &type, uint32_t index)
if (dec.decoration_flags & (1ull << DecorationLocation)) if (dec.decoration_flags & (1ull << DecorationLocation))
attr.push_back(join("location = ", dec.location)); attr.push_back(join("location = ", dec.location));
// DecorationCPacked is set by layout_for_variable earlier to mark that we need to emit offset qualifiers.
// This is only done selectively in GLSL as needed.
if (has_decoration(type.self, DecorationCPacked) && (dec.decoration_flags & (1ull << DecorationOffset)) != 0)
attr.push_back(join("offset = ", dec.offset));
if (attr.empty()) if (attr.empty())
return ""; return "";
@ -814,7 +819,7 @@ uint32_t CompilerGLSL::type_to_packed_alignment(const SPIRType &type, uint64_t f
} }
// In std140, struct alignment is rounded up to 16. // In std140, struct alignment is rounded up to 16.
if (packing == BufferPackingStd140) if (packing == BufferPackingStd140 || packing == BufferPackingStd140EnhancedLayout)
alignment = max(alignment, 16u); alignment = max(alignment, 16u);
return alignment; return alignment;
@ -840,7 +845,9 @@ uint32_t CompilerGLSL::type_to_packed_alignment(const SPIRType &type, uint64_t f
// vectors. // vectors.
if ((flags & (1ull << DecorationColMajor)) && type.columns > 1) if ((flags & (1ull << DecorationColMajor)) && type.columns > 1)
{ {
if (type.vecsize == 3) if (packing == BufferPackingStd140 || packing == BufferPackingStd140EnhancedLayout)
return 4 * base_alignment;
else if (type.vecsize == 3)
return 4 * base_alignment; return 4 * base_alignment;
else else
return type.vecsize * base_alignment; return type.vecsize * base_alignment;
@ -851,7 +858,9 @@ uint32_t CompilerGLSL::type_to_packed_alignment(const SPIRType &type, uint64_t f
// Rule 7. // Rule 7.
if ((flags & (1ull << DecorationRowMajor)) && type.vecsize > 1) if ((flags & (1ull << DecorationRowMajor)) && type.vecsize > 1)
{ {
if (type.columns == 3) if (packing == BufferPackingStd140 || packing == BufferPackingStd140EnhancedLayout)
return 4 * base_alignment;
else if (type.columns == 3)
return 4 * base_alignment; return 4 * base_alignment;
else else
return type.columns * base_alignment; return type.columns * base_alignment;
@ -873,7 +882,7 @@ uint32_t CompilerGLSL::type_to_packed_array_stride(const SPIRType &type, uint64_
uint32_t alignment = type_to_packed_alignment(tmp, flags, packing); uint32_t alignment = type_to_packed_alignment(tmp, flags, packing);
// Rule 4. In std140, array strides are padded out to the alignment of a vec4. // Rule 4. In std140, array strides are padded out to the alignment of a vec4.
if (packing == BufferPackingStd140) if (packing == BufferPackingStd140 || packing == BufferPackingStd140EnhancedLayout)
alignment = max(alignment, 16u); alignment = max(alignment, 16u);
return (size + alignment - 1) & ~(alignment - 1); return (size + alignment - 1) & ~(alignment - 1);
@ -918,7 +927,7 @@ uint32_t CompilerGLSL::type_to_packed_size(const SPIRType &type, uint64_t flags,
if ((flags & (1ull << DecorationColMajor)) && type.columns > 1) if ((flags & (1ull << DecorationColMajor)) && type.columns > 1)
{ {
if (packing == BufferPackingStd140) if (packing == BufferPackingStd140 || packing == BufferPackingStd140EnhancedLayout)
size = type.columns * 4 * base_alignment; size = type.columns * 4 * base_alignment;
else if (type.vecsize == 3) else if (type.vecsize == 3)
size = type.columns * 4 * base_alignment; size = type.columns * 4 * base_alignment;
@ -928,7 +937,7 @@ uint32_t CompilerGLSL::type_to_packed_size(const SPIRType &type, uint64_t flags,
if ((flags & (1ull << DecorationRowMajor)) && type.vecsize > 1) if ((flags & (1ull << DecorationRowMajor)) && type.vecsize > 1)
{ {
if (packing == BufferPackingStd140) if (packing == BufferPackingStd140 || packing == BufferPackingStd140EnhancedLayout)
size = type.vecsize * 4 * base_alignment; size = type.vecsize * 4 * base_alignment;
else if (type.columns == 3) else if (type.columns == 3)
size = type.vecsize * 4 * base_alignment; size = type.vecsize * 4 * base_alignment;
@ -975,9 +984,14 @@ bool CompilerGLSL::ssbo_is_packing_standard(const SPIRType &type, BufferPackingS
else else
pad_alignment = 1; pad_alignment = 1;
uint32_t actual_offset = type_struct_member_offset(type, i); // We only care about offsets in std140, std430, for EnhancedLayout variants,
if (actual_offset != offset) // This cannot be the packing we're looking for. // we have the flexibility to choose our own offsets.
return false; if (packing == BufferPackingStd140 || packing == BufferPackingStd430)
{
uint32_t actual_offset = type_struct_member_offset(type, i);
if (actual_offset != offset) // This cannot be the packing we're looking for.
return false;
}
// Verify array stride rules. // Verify array stride rules.
if (!memb_type.array.empty() && if (!memb_type.array.empty() &&
@ -985,7 +999,22 @@ bool CompilerGLSL::ssbo_is_packing_standard(const SPIRType &type, BufferPackingS
return false; return false;
// Verify that sub-structs also follow packing rules. // Verify that sub-structs also follow packing rules.
if (!memb_type.member_types.empty() && !ssbo_is_packing_standard(memb_type, packing)) // We cannot use enhanced layouts on substructs, so they better be up to spec.
BufferPackingStandard substruct_packing;
switch (packing)
{
case BufferPackingStd140EnhancedLayout:
substruct_packing = BufferPackingStd140;
break;
case BufferPackingStd430EnhancedLayout:
substruct_packing = BufferPackingStd430;
break;
default:
substruct_packing = packing;
break;
}
if (!memb_type.member_types.empty() && !ssbo_is_packing_standard(memb_type, substruct_packing))
return false; return false;
// Bump size. // Bump size.
@ -1073,34 +1102,74 @@ string CompilerGLSL::layout_for_variable(const SPIRVariable &var)
if (flags & (1ull << DecorationOffset)) if (flags & (1ull << DecorationOffset))
attr.push_back(join("offset = ", dec.offset)); attr.push_back(join("offset = ", dec.offset));
bool push_constant_block = options.vulkan_semantics && var.storage == StorageClassPushConstant;
bool ssbo_block = var.storage == StorageClassStorageBuffer ||
(var.storage == StorageClassUniform && (typeflags & (1ull << DecorationBufferBlock)));
// Instead of adding explicit offsets for every element here, just assume we're using std140 or std430. // Instead of adding explicit offsets for every element here, just assume we're using std140 or std430.
// If SPIR-V does not comply with either layout, we cannot really work around it. // If SPIR-V does not comply with either layout, we cannot really work around it.
if (var.storage == StorageClassUniform && (typeflags & (1ull << DecorationBlock))) if (var.storage == StorageClassUniform && (typeflags & (1ull << DecorationBlock)))
{ {
if (ssbo_is_packing_standard(type, BufferPackingStd140)) if (ssbo_is_packing_standard(type, BufferPackingStd140))
attr.push_back("std140"); attr.push_back("std140");
else if (ssbo_is_packing_standard(type, BufferPackingStd140EnhancedLayout))
{
// Fallback time. We might be able to use the ARB_enhanced_layouts to deal with this difference,
// however, we can only use layout(offset) on the block itself, not any substructs, so the substructs better be the appropriate layout.
// Enhanced layouts seem to always work in Vulkan GLSL, so no need for extensions there.
if (options.es && !options.vulkan_semantics)
SPIRV_CROSS_THROW("Push constant block cannot be expressed as neither std430 nor std140. ES-targets do not support GL_ARB_enhanced_layouts.");
if (!options.es && !options.vulkan_semantics && options.version < 440)
require_extension("GL_ARB_enhanced_layouts");
// This is a very last minute to check for this, but use this unused decoration to mark that we should emit
// explicit offsets for this block type.
// layout_for_variable() will be called before the actual buffer emit.
// The alternative is a full pass before codegen where we deduce this decoration,
// but then we are just doing the exact same work twice, and more complexity.
set_decoration(type.self, DecorationCPacked);
}
else else
SPIRV_CROSS_THROW("Uniform buffer cannot be expressed as std140. You can try flattening this block to " {
"support a more flexible layout."); SPIRV_CROSS_THROW(
"Uniform buffer cannot be expressed as std140, even with enhanced layouts. You can try flattening this block to "
"support a more flexible layout.");
}
} }
else if (var.storage == StorageClassStorageBuffer || else if (push_constant_block || ssbo_block)
(var.storage == StorageClassUniform && (typeflags & (1ull << DecorationBufferBlock))))
{ {
if (ssbo_is_packing_standard(type, BufferPackingStd430)) if (ssbo_is_packing_standard(type, BufferPackingStd430))
attr.push_back("std430"); attr.push_back("std430");
else if (ssbo_is_packing_standard(type, BufferPackingStd140)) else if (ssbo_is_packing_standard(type, BufferPackingStd140))
attr.push_back("std140"); attr.push_back("std140");
else else if (ssbo_is_packing_standard(type, BufferPackingStd140EnhancedLayout))
SPIRV_CROSS_THROW("Buffer block cannot be expressed as neither std430 nor std140."); {
}
else if (options.vulkan_semantics && var.storage == StorageClassPushConstant)
{
if (ssbo_is_packing_standard(type, BufferPackingStd430))
attr.push_back("std430");
else if (ssbo_is_packing_standard(type, BufferPackingStd140))
attr.push_back("std140"); attr.push_back("std140");
// Fallback time. We might be able to use the ARB_enhanced_layouts to deal with this difference,
// however, we can only use layout(offset) on the block itself, not any substructs, so the substructs better be the appropriate layout.
// Enhanced layouts seem to always work in Vulkan GLSL, so no need for extensions there.
if (options.es && !options.vulkan_semantics)
SPIRV_CROSS_THROW("Push constant block cannot be expressed as neither std430 nor std140. ES-targets do not support GL_ARB_enhanced_layouts.");
if (!options.es && !options.vulkan_semantics && options.version < 440)
require_extension("GL_ARB_enhanced_layouts");
set_decoration(type.self, DecorationCPacked);
}
else if (ssbo_is_packing_standard(type, BufferPackingStd430EnhancedLayout))
{
attr.push_back("std430");
if (options.es && !options.vulkan_semantics)
SPIRV_CROSS_THROW("Push constant block cannot be expressed as neither std430 nor std140. ES-targets do not support GL_ARB_enhanced_layouts.");
if (!options.es && !options.vulkan_semantics && options.version < 440)
require_extension("GL_ARB_enhanced_layouts");
set_decoration(type.self, DecorationCPacked);
}
else else
SPIRV_CROSS_THROW("Push constant block cannot be expressed as neither std430 nor std140."); {
SPIRV_CROSS_THROW("Buffer block cannot be expressed as neither std430 nor std140, even with enhanced layouts. You can try flattening this block to support a more flexible layout.");
}
} }
// For images, the type itself adds a layout qualifer. // For images, the type itself adds a layout qualifer.