Commit 199a9f38 by Frank Henigman Committed by Commit Bot

Vulkan: support instanced draws.

Enable instanced draws with the Vulkan backend. So far it only works when Vulkan has VK_EXT_vertex_attribute_divisor. BUG=angleproject:2672 Change-Id: I9445ba64282fa00a6eaee207b15efa2c7a9abbd3 Reviewed-on: https://chromium-review.googlesource.com/c/1334973 Commit-Queue: Frank Henigman <fjhenigman@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 15335ac4
......@@ -199,6 +199,7 @@ angle::Result ContextVk::setupDraw(const gl::Context *context,
gl::PrimitiveMode mode,
GLint firstVertex,
GLsizei vertexOrIndexCount,
GLsizei instanceCount,
gl::DrawElementsType indexTypeOrNone,
const void *indices,
DirtyBits dirtyBitMask,
......@@ -216,7 +217,7 @@ angle::Result ContextVk::setupDraw(const gl::Context *context,
if (context->getStateCache().hasAnyActiveClientAttrib())
{
ANGLE_TRY(mVertexArray->updateClientAttribs(context, firstVertex, vertexOrIndexCount,
indexTypeOrNone, indices));
instanceCount, indexTypeOrNone, indices));
mDirtyBits.set(DIRTY_BIT_VERTEX_BUFFERS);
}
......@@ -266,6 +267,7 @@ angle::Result ContextVk::setupDraw(const gl::Context *context,
angle::Result ContextVk::setupIndexedDraw(const gl::Context *context,
gl::PrimitiveMode mode,
GLsizei indexCount,
GLsizei instanceCount,
gl::DrawElementsType indexType,
const void *indices,
vk::CommandBuffer **commandBufferOut)
......@@ -297,8 +299,8 @@ angle::Result ContextVk::setupIndexedDraw(const gl::Context *context,
}
}
return setupDraw(context, mode, 0, indexCount, indexType, indices, mIndexedDirtyBitsMask,
commandBufferOut);
return setupDraw(context, mode, 0, indexCount, instanceCount, indexType, indices,
mIndexedDirtyBitsMask, commandBufferOut);
}
angle::Result ContextVk::setupLineLoopDraw(const gl::Context *context,
......@@ -315,7 +317,7 @@ angle::Result ContextVk::setupLineLoopDraw(const gl::Context *context,
mCurrentDrawElementsType = indexTypeOrInvalid != gl::DrawElementsType::InvalidEnum
? indexTypeOrInvalid
: gl::DrawElementsType::UnsignedInt;
return setupDraw(context, mode, firstVertex, vertexOrIndexCount, indexTypeOrInvalid, indices,
return setupDraw(context, mode, firstVertex, vertexOrIndexCount, 1, indexTypeOrInvalid, indices,
mIndexedDirtyBitsMask, commandBufferOut);
}
......@@ -455,8 +457,8 @@ angle::Result ContextVk::drawArrays(const gl::Context *context,
}
else
{
ANGLE_TRY(setupDraw(context, mode, first, count, gl::DrawElementsType::InvalidEnum, nullptr,
mNonIndexedDirtyBitsMask, &commandBuffer));
ANGLE_TRY(setupDraw(context, mode, first, count, 1, gl::DrawElementsType::InvalidEnum,
nullptr, mNonIndexedDirtyBitsMask, &commandBuffer));
commandBuffer->draw(clampedVertexCount, 1, first, 0);
}
......@@ -467,10 +469,20 @@ angle::Result ContextVk::drawArraysInstanced(const gl::Context *context,
gl::PrimitiveMode mode,
GLint first,
GLsizei count,
GLsizei instanceCount)
GLsizei instances)
{
ANGLE_VK_UNREACHABLE(this);
return angle::Result::Stop;
if (mode == gl::PrimitiveMode::LineLoop)
{
// TODO - http://anglebug.com/2672
ANGLE_VK_UNREACHABLE(this);
return angle::Result::Stop;
}
vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(setupDraw(context, mode, first, count, instances, gl::DrawElementsType::InvalidEnum,
nullptr, mNonIndexedDirtyBitsMask, &commandBuffer));
commandBuffer->draw(gl::GetClampedVertexCount<uint32_t>(count), instances, first, 0);
return angle::Result::Continue;
}
angle::Result ContextVk::drawElements(const gl::Context *context,
......@@ -487,7 +499,7 @@ angle::Result ContextVk::drawElements(const gl::Context *context,
}
else
{
ANGLE_TRY(setupIndexedDraw(context, mode, count, type, indices, &commandBuffer));
ANGLE_TRY(setupIndexedDraw(context, mode, count, 1, type, indices, &commandBuffer));
commandBuffer->drawIndexed(count, 1, 0, 0, 0);
}
......@@ -501,8 +513,17 @@ angle::Result ContextVk::drawElementsInstanced(const gl::Context *context,
const void *indices,
GLsizei instances)
{
ANGLE_VK_UNREACHABLE(this);
return angle::Result::Stop;
if (mode == gl::PrimitiveMode::LineLoop)
{
// TODO - http://anglebug.com/2672
ANGLE_VK_UNREACHABLE(this);
return angle::Result::Stop;
}
vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(setupIndexedDraw(context, mode, count, instances, type, indices, &commandBuffer));
commandBuffer->drawIndexed(count, instances, 0, 0, 0);
return angle::Result::Continue;
}
angle::Result ContextVk::drawRangeElements(const gl::Context *context,
......
......@@ -61,7 +61,7 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::CommandBuff
GLsizei count,
gl::DrawElementsType type,
const void *indices,
GLsizei instances) override;
GLsizei instanceCount) override;
angle::Result drawRangeElements(const gl::Context *context,
gl::PrimitiveMode mode,
GLuint start,
......@@ -232,6 +232,7 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::CommandBuff
gl::PrimitiveMode mode,
GLint firstVertex,
GLsizei vertexOrIndexCount,
GLsizei instanceCount,
gl::DrawElementsType indexTypeOrInvalid,
const void *indices,
DirtyBits dirtyBitMask,
......@@ -239,6 +240,7 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::CommandBuff
angle::Result setupIndexedDraw(const gl::Context *context,
gl::PrimitiveMode mode,
GLsizei indexCount,
GLsizei instanceCount,
gl::DrawElementsType indexType,
const void *indices,
vk::CommandBuffer **commandBufferOut);
......
......@@ -205,6 +205,9 @@ class RendererVk : angle::NonCopyable
bool hasTextureFormatFeatureBits(VkFormat format, const VkFormatFeatureFlags featureBits);
bool hasBufferFormatFeatureBits(VkFormat format, const VkFormatFeatureFlags featureBits);
static constexpr size_t kMaxExtensionNames = 200;
using ExtensionNameList = angle::FixedVector<const char *, kMaxExtensionNames>;
private:
// Number of semaphores for external entities to renderer to issue a wait, such as surface's
// image acquire.
......@@ -224,7 +227,7 @@ class RendererVk : angle::NonCopyable
vk::CommandBuffer &&commandBuffer);
void freeAllInFlightResources();
angle::Result flushCommandGraph(vk::Context *context, vk::CommandBuffer *commandBatch);
void initFeatures(const std::vector<VkExtensionProperties> &deviceExtensionProps);
void initFeatures(const ExtensionNameList &extensions);
void initPipelineCacheVkKey();
angle::Result initPipelineCache(DisplayVk *display);
......@@ -262,6 +265,7 @@ class RendererVk : angle::NonCopyable
std::vector<VkQueueFamilyProperties> mQueueFamilyProperties;
VkQueue mQueue;
uint32_t mCurrentQueueFamilyIndex;
uint32_t mMaxVertexAttribDivisor;
VkDevice mDevice;
vk::CommandPool mCommandPool;
SerialFactory mQueueSerialFactory;
......
......@@ -492,6 +492,7 @@ angle::Result VertexArrayVk::syncDirtyAttrib(ContextVk *contextVk,
angle::Result VertexArrayVk::updateClientAttribs(const gl::Context *context,
GLint firstVertex,
GLsizei vertexOrIndexCount,
GLsizei instanceCount,
gl::DrawElementsType indexTypeOrInvalid,
const void *indices)
{
......@@ -522,22 +523,35 @@ angle::Result VertexArrayVk::updateClientAttribs(const gl::Context *context,
const vk::Format &vertexFormat = renderer->getFormat(GetVertexFormatID(attrib));
GLuint stride = vertexFormat.bufferFormat().pixelBytes;
const size_t bytesToAllocate = (startVertex + vertexCount) * stride;
const uint8_t *src =
static_cast<const uint8_t *>(attrib.pointer) + startVertex * binding.getStride();
size_t destOffset = startVertex * stride;
ASSERT(GetVertexInputAlignment(vertexFormat) <= kMaxVertexFormatAlignment);
// Only vertexCount() vertices will be used by the upcoming draw. so that is all we copy.
// We allocate space for startVertex + vertexCount so indexing will work. If we
// don't start at zero all the indices will be off.
// TODO(fjhenigman): See if we can account for indices being off by adjusting the
// offset, thus avoiding wasted memory.
ANGLE_TRY(StreamVertexData(
contextVk, &mDynamicVertexData, src, bytesToAllocate, destOffset, vertexCount,
binding.getStride(), vertexFormat.vertexLoadFunction,
&mCurrentArrayBuffers[attribIndex], &mCurrentArrayBufferOffsets[attribIndex]));
const uint8_t *src = static_cast<const uint8_t *>(attrib.pointer);
if (binding.getDivisor() > 0)
{
// instanced attrib
size_t count = UnsignedCeilDivide(instanceCount, binding.getDivisor());
size_t bytesToAllocate = count * stride;
ANGLE_TRY(StreamVertexData(contextVk, &mDynamicVertexData, src, bytesToAllocate, 0,
count, binding.getStride(), vertexFormat.vertexLoadFunction,
&mCurrentArrayBuffers[attribIndex],
&mCurrentArrayBufferOffsets[attribIndex]));
}
else
{
// Allocate space for startVertex + vertexCount so indexing will work. If we don't
// start at zero all the indices will be off.
// Only vertexCount vertices will be used by the upcoming draw so that is all we copy.
size_t bytesToAllocate = (startVertex + vertexCount) * stride;
src += startVertex * binding.getStride();
size_t destOffset = startVertex * stride;
ANGLE_TRY(StreamVertexData(
contextVk, &mDynamicVertexData, src, bytesToAllocate, destOffset, vertexCount,
binding.getStride(), vertexFormat.vertexLoadFunction,
&mCurrentArrayBuffers[attribIndex], &mCurrentArrayBufferOffsets[attribIndex]));
}
mCurrentArrayBufferHandles[attribIndex] =
mCurrentArrayBuffers[attribIndex]->getBuffer().getHandle();
}
......
......@@ -39,6 +39,7 @@ class VertexArrayVk : public VertexArrayImpl
angle::Result updateClientAttribs(const gl::Context *context,
GLint firstVertex,
GLsizei vertexOrIndexCount,
GLsizei instanceCount,
gl::DrawElementsType indexTypeOrInvalid,
const void *indices);
......
......@@ -536,6 +536,11 @@ angle::Result GraphicsPipelineDesc::initializePipeline(
sizeof(attributeDescs);
ANGLE_UNUSED_VARIABLE(unpackedSize);
gl::AttribArray<VkVertexInputBindingDivisorDescriptionEXT> divisorDesc;
VkPipelineVertexInputDivisorStateCreateInfoEXT divisorState = {};
divisorState.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT;
divisorState.pVertexBindingDivisors = divisorDesc.data();
for (size_t attribIndexSizeT : activeAttribLocationsMask)
{
const uint32_t attribIndex = static_cast<uint32_t>(attribIndexSizeT);
......@@ -545,8 +550,18 @@ angle::Result GraphicsPipelineDesc::initializePipeline(
const PackedAttribDesc &packedAttrib = mVertexInputAttribs.attribs[attribIndex];
bindingDesc.binding = attribIndex;
bindingDesc.inputRate = static_cast<VkVertexInputRate>(packedAttrib.inputRate);
bindingDesc.stride = static_cast<uint32_t>(packedAttrib.stride);
if (packedAttrib.divisor != 0)
{
bindingDesc.inputRate = static_cast<VkVertexInputRate>(VK_VERTEX_INPUT_RATE_INSTANCE);
divisorDesc[divisorState.vertexBindingDivisorCount].binding = bindingDesc.binding;
divisorDesc[divisorState.vertexBindingDivisorCount].divisor = packedAttrib.divisor;
++divisorState.vertexBindingDivisorCount;
}
else
{
bindingDesc.inputRate = static_cast<VkVertexInputRate>(VK_VERTEX_INPUT_RATE_VERTEX);
}
// The binding index could become more dynamic in ES 3.1.
attribDesc.binding = attribIndex;
......@@ -564,6 +579,8 @@ angle::Result GraphicsPipelineDesc::initializePipeline(
vertexInputState.pVertexBindingDescriptions = bindingDescs.data();
vertexInputState.vertexAttributeDescriptionCount = vertexAttribCount;
vertexInputState.pVertexAttributeDescriptions = attributeDescs.data();
if (divisorState.vertexBindingDivisorCount)
vertexInputState.pNext = &divisorState;
// Primitive topology is filled in at draw time.
inputAssemblyState.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
......@@ -696,12 +713,12 @@ void GraphicsPipelineDesc::updateVertexInput(GraphicsPipelineTransitionBits *tra
{
vk::PackedAttribDesc &packedAttrib = mVertexInputAttribs.attribs[attribIndex];
// TODO(http://anglebug.com/2672): This will need to be updated to support instancing.
ASSERT(divisor == 0);
// TODO: Handle the case where the divisor overflows the field that holds it.
// http://anglebug.com/2672
ASSERT(divisor <= std::numeric_limits<decltype(packedAttrib.divisor)>::max());
SetBitField(packedAttrib.stride, stride);
SetBitField(packedAttrib.inputRate,
divisor > 0 ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX);
SetBitField(packedAttrib.divisor, divisor);
if (format == VK_FORMAT_UNDEFINED)
{
......
......@@ -128,9 +128,8 @@ struct PackedAttribDesc final
{
uint8_t format;
// inputRate can also be used to store instancing divisors up to 255.
// TODO(http://anglebug.com/2672): Emulate divisors greater than UBYTE_MAX.
uint8_t inputRate;
uint8_t divisor;
// Can only take 11 bits on NV.
uint16_t offset;
......
......@@ -56,6 +56,10 @@ void RendererVk::ensureCapsInitialized() const
// TODO(geofflang): Support GL_OES_EGL_image_external_essl3. http://anglebug.com/2668
mNativeExtensions.eglImageExternalEssl3 = false;
// TODO: Enable this always and emulate instanced draws if any divisor exceeds the maximum
// supported. http://anglebug.com/2672
mNativeExtensions.instancedArraysANGLE = mMaxVertexAttribDivisor > 1;
// Only expose robust buffer access if the physical device supports it.
mNativeExtensions.robustBufferAccessBehavior = mPhysicalDeviceFeatures.robustBufferAccess;
......
......@@ -30,17 +30,6 @@ struct FeaturesVk;
class DisplayVk;
namespace vk
{
void GenerateCaps(const VkPhysicalDeviceProperties &physicalDeviceProperties,
const VkPhysicalDeviceFeatures &physicalDeviceFeatures,
const VkQueueFamilyProperties &queueFamilyProperties,
const gl::TextureCapsMap &textureCaps,
gl::Caps *outCaps,
gl::Extensions *outExtensions,
gl::Limitations * /* outLimitations */);
} // namespace vk
namespace egl_vk
{
constexpr GLenum kConfigDepthStencilFormats[] = {GL_NONE, GL_DEPTH24_STENCIL8, GL_DEPTH_COMPONENT24,
......
......@@ -480,6 +480,9 @@ PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT = nullptr;
PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT = nullptr;
PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT = nullptr;
// VK_KHR_get_physical_device_properties2
PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR = nullptr;
#define GET_FUNC(vkName) \
do \
{ \
......@@ -499,6 +502,11 @@ void InitDebugReportEXTFunctions(VkInstance instance)
GET_FUNC(vkDestroyDebugReportCallbackEXT);
}
void InitGetPhysicalDeviceProperties2KHRFunctions(VkInstance instance)
{
GET_FUNC(vkGetPhysicalDeviceProperties2KHR);
}
#undef GET_FUNC
namespace gl_vk
......
......@@ -387,9 +387,13 @@ extern PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT;
extern PFN_vkCreateDebugReportCallbackEXT vkCreateDebugReportCallbackEXT;
extern PFN_vkDestroyDebugReportCallbackEXT vkDestroyDebugReportCallbackEXT;
// VK_KHR_get_physical_device_properties2
extern PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR;
// Lazily load entry points for each extension as necessary.
void InitDebugUtilsEXTFunctions(VkInstance instance);
void InitDebugReportEXTFunctions(VkInstance instance);
void InitGetPhysicalDeviceProperties2KHRFunctions(VkInstance instance);
namespace gl_vk
{
......
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