Hash :
67a5ea45
Author :
Date :
2024-09-23T16:09:12
Vulkan: Fix the error from multiple lineloop draws
Since Vulkan does not support line-loop draws natively, such a
draw call requires the conversion of the related buffers to prepare
them for this operation. For glDrawElementsIndirect(), the index
and the indirect buffers would need conversion.
However, what currently happens in this case is that the original
buffer pointer is overwritten after the conversion, removing the
link to the original buffer. Therefore, if there is a second line-loop
call just after the first, it will try to use the converted buffer as
the new source, which leads to errors due the buffer already being in
use.
The index buffer for the draw is bound when the related dirty bit is
handled. Therefore, instead of using the draw index buffer directly
for handling the line-loop scenario, we can use the index buffer in
the form of a local pointer passed between functions. Then, in order
to reconcile line-loop with the other cases, the draw index buffer is
set just before setting up the indexed draw.
* Functions handling line-loop draws do not modify the element array
buffer in VertexArrayVk directly, but use local buffer pointers to
pass the current element array pointer to further processing and
drawing.
* Added mCurrentElementArrayBuffer for ContextVk to be bound to the
index buffer to used for draw instead of the one from its vertex
array object.
* Before the indexed draw, mCurrentElementArrayBuffer is set to the
last destination index buffer.
* Added unit test that makes a line-loop draw and then a non-LL call
using the same element array.
Bug: angleproject:360758685
Change-Id: I6d6328f6326c1a1f9f80e5ef346aa077c867d344
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/5878764
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
Commit-Queue: Amirali Abdolrashidi <abdolrashidi@google.com>
Reviewed-by: Charlie Lao <cclao@google.com>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
//
// Copyright 2016 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// VertexArrayVk.h:
// Defines the class interface for VertexArrayVk, implementing VertexArrayImpl.
//
#ifndef LIBANGLE_RENDERER_VULKAN_VERTEXARRAYVK_H_
#define LIBANGLE_RENDERER_VULKAN_VERTEXARRAYVK_H_
#include "libANGLE/renderer/VertexArrayImpl.h"
#include "libANGLE/renderer/vulkan/UtilsVk.h"
#include "libANGLE/renderer/vulkan/vk_cache_utils.h"
#include "libANGLE/renderer/vulkan/vk_helpers.h"
namespace rx
{
enum class BufferBindingDirty
{
No,
Yes,
};
struct AttributeRange
{
// Stream vertex attribute start pointer address.
uintptr_t startAddr;
// Stream vertex attribute end pointer address.
uintptr_t endAddr;
// Stream vertex attribute first used pointer address.
// ie. startAddr + startVertex * stride.
uintptr_t copyStartAddr;
AttributeRange() : startAddr(0), endAddr(0), copyStartAddr(0) {}
AttributeRange(uintptr_t start, uintptr_t end, uintptr_t copyStart)
: startAddr(start), endAddr(end), copyStartAddr(copyStart)
{}
};
ANGLE_INLINE bool operator<(const AttributeRange &a, const AttributeRange &b)
{
return a.startAddr == b.startAddr ? a.endAddr < b.endAddr : a.startAddr < b.startAddr;
}
class VertexArrayVk : public VertexArrayImpl
{
public:
VertexArrayVk(ContextVk *contextVk, const gl::VertexArrayState &state);
~VertexArrayVk() override;
void destroy(const gl::Context *context) override;
angle::Result syncState(const gl::Context *context,
const gl::VertexArray::DirtyBits &dirtyBits,
gl::VertexArray::DirtyAttribBitsArray *attribBits,
gl::VertexArray::DirtyBindingBitsArray *bindingBits) override;
angle::Result updateActiveAttribInfo(ContextVk *contextVk);
angle::Result updateDefaultAttrib(ContextVk *contextVk, size_t attribIndex);
angle::Result updateStreamedAttribs(const gl::Context *context,
GLint firstVertex,
GLsizei vertexOrIndexCount,
GLsizei instanceCount,
gl::DrawElementsType indexTypeOrInvalid,
const void *indices);
angle::Result handleLineLoop(ContextVk *contextVk,
GLint firstVertex,
GLsizei vertexOrIndexCount,
gl::DrawElementsType indexTypeOrInvalid,
const void *indices,
vk::BufferHelper **indexBufferOut,
uint32_t *indexCountOut);
angle::Result handleLineLoopIndexIndirect(ContextVk *contextVk,
gl::DrawElementsType glIndexType,
vk::BufferHelper *srcIndexBuffer,
vk::BufferHelper *srcIndirectBuffer,
VkDeviceSize indirectBufferOffset,
vk::BufferHelper **indexBufferOut,
vk::BufferHelper **indirectBufferOut);
angle::Result handleLineLoopIndirectDraw(const gl::Context *context,
vk::BufferHelper *indirectBufferVk,
VkDeviceSize indirectBufferOffset,
vk::BufferHelper **indexBufferOut,
vk::BufferHelper **indirectBufferOut);
const gl::AttribArray<VkBuffer> &getCurrentArrayBufferHandles() const
{
return mCurrentArrayBufferHandles;
}
const gl::AttribArray<VkDeviceSize> &getCurrentArrayBufferOffsets() const
{
return mCurrentArrayBufferOffsets;
}
const gl::AttribArray<GLuint> &getCurrentArrayBufferRelativeOffsets() const
{
return mCurrentArrayBufferRelativeOffsets;
}
const gl::AttribArray<vk::BufferHelper *> &getCurrentArrayBuffers() const
{
return mCurrentArrayBuffers;
}
const gl::AttribArray<angle::FormatID> &getCurrentArrayBufferFormats() const
{
return mCurrentArrayBufferFormats;
}
const gl::AttribArray<GLuint> &getCurrentArrayBufferStrides() const
{
return mCurrentArrayBufferStrides;
}
const gl::AttribArray<GLuint> &getCurrentArrayBufferDivisors() const
{
return mCurrentArrayBufferDivisors;
}
const gl::AttributesMask &getCurrentArrayBufferCompressed() const
{
return mCurrentArrayBufferCompressed;
}
// Update mCurrentElementArrayBuffer based on the vertex array state
void updateCurrentElementArrayBuffer();
vk::BufferHelper *getCurrentElementArrayBuffer() const { return mCurrentElementArrayBuffer; }
angle::Result convertIndexBufferGPU(ContextVk *contextVk,
BufferVk *bufferVk,
const void *indices);
angle::Result convertIndexBufferIndirectGPU(ContextVk *contextVk,
vk::BufferHelper *srcIndirectBuf,
VkDeviceSize srcIndirectBufOffset,
vk::BufferHelper **indirectBufferVkOut);
angle::Result convertIndexBufferCPU(ContextVk *contextVk,
gl::DrawElementsType indexType,
size_t indexCount,
const void *sourcePointer,
BufferBindingDirty *bufferBindingDirty);
const gl::AttributesMask &getStreamingVertexAttribsMask() const
{
return mStreamingVertexAttribsMask;
}
private:
gl::AttributesMask mergeClientAttribsRange(
vk::Renderer *renderer,
const gl::AttributesMask activeStreamedAttribs,
size_t startVertex,
size_t endVertex,
std::array<AttributeRange, gl::MAX_VERTEX_ATTRIBS> &mergeRangesOut,
std::array<size_t, gl::MAX_VERTEX_ATTRIBS> &mergedIndexesOut) const;
angle::Result setDefaultPackedInput(ContextVk *contextVk,
size_t attribIndex,
angle::FormatID *formatOut);
angle::Result convertVertexBufferGPU(ContextVk *contextVk,
BufferVk *srcBuffer,
VertexConversionBuffer *conversion,
const angle::Format &srcFormat,
const angle::Format &dstFormat);
angle::Result convertVertexBufferCPU(ContextVk *contextVk,
BufferVk *srcBuffer,
VertexConversionBuffer *conversion,
const angle::Format &srcFormat,
const angle::Format &dstFormat,
const VertexCopyFunction vertexLoadFunction);
angle::Result syncDirtyAttrib(ContextVk *contextVk,
const gl::VertexAttribute &attrib,
const gl::VertexBinding &binding,
size_t attribIndex,
bool bufferOnly);
gl::AttribArray<VkBuffer> mCurrentArrayBufferHandles;
gl::AttribArray<VkDeviceSize> mCurrentArrayBufferOffsets;
// The offset into the buffer to the first attrib
gl::AttribArray<GLuint> mCurrentArrayBufferRelativeOffsets;
gl::AttribArray<vk::BufferHelper *> mCurrentArrayBuffers;
// Tracks BufferSerial of mCurrentArrayBuffers since they are always valid to access.
gl::AttribArray<vk::BufferSerial> mCurrentArrayBufferSerial;
// Cache strides of attributes for a fast pipeline cache update when VAOs are changed
gl::AttribArray<angle::FormatID> mCurrentArrayBufferFormats;
gl::AttribArray<GLuint> mCurrentArrayBufferStrides;
gl::AttribArray<GLuint> mCurrentArrayBufferDivisors;
gl::AttributesMask mCurrentArrayBufferCompressed;
vk::BufferHelper *mCurrentElementArrayBuffer;
// Cached element array buffers for improving performance.
vk::BufferHelperQueue mCachedStreamIndexBuffers;
ConversionBuffer mStreamedIndexData;
ConversionBuffer mTranslatedByteIndexData;
ConversionBuffer mTranslatedByteIndirectData;
LineLoopHelper mLineLoopHelper;
Optional<GLint> mLineLoopBufferFirstIndex;
Optional<size_t> mLineLoopBufferLastIndex;
bool mDirtyLineLoopTranslation;
// Track client and/or emulated attribs that we have to stream their buffer contents
gl::AttributesMask mStreamingVertexAttribsMask;
// The attrib/binding dirty bits that requires graphics pipeline update
gl::VertexArray::DirtyBindingBits mBindingDirtyBitsRequiresPipelineUpdate;
gl::VertexArray::DirtyAttribBits mAttribDirtyBitsRequiresPipelineUpdate;
};
} // namespace rx
#endif // LIBANGLE_RENDERER_VULKAN_VERTEXARRAYVK_H_