Commit f0b02054 by Geoff Lang Committed by Commit Bot

Add a Vulkan feature to compress float32 vertex formats.

Use the vertex conversion pipeline in VertexArrayVk to detect static vertex data and convert float32 vertices to float16. This feature is useful for determining if an allication is vertex bandwidth bound and seeing what gains could be had by using smaller attributes. This feature could be implemented in ANGLE's frontend but new infrastructure for converting and storing the converted attributes would need to be added to gl::VertexArray. Our backends already have the functionality needed to handle unsupported attribute formats and this can be repurposed for compressing vertex formats. Bug: b/167404532 Bug: b/161716126 Change-Id: I9a09656a72e8499faa4124adf876d7261c8341c9 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2342285 Commit-Queue: Geoff Lang <geofflang@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org>
parent 4d779fb3
...@@ -408,6 +408,14 @@ struct FeaturesVk : FeatureSetBase ...@@ -408,6 +408,14 @@ struct FeaturesVk : FeatureSetBase
Feature forceNearestMipFiltering = {"force_nearest_mip_filtering", Feature forceNearestMipFiltering = {"force_nearest_mip_filtering",
FeatureCategory::VulkanWorkarounds, FeatureCategory::VulkanWorkarounds,
"Force nearest mip filtering when sampling.", &members}; "Force nearest mip filtering when sampling.", &members};
// Compress float32 vertices in static buffers to float16 at draw time. ANGLE is non-conformant
// if this feature is enabled.
angle::Feature compressVertexData = {"compress_vertex_data",
angle::FeatureCategory::VulkanWorkarounds,
"Compress vertex data to smaller data types when "
"possible. Using this feature makes ANGLE non-conformant.",
&members};
}; };
inline FeaturesVk::FeaturesVk() = default; inline FeaturesVk::FeaturesVk() = default;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
"src/libANGLE/es3_copy_conversion_formats.json": "src/libANGLE/es3_copy_conversion_formats.json":
"54608f6f7d9aa7c59a8458ccf3ab9935", "54608f6f7d9aa7c59a8458ccf3ab9935",
"src/libANGLE/es3_copy_conversion_table_autogen.cpp": "src/libANGLE/es3_copy_conversion_table_autogen.cpp":
"b20d198cf5e292c43170d4873b381b34", "773a77dd24084a9071431e9df1c097e3",
"src/libANGLE/gen_copy_conversion_table.py": "src/libANGLE/gen_copy_conversion_table.py":
"18e0d2ff461f730a9efb0dcdfa3f058a", "18e0d2ff461f730a9efb0dcdfa3f058a",
"src/libANGLE/renderer/angle_format.py": "src/libANGLE/renderer/angle_format.py":
......
...@@ -4,9 +4,9 @@ ...@@ -4,9 +4,9 @@
"src/libANGLE/renderer/angle_format_map.json": "src/libANGLE/renderer/angle_format_map.json":
"aa4a0d3463b76858a75787b9cdec8e98", "aa4a0d3463b76858a75787b9cdec8e98",
"src/libANGLE/renderer/vulkan/gen_vk_format_table.py": "src/libANGLE/renderer/vulkan/gen_vk_format_table.py":
"54a7374f93f17da1386600027acca7a3", "b4d38f08a354849dcba2a8f9f2569069",
"src/libANGLE/renderer/vulkan/vk_format_map.json": "src/libANGLE/renderer/vulkan/vk_format_map.json":
"5dc3cfb41778806e379876ce9fa427f3", "b62588b1e9f6d9fa98aeea886d8ed2bd",
"src/libANGLE/renderer/vulkan/vk_format_table_autogen.cpp": "src/libANGLE/renderer/vulkan/vk_format_table_autogen.cpp":
"7882959fda2dbda451164f2afd2e35be" "dfb656a573582202fe813eb23d5052dc"
} }
\ No newline at end of file
...@@ -89,6 +89,8 @@ ...@@ -89,6 +89,8 @@
"df913989b39699e549ba9089190d358c", "df913989b39699e549ba9089190d358c",
"src/libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000007.inc": "src/libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000007.inc":
"c9e0fad17170e97662b0fa0d37919ea3", "c9e0fad17170e97662b0fa0d37919ea3",
"src/libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000008.inc":
"680d2865d4350c8f36fbbdaec1b43682",
"src/libANGLE/renderer/vulkan/shaders/gen/FullScreenQuad.vert.00000000.inc": "src/libANGLE/renderer/vulkan/shaders/gen/FullScreenQuad.vert.00000000.inc":
"3a4ab796f02d3f1c306c92f7da2c68ee", "3a4ab796f02d3f1c306c92f7da2c68ee",
"src/libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000000.inc": "src/libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000000.inc":
...@@ -250,9 +252,9 @@ ...@@ -250,9 +252,9 @@
"src/libANGLE/renderer/vulkan/shaders/src/ConvertIndirectLineLoop.comp.json": "src/libANGLE/renderer/vulkan/shaders/src/ConvertIndirectLineLoop.comp.json":
"c2c79c40b0fbcb4876637aa06e8aa919", "c2c79c40b0fbcb4876637aa06e8aa919",
"src/libANGLE/renderer/vulkan/shaders/src/ConvertVertex.comp": "src/libANGLE/renderer/vulkan/shaders/src/ConvertVertex.comp":
"f63e7c4f738c11f1a1b19023d4515dc4", "537e6cff8f9a0b6acdf1c9be5fdcef7c",
"src/libANGLE/renderer/vulkan/shaders/src/ConvertVertex.comp.json": "src/libANGLE/renderer/vulkan/shaders/src/ConvertVertex.comp.json":
"e4a95aae7f216780946e7332d22aa74e", "f2abd98463e46c0af45e8a1a5e5af88f",
"src/libANGLE/renderer/vulkan/shaders/src/FullScreenQuad.vert": "src/libANGLE/renderer/vulkan/shaders/src/FullScreenQuad.vert":
"805ec8b2f87d4bd4242dc5b1c58ba3b4", "805ec8b2f87d4bd4242dc5b1c58ba3b4",
"src/libANGLE/renderer/vulkan/shaders/src/GenerateMipmap.comp": "src/libANGLE/renderer/vulkan/shaders/src/GenerateMipmap.comp":
...@@ -276,9 +278,9 @@ ...@@ -276,9 +278,9 @@
"src/libANGLE/renderer/vulkan/shaders/src/OverlayDraw.comp.json": "src/libANGLE/renderer/vulkan/shaders/src/OverlayDraw.comp.json":
"af79e5153c99cdb1e6b551b11bbf7f6b", "af79e5153c99cdb1e6b551b11bbf7f6b",
"src/libANGLE/renderer/vulkan/vk_internal_shaders_autogen.cpp": "src/libANGLE/renderer/vulkan/vk_internal_shaders_autogen.cpp":
"df9efda2dee207bea8e346f2c9fa824f", "a62caa10b2ec5dc17cc8cbf9094d38f4",
"src/libANGLE/renderer/vulkan/vk_internal_shaders_autogen.h": "src/libANGLE/renderer/vulkan/vk_internal_shaders_autogen.h":
"96c6f4b7a06a3b21f3b0ceea661014ef", "a18cac1ffca9735480a2b9dea67b73c0",
"tools/glslang/glslang_validator.exe.sha1": "tools/glslang/glslang_validator.exe.sha1":
"17e862cc6f462fecbf50b24ed6544a27", "17e862cc6f462fecbf50b24ed6544a27",
"tools/glslang/glslang_validator.sha1": "tools/glslang/glslang_validator.sha1":
......
...@@ -200,6 +200,19 @@ bool IsArrayTextureType(TextureType type) ...@@ -200,6 +200,19 @@ bool IsArrayTextureType(TextureType type)
} }
} }
bool IsStaticBufferUsage(BufferUsage useage)
{
switch (useage)
{
case BufferUsage::StaticCopy:
case BufferUsage::StaticDraw:
case BufferUsage::StaticRead:
return true;
default:
return false;
}
}
std::ostream &operator<<(std::ostream &os, PrimitiveMode value) std::ostream &operator<<(std::ostream &os, PrimitiveMode value)
{ {
switch (value) switch (value)
......
...@@ -223,6 +223,8 @@ TextureType SamplerTypeToTextureType(GLenum samplerType); ...@@ -223,6 +223,8 @@ TextureType SamplerTypeToTextureType(GLenum samplerType);
bool IsMultisampled(gl::TextureType type); bool IsMultisampled(gl::TextureType type);
bool IsArrayTextureType(gl::TextureType type); bool IsArrayTextureType(gl::TextureType type);
bool IsStaticBufferUsage(BufferUsage useage);
enum class PrimitiveMode : uint8_t enum class PrimitiveMode : uint8_t
{ {
Points = 0x0, Points = 0x0,
......
// GENERATED FILE - DO NOT EDIT. // GENERATED FILE - DO NOT EDIT.
// Generated by gen_copy_conversion_table.py using data from es3_copy_conversion_formats.json. // Generated by gen_copy_conversion_table.py using data from es3_copy_conversion_formats.json.
// //
// Copyright 2019 The ANGLE Project Authors. All rights reserved. // Copyright 2020 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// //
......
...@@ -44,6 +44,13 @@ void Copy32FixedTo32FVertexData(const uint8_t *input, size_t stride, size_t coun ...@@ -44,6 +44,13 @@ void Copy32FixedTo32FVertexData(const uint8_t *input, size_t stride, size_t coun
template <typename T, size_t inputComponentCount, size_t outputComponentCount, bool normalized> template <typename T, size_t inputComponentCount, size_t outputComponentCount, bool normalized>
void CopyTo32FVertexData(const uint8_t *input, size_t stride, size_t count, uint8_t *output); void CopyTo32FVertexData(const uint8_t *input, size_t stride, size_t count, uint8_t *output);
template <size_t inputComponentCount, size_t outputComponentCount>
void Copy32FTo16FVertexData(const uint8_t *input, size_t stride, size_t count, uint8_t *output);
void CopyXYZ32FToXYZ9E5(const uint8_t *input, size_t stride, size_t count, uint8_t *output);
void CopyXYZ32FToX11Y11B10F(const uint8_t *input, size_t stride, size_t count, uint8_t *output);
template <bool isSigned, bool normalized, bool toFloat> template <bool isSigned, bool normalized, bool toFloat>
void CopyXYZ10W2ToXYZW32FVertexData(const uint8_t *input, void CopyXYZ10W2ToXYZW32FVertexData(const uint8_t *input,
size_t stride, size_t stride,
......
...@@ -219,6 +219,57 @@ inline void CopyTo32FVertexData(const uint8_t *input, size_t stride, size_t coun ...@@ -219,6 +219,57 @@ inline void CopyTo32FVertexData(const uint8_t *input, size_t stride, size_t coun
} }
} }
template <size_t inputComponentCount, size_t outputComponentCount>
void Copy32FTo16FVertexData(const uint8_t *input, size_t stride, size_t count, uint8_t *output)
{
const unsigned short kZero = gl::float32ToFloat16(0.0f);
const unsigned short kOne = gl::float32ToFloat16(1.0f);
for (size_t i = 0; i < count; i++)
{
const float *offsetInput = reinterpret_cast<const float *>(input + (stride * i));
unsigned short *offsetOutput =
reinterpret_cast<unsigned short *>(output) + i * outputComponentCount;
for (size_t j = 0; j < inputComponentCount; j++)
{
offsetOutput[j] = gl::float32ToFloat16(offsetInput[j]);
}
for (size_t j = inputComponentCount; j < outputComponentCount; j++)
{
offsetOutput[j] = (j == 3) ? kOne : kZero;
}
}
}
inline void CopyXYZ32FToXYZ9E5(const uint8_t *input, size_t stride, size_t count, uint8_t *output)
{
for (size_t i = 0; i < count; i++)
{
const float *offsetInput = reinterpret_cast<const float *>(input + (stride * i));
unsigned int *offsetOutput = reinterpret_cast<unsigned int *>(output) + i;
*offsetOutput = gl::convertRGBFloatsTo999E5(offsetInput[0], offsetInput[1], offsetInput[2]);
}
}
inline void CopyXYZ32FToX11Y11B10F(const uint8_t *input,
size_t stride,
size_t count,
uint8_t *output)
{
for (size_t i = 0; i < count; i++)
{
const float *offsetInput = reinterpret_cast<const float *>(input + (stride * i));
unsigned int *offsetOutput = reinterpret_cast<unsigned int *>(output) + i;
*offsetOutput = gl::float32ToFloat11(offsetInput[0]) << 0 |
gl::float32ToFloat11(offsetInput[1]) << 11 |
gl::float32ToFloat10(offsetInput[2]) << 22;
}
}
namespace priv namespace priv
{ {
......
...@@ -352,6 +352,7 @@ class ContextVk : public ContextImpl, public vk::Context ...@@ -352,6 +352,7 @@ class ContextVk : public ContextImpl, public vk::Context
GLuint stride, GLuint stride,
GLuint divisor, GLuint divisor,
angle::FormatID format, angle::FormatID format,
bool compressed,
GLuint relativeOffset, GLuint relativeOffset,
const vk::BufferHelper *vertexBuffer); const vk::BufferHelper *vertexBuffer);
...@@ -1167,6 +1168,7 @@ ANGLE_INLINE angle::Result ContextVk::onVertexAttributeChange(size_t attribIndex ...@@ -1167,6 +1168,7 @@ ANGLE_INLINE angle::Result ContextVk::onVertexAttributeChange(size_t attribIndex
GLuint stride, GLuint stride,
GLuint divisor, GLuint divisor,
angle::FormatID format, angle::FormatID format,
bool compressed,
GLuint relativeOffset, GLuint relativeOffset,
const vk::BufferHelper *vertexBuffer) const vk::BufferHelper *vertexBuffer)
{ {
...@@ -1174,7 +1176,8 @@ ANGLE_INLINE angle::Result ContextVk::onVertexAttributeChange(size_t attribIndex ...@@ -1174,7 +1176,8 @@ ANGLE_INLINE angle::Result ContextVk::onVertexAttributeChange(size_t attribIndex
// Set divisor to 1 for attribs with emulated divisor // Set divisor to 1 for attribs with emulated divisor
mGraphicsPipelineDesc->updateVertexInput( mGraphicsPipelineDesc->updateVertexInput(
&mGraphicsPipelineTransition, static_cast<uint32_t>(attribIndex), stride, &mGraphicsPipelineTransition, static_cast<uint32_t>(attribIndex), stride,
divisor > mRenderer->getMaxVertexAttribDivisor() ? 1 : divisor, format, relativeOffset); divisor > mRenderer->getMaxVertexAttribDivisor() ? 1 : divisor, format, compressed,
relativeOffset);
return onVertexBufferChange(vertexBuffer); return onVertexBufferChange(vertexBuffer);
} }
} // namespace rx } // namespace rx
......
...@@ -1895,6 +1895,8 @@ void RendererVk::initFeatures(DisplayVk *displayVk, const ExtensionNameList &dev ...@@ -1895,6 +1895,8 @@ void RendererVk::initFeatures(DisplayVk *displayVk, const ExtensionNameList &dev
ANGLE_FEATURE_CONDITION(&mFeatures, forceNearestFiltering, false); ANGLE_FEATURE_CONDITION(&mFeatures, forceNearestFiltering, false);
ANGLE_FEATURE_CONDITION(&mFeatures, forceNearestMipFiltering, false); ANGLE_FEATURE_CONDITION(&mFeatures, forceNearestMipFiltering, false);
ANGLE_FEATURE_CONDITION(&mFeatures, compressVertexData, false);
angle::PlatformMethods *platform = ANGLEPlatformCurrent(); angle::PlatformMethods *platform = ANGLEPlatformCurrent();
platform->overrideFeaturesVk(platform, &mFeatures); platform->overrideFeaturesVk(platform, &mFeatures);
......
...@@ -67,14 +67,13 @@ uint32_t GetConvertVertexFlags(const UtilsVk::ConvertVertexParameters &params) ...@@ -67,14 +67,13 @@ uint32_t GetConvertVertexFlags(const UtilsVk::ConvertVertexParameters &params)
// Assert on the types to make sure the shader supports its. These are based on // Assert on the types to make sure the shader supports its. These are based on
// ConvertVertex_comp::Conversion values. // ConvertVertex_comp::Conversion values.
ASSERT(!destIsSint || srcIsSint); // If destination is sint, src must be sint too ASSERT(!destIsSint || srcIsSint); // If destination is sint, src must be sint too
ASSERT(!destIsUint || srcIsUint); // If destination is uint, src must be uint too ASSERT(!destIsUint || srcIsUint); // If destination is uint, src must be uint too
ASSERT(!srcIsFixed || destIsFloat); // If source is fixed, dest must be float ASSERT(!srcIsFixed || destIsFloat); // If source is fixed, dest must be float
ASSERT(srcIsHalfFloat == destIsHalfFloat); // Both src and dest are half float or neither
// One of each bool set must be true // One of each bool set must be true
ASSERT(srcIsSint || srcIsUint || srcIsSnorm || srcIsUnorm || srcIsFixed || srcIsFloat); ASSERT(srcIsSint || srcIsUint || srcIsSnorm || srcIsUnorm || srcIsFixed || srcIsFloat);
ASSERT(destIsSint || destIsUint || destIsFloat); ASSERT(destIsSint || destIsUint || destIsFloat || destIsHalfFloat);
// We currently don't have any big-endian devices in the list of supported platforms. The // We currently don't have any big-endian devices in the list of supported platforms. The
// shader is capable of supporting big-endian architectures, but the relevant flag (IsBigEndian) // shader is capable of supporting big-endian architectures, but the relevant flag (IsBigEndian)
...@@ -90,6 +89,10 @@ uint32_t GetConvertVertexFlags(const UtilsVk::ConvertVertexParameters &params) ...@@ -90,6 +89,10 @@ uint32_t GetConvertVertexFlags(const UtilsVk::ConvertVertexParameters &params)
// Note that HalfFloat conversion uses the same shader as Uint. // Note that HalfFloat conversion uses the same shader as Uint.
flags |= ConvertVertex_comp::kUintToUint; flags |= ConvertVertex_comp::kUintToUint;
} }
else if (srcIsFloat && destIsHalfFloat)
{
flags |= ConvertVertex_comp::kFloatToHalf;
}
else if (srcIsSint && destIsSint) else if (srcIsSint && destIsSint)
{ {
flags |= ConvertVertex_comp::kSintToSint; flags |= ConvertVertex_comp::kSintToSint;
......
...@@ -123,14 +123,16 @@ class VertexArrayVk : public VertexArrayImpl ...@@ -123,14 +123,16 @@ class VertexArrayVk : public VertexArrayImpl
size_t attribIndex, size_t attribIndex,
const vk::Format &vertexFormat, const vk::Format &vertexFormat,
ConversionBuffer *conversion, ConversionBuffer *conversion,
GLuint relativeOffset); GLuint relativeOffset,
bool compressed);
angle::Result convertVertexBufferCPU(ContextVk *contextVk, angle::Result convertVertexBufferCPU(ContextVk *contextVk,
BufferVk *srcBuffer, BufferVk *srcBuffer,
const gl::VertexBinding &binding, const gl::VertexBinding &binding,
size_t attribIndex, size_t attribIndex,
const vk::Format &vertexFormat, const vk::Format &vertexFormat,
ConversionBuffer *conversion, ConversionBuffer *conversion,
GLuint relativeOffset); GLuint relativeOffset,
bool compress);
angle::Result syncDirtyAttrib(ContextVk *contextVk, angle::Result syncDirtyAttrib(ContextVk *contextVk,
const gl::VertexAttribute &attrib, const gl::VertexAttribute &attrib,
...@@ -145,6 +147,7 @@ class VertexArrayVk : public VertexArrayImpl ...@@ -145,6 +147,7 @@ class VertexArrayVk : public VertexArrayImpl
gl::AttribArray<vk::BufferHelper *> mCurrentArrayBuffers; gl::AttribArray<vk::BufferHelper *> mCurrentArrayBuffers;
// Cache strides of attributes for a fast pipeline cache update when VAOs are changed // Cache strides of attributes for a fast pipeline cache update when VAOs are changed
gl::AttribArray<GLuint> mCurrentArrayBufferStrides; gl::AttribArray<GLuint> mCurrentArrayBufferStrides;
gl::AttributesMask mCurrentArrayBufferCompressed;
VkDeviceSize mCurrentElementArrayBufferOffset; VkDeviceSize mCurrentElementArrayBufferOffset;
vk::BufferHelper *mCurrentElementArrayBuffer; vk::BufferHelper *mCurrentElementArrayBuffer;
......
...@@ -93,7 +93,7 @@ buffer_struct_template = """{{{buffer}, {vk_buffer_format}, {vk_buffer_format_is ...@@ -93,7 +93,7 @@ buffer_struct_template = """{{{buffer}, {vk_buffer_format}, {vk_buffer_format_is
buffer_fallback_template = """{{ buffer_fallback_template = """{{
static constexpr BufferFormatInitInfo kInfo[] = {{{buffer_list}}}; static constexpr BufferFormatInitInfo kInfo[] = {{{buffer_list}}};
initBufferFallback(renderer, kInfo, ArraySize(kInfo)); initBufferFallback(renderer, kInfo, ArraySize(kInfo), {buffer_compressed_offset});
}}""" }}"""
...@@ -155,9 +155,17 @@ def gen_format_case(angle, internal_format, vk_json_data): ...@@ -155,9 +155,17 @@ def gen_format_case(angle, internal_format, vk_json_data):
fallbacks = vk_fallbacks.get(format, {}).get(type, []) fallbacks = vk_fallbacks.get(format, {}).get(type, [])
if not isinstance(fallbacks, list): if not isinstance(fallbacks, list):
fallbacks = [fallbacks] fallbacks = [fallbacks]
if format not in vk_map:
return fallbacks compressed = vk_fallbacks.get(format, {}).get(type + "_compressed", [])
return [format] + fallbacks if not isinstance(compressed, list):
compressed = [compressed]
fallbacks += compressed
if format in vk_map:
fallbacks = [format] + fallbacks
return (fallbacks, len(fallbacks) - len(compressed))
def image_args(format): def image_args(format):
return dict( return dict(
...@@ -176,7 +184,7 @@ def gen_format_case(angle, internal_format, vk_json_data): ...@@ -176,7 +184,7 @@ def gen_format_case(angle, internal_format, vk_json_data):
vertex_load_converts='false' if angle == format else 'true', vertex_load_converts='false' if angle == format else 'true',
) )
images = get_formats(angle, "image") images, images_compressed_offset = get_formats(angle, "image")
if len(images) == 1: if len(images) == 1:
args.update(image_template=image_basic_template) args.update(image_template=image_basic_template)
args.update(image_args(images[0])) args.update(image_args(images[0]))
...@@ -185,7 +193,7 @@ def gen_format_case(angle, internal_format, vk_json_data): ...@@ -185,7 +193,7 @@ def gen_format_case(angle, internal_format, vk_json_data):
image_template=image_fallback_template, image_template=image_fallback_template,
image_list=", ".join(image_struct_template.format(**image_args(i)) for i in images)) image_list=", ".join(image_struct_template.format(**image_args(i)) for i in images))
buffers = get_formats(angle, "buffer") buffers, buffers_compressed_offset = get_formats(angle, "buffer")
if len(buffers) == 1: if len(buffers) == 1:
args.update(buffer_template=buffer_basic_template) args.update(buffer_template=buffer_basic_template)
args.update(buffer_args(buffers[0])) args.update(buffer_args(buffers[0]))
...@@ -193,7 +201,8 @@ def gen_format_case(angle, internal_format, vk_json_data): ...@@ -193,7 +201,8 @@ def gen_format_case(angle, internal_format, vk_json_data):
args.update( args.update(
buffer_template=buffer_fallback_template, buffer_template=buffer_fallback_template,
buffer_list=", ".join( buffer_list=", ".join(
buffer_struct_template.format(**buffer_args(i)) for i in buffers)) buffer_struct_template.format(**buffer_args(i)) for i in buffers),
buffer_compressed_offset=buffers_compressed_offset)
return format_entry_template.format(**args).format(**args) return format_entry_template.format(**args).format(**args)
......
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
// * UnormToFloat: Similar to UintToFloat, but normalized. // * UnormToFloat: Similar to UintToFloat, but normalized.
// * FixedToFloat: 16.16 signed fixed-point to floating point. // * FixedToFloat: 16.16 signed fixed-point to floating point.
// * FloatToFloat: float. // * FloatToFloat: float.
// * FloatToHalf: float to half-float
// //
// SintToSint, UintToUint and FloatToFloat correspond to CopyNativeVertexData() and // SintToSint, UintToUint and FloatToFloat correspond to CopyNativeVertexData() and
// Copy8SintTo16SintVertexData() in renderer/copyvertex.inc. // Copy8SintTo16SintVertexData() in renderer/copyvertex.inc.
...@@ -67,7 +68,7 @@ ...@@ -67,7 +68,7 @@
#define SrcType int #define SrcType int
#elif UintToUint || UintToFloat #elif UintToUint || UintToFloat
#define SrcType uint #define SrcType uint
#elif SnormToFloat || UnormToFloat || FixedToFloat || FloatToFloat #elif SnormToFloat || UnormToFloat || FixedToFloat || FloatToFloat || FloatToHalf
#define SrcType float #define SrcType float
#else #else
#error "Not all conversions are accounted for" #error "Not all conversions are accounted for"
...@@ -83,6 +84,9 @@ ...@@ -83,6 +84,9 @@
#elif SintToFloat || UintToFloat || SnormToFloat || UnormToFloat || FixedToFloat || FloatToFloat #elif SintToFloat || UintToFloat || SnormToFloat || UnormToFloat || FixedToFloat || FloatToFloat
#define DestType float #define DestType float
#define IsDestFloat 1 #define IsDestFloat 1
#elif FloatToHalf
#define DestType float
#define IsDestFloat 0
#else #else
#error "Not all conversions are accounted for" #error "Not all conversions are accounted for"
#endif #endif
...@@ -254,7 +258,7 @@ SrcType loadSourceComponent(uint cd) ...@@ -254,7 +258,7 @@ SrcType loadSourceComponent(uint cd)
#elif FixedToFloat #elif FixedToFloat
// 1.0 in fixed point is 0x10000 // 1.0 in fixed point is 0x10000
valueAsUint = 0x10000; valueAsUint = 0x10000;
#elif FloatToFloat #elif FloatToFloat || FloatToHalf
valueAsUint = floatBitsToUint(1.0); valueAsUint = floatBitsToUint(1.0);
#else #else
#error "Not all conversions are accounted for" #error "Not all conversions are accounted for"
...@@ -296,7 +300,7 @@ SrcType loadSourceComponent(uint cd) ...@@ -296,7 +300,7 @@ SrcType loadSourceComponent(uint cd)
#elif FixedToFloat #elif FixedToFloat
float divisor = 1.0f / 65536.0f; float divisor = 1.0f / 65536.0f;
SrcType value = int(valueAsUint) * divisor; SrcType value = int(valueAsUint) * divisor;
#elif FloatToFloat #elif FloatToFloat || FloatToHalf
SrcType value = uintBitsToFloat(valueAsUint); SrcType value = uintBitsToFloat(valueAsUint);
#else #else
#error "Not all conversions are accounted for" #error "Not all conversions are accounted for"
...@@ -328,6 +332,10 @@ uint makeDestinationComponent(uint cd, DestType value) ...@@ -328,6 +332,10 @@ uint makeDestinationComponent(uint cd, DestType value)
uint valueMask = valueBits == 32 ? -1 : (1 << valueBits) - 1; uint valueMask = valueBits == 32 ? -1 : (1 << valueBits) - 1;
uint valueAsUint = (uint(value) & valueMask) << shiftBits; uint valueAsUint = (uint(value) & valueMask) << shiftBits;
#elif FloatToHalf
uint shift = ((cd & 1) == 0) ? 0 : 16;
uint valueAsUint = packHalf2x16(vec2(value, 0.0)) << shift;
#elif IsDestFloat #elif IsDestFloat
// If the destination is float, it will occupy the whole result. // If the destination is float, it will occupy the whole result.
uint valueAsUint = floatBitsToInt(value); uint valueAsUint = floatBitsToInt(value);
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
[ "SnormToFloat", "-Od" ], [ "SnormToFloat", "-Od" ],
[ "UnormToFloat", "-Od" ], [ "UnormToFloat", "-Od" ],
[ "FixedToFloat", "-Od" ], [ "FixedToFloat", "-Od" ],
[ "FloatToFloat", "-Od" ] [ "FloatToFloat", "-Od" ],
[ "FloatToHalf", "-Od" ]
] ]
} }
...@@ -910,6 +910,7 @@ void GraphicsPipelineDesc::initDefaults() ...@@ -910,6 +910,7 @@ void GraphicsPipelineDesc::initDefaults()
SetBitField(packedAttrib.stride, 0); SetBitField(packedAttrib.stride, 0);
SetBitField(packedAttrib.divisor, 0); SetBitField(packedAttrib.divisor, 0);
SetBitField(packedAttrib.format, defaultFormat); SetBitField(packedAttrib.format, defaultFormat);
SetBitField(packedAttrib.compressed, 0);
SetBitField(packedAttrib.offset, 0); SetBitField(packedAttrib.offset, 0);
} }
...@@ -1107,7 +1108,8 @@ angle::Result GraphicsPipelineDesc::initializePipeline( ...@@ -1107,7 +1108,8 @@ angle::Result GraphicsPipelineDesc::initializePipeline(
angle::FormatID formatID = static_cast<angle::FormatID>(packedAttrib.format); angle::FormatID formatID = static_cast<angle::FormatID>(packedAttrib.format);
const vk::Format &format = contextVk->getRenderer()->getFormat(formatID); const vk::Format &format = contextVk->getRenderer()->getFormat(formatID);
const angle::Format &angleFormat = format.intendedFormat(); const angle::Format &angleFormat = format.intendedFormat();
VkFormat vkFormat = format.vkBufferFormat; VkFormat vkFormat =
packedAttrib.compressed ? format.vkCompressedBufferFormat : format.vkBufferFormat;
gl::ComponentType attribType = gl::ComponentType attribType =
GetVertexAttributeComponentType(angleFormat.isPureInt(), angleFormat.vertexAttribType); GetVertexAttributeComponentType(angleFormat.isPureInt(), angleFormat.vertexAttribType);
...@@ -1343,6 +1345,7 @@ void GraphicsPipelineDesc::updateVertexInput(GraphicsPipelineTransitionBits *tra ...@@ -1343,6 +1345,7 @@ void GraphicsPipelineDesc::updateVertexInput(GraphicsPipelineTransitionBits *tra
GLuint stride, GLuint stride,
GLuint divisor, GLuint divisor,
angle::FormatID format, angle::FormatID format,
bool compressed,
GLuint relativeOffset) GLuint relativeOffset)
{ {
vk::PackedAttribDesc &packedAttrib = mVertexInputAttribs.attribs[attribIndex]; vk::PackedAttribDesc &packedAttrib = mVertexInputAttribs.attribs[attribIndex];
...@@ -1356,6 +1359,7 @@ void GraphicsPipelineDesc::updateVertexInput(GraphicsPipelineTransitionBits *tra ...@@ -1356,6 +1359,7 @@ void GraphicsPipelineDesc::updateVertexInput(GraphicsPipelineTransitionBits *tra
} }
SetBitField(packedAttrib.format, format); SetBitField(packedAttrib.format, format);
SetBitField(packedAttrib.compressed, compressed);
SetBitField(packedAttrib.offset, relativeOffset); SetBitField(packedAttrib.offset, relativeOffset);
constexpr size_t kAttribBits = kPackedAttribDescSize * kBitsPerByte; constexpr size_t kAttribBits = kPackedAttribDescSize * kBitsPerByte;
......
...@@ -310,8 +310,10 @@ struct PackedAttribDesc final ...@@ -310,8 +310,10 @@ struct PackedAttribDesc final
uint8_t format; uint8_t format;
uint8_t divisor; uint8_t divisor;
// Can only take 11 bits on NV. // Desktop drivers support
uint16_t offset; uint16_t offset : kAttributeOffsetMaxBits;
uint16_t compressed : 1;
// Although technically stride can be any value in ES 2.0, in practice supporting stride // Although technically stride can be any value in ES 2.0, in practice supporting stride
// greater than MAX_USHORT should not be that helpful. Note that stride limits are // greater than MAX_USHORT should not be that helpful. Note that stride limits are
...@@ -519,6 +521,7 @@ class GraphicsPipelineDesc final ...@@ -519,6 +521,7 @@ class GraphicsPipelineDesc final
GLuint stride, GLuint stride,
GLuint divisor, GLuint divisor,
angle::FormatID format, angle::FormatID format,
bool compressed,
GLuint relativeOffset); GLuint relativeOffset);
// Input assembly info // Input assembly info
......
...@@ -238,8 +238,7 @@ void RendererVk::ensureCapsInitialized() const ...@@ -238,8 +238,7 @@ void RendererVk::ensureCapsInitialized() const
mNativeCaps.maxVertexAttribBindings = LimitToInt(limitsVk.maxVertexInputBindings); mNativeCaps.maxVertexAttribBindings = LimitToInt(limitsVk.maxVertexInputBindings);
// Offset and stride are stored as uint16_t in PackedAttribDesc. // Offset and stride are stored as uint16_t in PackedAttribDesc.
mNativeCaps.maxVertexAttribRelativeOffset = mNativeCaps.maxVertexAttribRelativeOffset =
std::min(static_cast<uint32_t>(std::numeric_limits<uint16_t>::max()), std::min((1u << kAttributeOffsetMaxBits) - 1, limitsVk.maxVertexInputAttributeOffset);
limitsVk.maxVertexInputAttributeOffset);
mNativeCaps.maxVertexAttribStride = mNativeCaps.maxVertexAttribStride =
std::min(static_cast<uint32_t>(std::numeric_limits<uint16_t>::max()), std::min(static_cast<uint32_t>(std::numeric_limits<uint16_t>::max()),
limitsVk.maxVertexInputBindingStride); limitsVk.maxVertexInputBindingStride);
......
...@@ -258,6 +258,15 @@ ...@@ -258,6 +258,15 @@
"R32G32B32A32_SNORM": { "R32G32B32A32_SNORM": {
"buffer": "R32G32B32A32_FLOAT" "buffer": "R32G32B32A32_FLOAT"
}, },
"R32G32B32A32_FLOAT": {
"buffer_compressed": "R16G16B16A16_FLOAT"
},
"R32G32_FLOAT": {
"buffer_compressed": "R16G16_FLOAT"
},
"R32_FLOAT": {
"buffer_compressed": "R16_FLOAT"
},
"R32_USCALED": { "R32_USCALED": {
"buffer": "R32_FLOAT" "buffer": "R32_FLOAT"
}, },
...@@ -466,7 +475,8 @@ ...@@ -466,7 +475,8 @@
"buffer": "R16G16B16A16_FLOAT" "buffer": "R16G16B16A16_FLOAT"
}, },
"R32G32B32_FLOAT": { "R32G32B32_FLOAT": {
"image": "R32G32B32A32_FLOAT" "image": "R32G32B32A32_FLOAT",
"buffer_compressed": "R16G16B16A16_FLOAT"
}, },
"ETC2_R8G8B8_UNORM_BLOCK": { "ETC2_R8G8B8_UNORM_BLOCK": {
"image": "R8G8B8A8_UNORM" "image": "R8G8B8A8_UNORM"
......
...@@ -110,9 +110,14 @@ Format::Format() ...@@ -110,9 +110,14 @@ Format::Format()
vkImageFormat(VK_FORMAT_UNDEFINED), vkImageFormat(VK_FORMAT_UNDEFINED),
actualBufferFormatID(angle::FormatID::NONE), actualBufferFormatID(angle::FormatID::NONE),
vkBufferFormat(VK_FORMAT_UNDEFINED), vkBufferFormat(VK_FORMAT_UNDEFINED),
actualCompressedBufferFormatID(angle::FormatID::NONE),
vkCompressedBufferFormat(VK_FORMAT_UNDEFINED),
imageInitializerFunction(nullptr), imageInitializerFunction(nullptr),
textureLoadFunctions(), textureLoadFunctions(),
vertexLoadFunction(nullptr),
compressedVertexLoadFunction(nullptr),
vertexLoadRequiresConversion(false), vertexLoadRequiresConversion(false),
compressedVertexLoadRequiresConversion(false),
vkBufferFormatIsPacked(false), vkBufferFormatIsPacked(false),
vkFormatIsInt(false), vkFormatIsInt(false),
vkFormatIsUnsigned(false) vkFormatIsUnsigned(false)
...@@ -145,17 +150,34 @@ void Format::initImageFallback(RendererVk *renderer, const ImageFormatInitInfo * ...@@ -145,17 +150,34 @@ void Format::initImageFallback(RendererVk *renderer, const ImageFormatInitInfo *
imageInitializerFunction = info[i].initializer; imageInitializerFunction = info[i].initializer;
} }
void Format::initBufferFallback(RendererVk *renderer, const BufferFormatInitInfo *info, int numInfo) void Format::initBufferFallback(RendererVk *renderer,
const BufferFormatInitInfo *info,
int numInfo,
int compressedStartIndex)
{ {
size_t skip = renderer->getFeatures().forceFallbackFormat.enabled ? 1 : 0; {
int i = FindSupportedFormat(renderer, info, skip, static_cast<uint32_t>(numInfo), size_t skip = renderer->getFeatures().forceFallbackFormat.enabled ? 1 : 0;
HasFullBufferFormatSupport); int i = FindSupportedFormat(renderer, info, skip, compressedStartIndex,
HasFullBufferFormatSupport);
actualBufferFormatID = info[i].format;
vkBufferFormat = info[i].vkFormat; actualBufferFormatID = info[i].format;
vkBufferFormatIsPacked = info[i].vkFormatIsPacked; vkBufferFormat = info[i].vkFormat;
vertexLoadFunction = info[i].vertexLoadFunction; vkBufferFormatIsPacked = info[i].vkFormatIsPacked;
vertexLoadRequiresConversion = info[i].vertexLoadRequiresConversion; vertexLoadFunction = info[i].vertexLoadFunction;
vertexLoadRequiresConversion = info[i].vertexLoadRequiresConversion;
}
if (renderer->getFeatures().compressVertexData.enabled && compressedStartIndex < numInfo)
{
int i = FindSupportedFormat(renderer, info, compressedStartIndex, numInfo,
HasFullBufferFormatSupport);
actualCompressedBufferFormatID = info[i].format;
vkCompressedBufferFormat = info[i].vkFormat;
vkCompressedBufferFormatIsPacked = info[i].vkFormatIsPacked;
compressedVertexLoadFunction = info[i].vertexLoadFunction;
compressedVertexLoadRequiresConversion = info[i].vertexLoadRequiresConversion;
}
} }
size_t Format::getImageCopyBufferAlignment() const size_t Format::getImageCopyBufferAlignment() const
...@@ -332,9 +354,9 @@ bool HasNonRenderableTextureFormatSupport(RendererVk *renderer, VkFormat vkForma ...@@ -332,9 +354,9 @@ bool HasNonRenderableTextureFormatSupport(RendererVk *renderer, VkFormat vkForma
renderer->hasImageFormatFeatureBits(vkFormat, kBitsDepth); renderer->hasImageFormatFeatureBits(vkFormat, kBitsDepth);
} }
size_t GetVertexInputAlignment(const vk::Format &format) size_t GetVertexInputAlignment(const vk::Format &format, bool compressed)
{ {
const angle::Format &bufferFormat = format.actualBufferFormat(); const angle::Format &bufferFormat = format.actualBufferFormat(compressed);
size_t pixelBytes = bufferFormat.pixelBytes; size_t pixelBytes = bufferFormat.pixelBytes;
return format.vkBufferFormatIsPacked ? pixelBytes : (pixelBytes / bufferFormat.channelCount); return format.vkBufferFormatIsPacked ? pixelBytes : (pixelBytes / bufferFormat.channelCount);
} }
......
...@@ -70,9 +70,20 @@ struct Format final : private angle::NonCopyable ...@@ -70,9 +70,20 @@ struct Format final : private angle::NonCopyable
} }
// The actual Buffer format is used to implement the front-end format for Buffers. // The actual Buffer format is used to implement the front-end format for Buffers.
const angle::Format &actualBufferFormat() const const angle::Format &actualBufferFormat(bool compressed) const
{ {
return angle::Format::Get(actualBufferFormatID); return angle::Format::Get(compressed ? actualCompressedBufferFormatID
: actualBufferFormatID);
}
VertexCopyFunction getVertexLoadFunction(bool compressed) const
{
return compressed ? compressedVertexLoadFunction : vertexLoadFunction;
}
bool getVertexLoadRequiresConversion(bool compressed) const
{
return compressed ? compressedVertexLoadRequiresConversion : vertexLoadRequiresConversion;
} }
// The |internalFormat| always correponds to a valid GLenum type. For types that don't have a // The |internalFormat| always correponds to a valid GLenum type. For types that don't have a
...@@ -94,7 +105,10 @@ struct Format final : private angle::NonCopyable ...@@ -94,7 +105,10 @@ struct Format final : private angle::NonCopyable
// These are used in the format table init. // These are used in the format table init.
void initImageFallback(RendererVk *renderer, const ImageFormatInitInfo *info, int numInfo); void initImageFallback(RendererVk *renderer, const ImageFormatInitInfo *info, int numInfo);
void initBufferFallback(RendererVk *renderer, const BufferFormatInitInfo *info, int numInfo); void initBufferFallback(RendererVk *renderer,
const BufferFormatInitInfo *fallbackInfo,
int numInfo,
int compressedStartIndex);
angle::FormatID intendedFormatID; angle::FormatID intendedFormatID;
GLenum internalFormat; GLenum internalFormat;
...@@ -102,13 +116,18 @@ struct Format final : private angle::NonCopyable ...@@ -102,13 +116,18 @@ struct Format final : private angle::NonCopyable
VkFormat vkImageFormat; VkFormat vkImageFormat;
angle::FormatID actualBufferFormatID; angle::FormatID actualBufferFormatID;
VkFormat vkBufferFormat; VkFormat vkBufferFormat;
angle::FormatID actualCompressedBufferFormatID;
VkFormat vkCompressedBufferFormat;
InitializeTextureDataFunction imageInitializerFunction; InitializeTextureDataFunction imageInitializerFunction;
LoadFunctionMap textureLoadFunctions; LoadFunctionMap textureLoadFunctions;
VertexCopyFunction vertexLoadFunction; VertexCopyFunction vertexLoadFunction;
VertexCopyFunction compressedVertexLoadFunction;
bool vertexLoadRequiresConversion; bool vertexLoadRequiresConversion;
bool compressedVertexLoadRequiresConversion;
bool vkBufferFormatIsPacked; bool vkBufferFormatIsPacked;
bool vkCompressedBufferFormatIsPacked;
bool vkFormatIsInt; bool vkFormatIsInt;
bool vkFormatIsUnsigned; bool vkFormatIsUnsigned;
}; };
...@@ -162,7 +181,7 @@ bool HasNonRenderableTextureFormatSupport(RendererVk *renderer, VkFormat vkForma ...@@ -162,7 +181,7 @@ bool HasNonRenderableTextureFormatSupport(RendererVk *renderer, VkFormat vkForma
// Returns the alignment for a buffer to be used with the vertex input stage in Vulkan. This // Returns the alignment for a buffer to be used with the vertex input stage in Vulkan. This
// calculation is listed in the Vulkan spec at the end of the section 'Vertex Input Description'. // calculation is listed in the Vulkan spec at the end of the section 'Vertex Input Description'.
size_t GetVertexInputAlignment(const vk::Format &format); size_t GetVertexInputAlignment(const vk::Format &format, bool compressed);
// Get the swizzle state based on format's requirements and emulations. // Get the swizzle state based on format's requirements and emulations.
gl::SwizzleState GetFormatSwizzle(const ContextVk *contextVk, gl::SwizzleState GetFormatSwizzle(const ContextVk *contextVk,
......
...@@ -63,6 +63,7 @@ namespace ...@@ -63,6 +63,7 @@ namespace
#include "libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000005.inc" #include "libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000005.inc"
#include "libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000006.inc" #include "libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000006.inc"
#include "libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000007.inc" #include "libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000007.inc"
#include "libANGLE/renderer/vulkan/shaders/gen/ConvertVertex.comp.00000008.inc"
#include "libANGLE/renderer/vulkan/shaders/gen/FullScreenQuad.vert.00000000.inc" #include "libANGLE/renderer/vulkan/shaders/gen/FullScreenQuad.vert.00000000.inc"
#include "libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000000.inc" #include "libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000000.inc"
#include "libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000001.inc" #include "libANGLE/renderer/vulkan/shaders/gen/GenerateMipmap.comp.00000001.inc"
...@@ -199,6 +200,7 @@ constexpr CompressedShaderBlob kConvertVertex_comp_shaders[] = { ...@@ -199,6 +200,7 @@ constexpr CompressedShaderBlob kConvertVertex_comp_shaders[] = {
{kConvertVertex_comp_00000005, sizeof(kConvertVertex_comp_00000005)}, {kConvertVertex_comp_00000005, sizeof(kConvertVertex_comp_00000005)},
{kConvertVertex_comp_00000006, sizeof(kConvertVertex_comp_00000006)}, {kConvertVertex_comp_00000006, sizeof(kConvertVertex_comp_00000006)},
{kConvertVertex_comp_00000007, sizeof(kConvertVertex_comp_00000007)}, {kConvertVertex_comp_00000007, sizeof(kConvertVertex_comp_00000007)},
{kConvertVertex_comp_00000008, sizeof(kConvertVertex_comp_00000008)},
}; };
constexpr CompressedShaderBlob kFullScreenQuad_vert_shaders[] = { constexpr CompressedShaderBlob kFullScreenQuad_vert_shaders[] = {
{kFullScreenQuad_vert_00000000, sizeof(kFullScreenQuad_vert_00000000)}, {kFullScreenQuad_vert_00000000, sizeof(kFullScreenQuad_vert_00000000)},
......
...@@ -53,6 +53,7 @@ angle_vulkan_internal_shaders = [ ...@@ -53,6 +53,7 @@ angle_vulkan_internal_shaders = [
"shaders/gen/ConvertVertex.comp.00000005.inc", "shaders/gen/ConvertVertex.comp.00000005.inc",
"shaders/gen/ConvertVertex.comp.00000006.inc", "shaders/gen/ConvertVertex.comp.00000006.inc",
"shaders/gen/ConvertVertex.comp.00000007.inc", "shaders/gen/ConvertVertex.comp.00000007.inc",
"shaders/gen/ConvertVertex.comp.00000008.inc",
"shaders/gen/FullScreenQuad.vert.00000000.inc", "shaders/gen/FullScreenQuad.vert.00000000.inc",
"shaders/gen/GenerateMipmap.comp.00000000.inc", "shaders/gen/GenerateMipmap.comp.00000000.inc",
"shaders/gen/GenerateMipmap.comp.00000001.inc", "shaders/gen/GenerateMipmap.comp.00000001.inc",
......
...@@ -86,8 +86,9 @@ enum Conversion ...@@ -86,8 +86,9 @@ enum Conversion
kUnormToFloat = 0x00000005, kUnormToFloat = 0x00000005,
kFixedToFloat = 0x00000006, kFixedToFloat = 0x00000006,
kFloatToFloat = 0x00000007, kFloatToFloat = 0x00000007,
kFloatToHalf = 0x00000008,
}; };
constexpr size_t kArrayLen = 0x00000008; constexpr size_t kArrayLen = 0x00000009;
} // namespace ConvertVertex_comp } // namespace ConvertVertex_comp
namespace FullScreenQuad_vert namespace FullScreenQuad_vert
......
...@@ -103,6 +103,10 @@ enum class TextureDimension ...@@ -103,6 +103,10 @@ enum class TextureDimension
TEX_2D_ARRAY, TEX_2D_ARRAY,
}; };
// A maximum offset of 4096 covers almost every Vulkan driver on desktop (80%) and mobile (99%). The
// next highest values to meet native drivers are 16 bits or 32 bits.
constexpr uint32_t kAttributeOffsetMaxBits = 15;
namespace vk namespace vk
{ {
struct Format; struct Format;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment