Commit 88fc6da3 by Jamie Madill Committed by Commit Bot

Vulkan: Mega-refactor to VertexArrayVk.

This moves a lot of the code in VertexArrayVk into ContextVk. Having the code in a centralized place makes the code a bit more organized since the Context is reponsible for binding state to the command buffers. It also makes it easier to use dirty bits to track the command buffer state. Bug: angleproject:2786 Change-Id: I5cefbb14028e8f3fe651f26e997ca88f8f1c7628 Reviewed-on: https://chromium-review.googlesource.com/1188953 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 253038d8
...@@ -98,6 +98,7 @@ class StateCache final : angle::NonCopyable ...@@ -98,6 +98,7 @@ class StateCache final : angle::NonCopyable
AttributesMask getActiveClientAttribsMask() const { return mCachedActiveClientAttribsMask; } AttributesMask getActiveClientAttribsMask() const { return mCachedActiveClientAttribsMask; }
AttributesMask getActiveDefaultAttribsMask() const { return mCachedActiveDefaultAttribsMask; } AttributesMask getActiveDefaultAttribsMask() const { return mCachedActiveDefaultAttribsMask; }
bool hasAnyEnabledClientAttrib() const { return mCachedHasAnyEnabledClientAttrib; } bool hasAnyEnabledClientAttrib() const { return mCachedHasAnyEnabledClientAttrib; }
bool hasAnyActiveClientAttrib() const { return mCachedActiveClientAttribsMask.any(); }
// Places that can trigger updateVertexElementLimits: // Places that can trigger updateVertexElementLimits:
// 1. onVertexArrayBindingChange. // 1. onVertexArrayBindingChange.
......
...@@ -50,6 +50,45 @@ GLenum DefaultGLErrorCode(VkResult result) ...@@ -50,6 +50,45 @@ GLenum DefaultGLErrorCode(VkResult result)
} }
} }
void BindNonNullVertexBufferRanges(vk::CommandBuffer *commandBuffer,
const gl::AttributesMask &nonNullAttribMask,
uint32_t maxAttrib,
const gl::AttribArray<VkBuffer> &arrayBufferHandles,
const gl::AttribArray<VkDeviceSize> &arrayBufferOffsets)
{
// Vulkan does not allow binding a null vertex buffer but the default state of null buffers is
// valid.
// We can detect if there are no gaps in active attributes by using the mask of the program
// attribs and the max enabled attrib.
ASSERT(maxAttrib > 0);
if (nonNullAttribMask.to_ulong() == (maxAttrib - 1))
{
commandBuffer->bindVertexBuffers(0, maxAttrib, arrayBufferHandles.data(),
arrayBufferOffsets.data());
return;
}
// Find ranges of non-null buffers and bind them all together.
for (uint32_t attribIdx = 0; attribIdx < maxAttrib; attribIdx++)
{
if (arrayBufferHandles[attribIdx] != VK_NULL_HANDLE)
{
// Find the end of this range of non-null handles
uint32_t rangeCount = 1;
while (attribIdx + rangeCount < maxAttrib &&
arrayBufferHandles[attribIdx + rangeCount] != VK_NULL_HANDLE)
{
rangeCount++;
}
commandBuffer->bindVertexBuffers(attribIdx, rangeCount, &arrayBufferHandles[attribIdx],
&arrayBufferOffsets[attribIdx]);
attribIdx += rangeCount;
}
}
}
constexpr gl::Rectangle kMaxSizedScissor(0, constexpr gl::Rectangle kMaxSizedScissor(0,
0, 0,
std::numeric_limits<int>::max(), std::numeric_limits<int>::max(),
...@@ -74,8 +113,14 @@ ContextVk::ContextVk(const gl::ContextState &state, RendererVk *renderer) ...@@ -74,8 +113,14 @@ ContextVk::ContextVk(const gl::ContextState &state, RendererVk *renderer)
: ContextImpl(state), : ContextImpl(state),
vk::Context(renderer), vk::Context(renderer),
mCurrentDrawMode(gl::PrimitiveMode::InvalidEnum), mCurrentDrawMode(gl::PrimitiveMode::InvalidEnum),
mDirtyDefaultAttribs(false),
mPipelineDirty(false),
mTexturesDirty(false), mTexturesDirty(false),
mVertexArrayBindingHasChanged(false), mVertexBuffersDirty(false),
mIndexBufferDirty(false),
mDescriptorSetsDirty(false),
mLastIndexBufferOffset(0),
mCurrentDrawElementsType(GL_NONE),
mClearColorMask(kAllColorChannelsMask), mClearColorMask(kAllColorChannelsMask),
mFlipYForCurrentSurface(false), mFlipYForCurrentSurface(false),
mDriverUniformsBuffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, sizeof(DriverUniforms) * 16), mDriverUniformsBuffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, sizeof(DriverUniforms) * 16),
...@@ -166,7 +211,7 @@ gl::Error ContextVk::finish(const gl::Context *context) ...@@ -166,7 +211,7 @@ gl::Error ContextVk::finish(const gl::Context *context)
return mRenderer->finish(this); return mRenderer->finish(this);
} }
gl::Error ContextVk::initPipeline(const gl::DrawCallParams &drawCallParams) angle::Result ContextVk::initPipeline(const gl::DrawCallParams &drawCallParams)
{ {
ASSERT(!mCurrentPipeline); ASSERT(!mCurrentPipeline);
...@@ -200,13 +245,13 @@ gl::Error ContextVk::initPipeline(const gl::DrawCallParams &drawCallParams) ...@@ -200,13 +245,13 @@ gl::Error ContextVk::initPipeline(const gl::DrawCallParams &drawCallParams)
*pipelineLayout, *mPipelineDesc, activeAttribLocationsMask, *pipelineLayout, *mPipelineDesc, activeAttribLocationsMask,
&mCurrentPipeline)); &mCurrentPipeline));
return gl::NoError(); return angle::Result::Continue();
} }
gl::Error ContextVk::setupDraw(const gl::Context *context, angle::Result ContextVk::setupDraw(const gl::Context *context,
const gl::DrawCallParams &drawCallParams, const gl::DrawCallParams &drawCallParams,
vk::CommandBuffer **commandBufferOut, bool useIndexBuffer,
bool *shouldApplyVertexArrayOut) vk::CommandBuffer **commandBufferOut)
{ {
if (drawCallParams.mode() != mCurrentDrawMode) if (drawCallParams.mode() != mCurrentDrawMode)
{ {
...@@ -214,41 +259,65 @@ gl::Error ContextVk::setupDraw(const gl::Context *context, ...@@ -214,41 +259,65 @@ gl::Error ContextVk::setupDraw(const gl::Context *context,
mCurrentDrawMode = drawCallParams.mode(); mCurrentDrawMode = drawCallParams.mode();
} }
if (mDirtyDefaultAttribs.any()) const gl::State &state = mState.getState();
const gl::Program *programGL = state.getProgram();
ProgramVk *programVk = vk::GetImpl(programGL);
FramebufferVk *framebufferVk = vk::GetImpl(state.getDrawFramebuffer());
VertexArrayVk *vertexArrayVk = vk::GetImpl(state.getVertexArray());
vk::RecordingMode mode;
ANGLE_TRY(framebufferVk->getCommandBufferForDraw(this, commandBufferOut, &mode));
// Set any dirty bits that depend on draw call parameters or other objects.
if (mode == vk::RecordingMode::Start)
{ {
ANGLE_TRY(updateDefaultAttributes()); mPipelineDirty = true;
mTexturesDirty = true;
mVertexBuffersDirty = true;
mIndexBufferDirty = true;
mDescriptorSetsDirty = true;
} }
if (!mCurrentPipeline) if (context->getStateCache().hasAnyActiveClientAttrib())
{ {
ANGLE_TRY(initPipeline(drawCallParams)); ANGLE_TRY(vertexArrayVk->updateClientAttribs(context, drawCallParams));
mVertexBuffersDirty = true;
} }
const auto &state = mState.getState(); if (programVk->dirtyUniforms())
const gl::Program *programGL = state.getProgram(); {
ProgramVk *programVk = vk::GetImpl(programGL); ANGLE_TRY(programVk->updateUniforms(this));
const gl::Framebuffer *framebuffer = state.getDrawFramebuffer(); mDescriptorSetsDirty = true;
FramebufferVk *framebufferVk = vk::GetImpl(framebuffer); }
Serial queueSerial = mRenderer->getCurrentQueueSerial();
vk::RecordingMode mode = vk::RecordingMode::Start;
ANGLE_TRY(framebufferVk->getCommandBufferForDraw(this, commandBufferOut, &mode));
if (mode == vk::RecordingMode::Start) // Flush any relevant dirty bits.
if (mDirtyDefaultAttribs)
{ {
mTexturesDirty = true; ANGLE_TRY(updateDefaultAttributes());
*shouldApplyVertexArrayOut = true; mDirtyDefaultAttribs = false;
} }
else
if (mPipelineDirty)
{ {
*shouldApplyVertexArrayOut = mVertexArrayBindingHasChanged; if (!mCurrentPipeline)
mVertexArrayBindingHasChanged = false; {
ANGLE_TRY(initPipeline(drawCallParams));
}
(*commandBufferOut)->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, mCurrentPipeline->get());
// Update the queue serial for the pipeline object.
ASSERT(mCurrentPipeline && mCurrentPipeline->valid());
mCurrentPipeline->updateSerial(mRenderer->getCurrentQueueSerial());
mPipelineDirty = false;
mVertexBuffersDirty = true;
mIndexBufferDirty = true;
} }
// Ensure any writes to the textures are flushed before we read from them. // Ensure any writes to the textures are flushed before we read from them.
if (mTexturesDirty) if (mTexturesDirty)
{ {
mTexturesDirty = false; ANGLE_TRY(updateActiveTextures(context));
// TODO(jmadill): Should probably merge this for loop with programVk's descriptor update. // TODO(jmadill): Should probably merge this for loop with programVk's descriptor update.
for (size_t textureIndex : programGL->getActiveSamplersMask()) for (size_t textureIndex : programGL->getActiveSamplersMask())
...@@ -257,18 +326,113 @@ gl::Error ContextVk::setupDraw(const gl::Context *context, ...@@ -257,18 +326,113 @@ gl::Error ContextVk::setupDraw(const gl::Context *context,
ANGLE_TRY(textureVk->ensureImageInitialized(this)); ANGLE_TRY(textureVk->ensureImageInitialized(this));
textureVk->addReadDependency(framebufferVk); textureVk->addReadDependency(framebufferVk);
} }
if (programVk->hasTextures())
{
ANGLE_TRY(programVk->updateTexturesDescriptorSet(this));
}
mDescriptorSetsDirty = true;
mTexturesDirty = false;
} }
(*commandBufferOut)->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, mCurrentPipeline->get()); if (mDescriptorSetsDirty)
{
ANGLE_TRY(programVk->updateDescriptorSets(this, drawCallParams, *commandBufferOut));
// Bind the graphics descriptor sets.
(*commandBufferOut)
->bindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, programVk->getPipelineLayout(),
kDriverUniformsDescriptorSetIndex, 1,
&mDriverUniformsDescriptorSet, 0, nullptr);
mDescriptorSetsDirty = false;
}
// Update the queue serial for the pipeline object. uint32_t maxAttrib = programGL->getState().getMaxActiveAttribLocation();
ASSERT(mCurrentPipeline && mCurrentPipeline->valid()); if (mVertexBuffersDirty && maxAttrib > 0)
mCurrentPipeline->updateSerial(queueSerial); {
const gl::AttributesMask &programAttribsMask = programGL->getActiveAttribLocationsMask();
BindNonNullVertexBufferRanges(*commandBufferOut, programAttribsMask, maxAttrib,
vertexArrayVk->getCurrentArrayBufferHandles(),
vertexArrayVk->getCurrentArrayBufferOffsets());
// Bind the graphics descriptor sets. const auto &arrayBufferResources = vertexArrayVk->getCurrentArrayBufferResources();
ANGLE_TRY(programVk->updateDescriptorSets(this, drawCallParams, mDriverUniformsDescriptorSet,
*commandBufferOut)); for (size_t attribIndex : context->getStateCache().getActiveBufferedAttribsMask())
return gl::NoError(); {
if (arrayBufferResources[attribIndex])
arrayBufferResources[attribIndex]->addReadDependency(framebufferVk);
}
mVertexBuffersDirty = false;
}
if (useIndexBuffer && mIndexBufferDirty)
{
(*commandBufferOut)
->bindIndexBuffer(vertexArrayVk->getCurrentElementArrayBufferHandle(),
vertexArrayVk->getCurrentElementArrayBufferOffset(),
gl_vk::GetIndexType(mCurrentDrawElementsType));
vk::CommandGraphResource *elementArrayBufferResource =
vertexArrayVk->getCurrentElementArrayBufferResource();
if (elementArrayBufferResource)
{
elementArrayBufferResource->addReadDependency(framebufferVk);
}
mIndexBufferDirty = false;
}
return angle::Result::Continue();
}
angle::Result ContextVk::setupIndexedDraw(const gl::Context *context,
const gl::DrawCallParams &drawCallParams,
vk::CommandBuffer **commandBufferOut)
{
VertexArrayVk *vertexArrayVk = vk::GetImpl(mState.getState().getVertexArray());
if (drawCallParams.type() != mCurrentDrawElementsType)
{
mIndexBufferDirty = true;
mCurrentDrawElementsType = drawCallParams.type();
}
const gl::Buffer *elementArrayBuffer = vertexArrayVk->getState().getElementArrayBuffer().get();
if (!elementArrayBuffer)
{
mIndexBufferDirty = true;
ANGLE_TRY(vertexArrayVk->updateIndexTranslation(this, drawCallParams));
}
else
{
if (drawCallParams.indices() != mLastIndexBufferOffset)
{
mIndexBufferDirty = true;
mLastIndexBufferOffset = drawCallParams.indices();
vertexArrayVk->updateCurrentElementArrayBufferOffset(mLastIndexBufferOffset);
}
if (drawCallParams.type() == GL_UNSIGNED_BYTE && mIndexBufferDirty)
{
ANGLE_TRY(vertexArrayVk->updateIndexTranslation(this, drawCallParams));
}
}
return setupDraw(context, drawCallParams, true, commandBufferOut);
}
angle::Result ContextVk::setupLineLoopDraw(const gl::Context *context,
const gl::DrawCallParams &drawCallParams,
vk::CommandBuffer **commandBufferOut)
{
VertexArrayVk *vertexArrayVk = vk::GetImpl(mState.getState().getVertexArray());
ANGLE_TRY(vertexArrayVk->handleLineLoop(this, drawCallParams));
mIndexBufferDirty = true;
mCurrentDrawElementsType =
drawCallParams.isDrawElements() ? drawCallParams.type() : GL_UNSIGNED_INT;
return setupDraw(context, drawCallParams, true, commandBufferOut);
} }
gl::Error ContextVk::drawArrays(const gl::Context *context, gl::Error ContextVk::drawArrays(const gl::Context *context,
...@@ -279,13 +443,18 @@ gl::Error ContextVk::drawArrays(const gl::Context *context, ...@@ -279,13 +443,18 @@ gl::Error ContextVk::drawArrays(const gl::Context *context,
const gl::DrawCallParams &drawCallParams = context->getParams<gl::DrawCallParams>(); const gl::DrawCallParams &drawCallParams = context->getParams<gl::DrawCallParams>();
vk::CommandBuffer *commandBuffer = nullptr; vk::CommandBuffer *commandBuffer = nullptr;
bool shouldApplyVertexArray = false; uint32_t clampedVertexCount = drawCallParams.getClampedVertexCount<uint32_t>();
ANGLE_TRY(setupDraw(context, drawCallParams, &commandBuffer, &shouldApplyVertexArray));
const gl::VertexArray *vertexArray = context->getGLState().getVertexArray(); if (mode == gl::PrimitiveMode::LineLoop)
VertexArrayVk *vertexArrayVk = vk::GetImpl(vertexArray); {
ANGLE_TRY( ANGLE_TRY(setupLineLoopDraw(context, drawCallParams, &commandBuffer));
vertexArrayVk->drawArrays(context, drawCallParams, commandBuffer, shouldApplyVertexArray)); vk::LineLoopHelper::Draw(clampedVertexCount, commandBuffer);
}
else
{
ANGLE_TRY(setupDraw(context, drawCallParams, false, &commandBuffer));
commandBuffer->draw(clampedVertexCount, 1, drawCallParams.firstVertex(), 0);
}
return gl::NoError(); return gl::NoError();
} }
...@@ -309,13 +478,16 @@ gl::Error ContextVk::drawElements(const gl::Context *context, ...@@ -309,13 +478,16 @@ gl::Error ContextVk::drawElements(const gl::Context *context,
const gl::DrawCallParams &drawCallParams = context->getParams<gl::DrawCallParams>(); const gl::DrawCallParams &drawCallParams = context->getParams<gl::DrawCallParams>();
vk::CommandBuffer *commandBuffer = nullptr; vk::CommandBuffer *commandBuffer = nullptr;
bool shouldApplyVertexArray = false; if (mode == gl::PrimitiveMode::LineLoop)
ANGLE_TRY(setupDraw(context, drawCallParams, &commandBuffer, &shouldApplyVertexArray)); {
ANGLE_TRY(setupLineLoopDraw(context, drawCallParams, &commandBuffer));
gl::VertexArray *vao = mState.getState().getVertexArray(); vk::LineLoopHelper::Draw(count, commandBuffer);
VertexArrayVk *vertexArrayVk = vk::GetImpl(vao); }
ANGLE_TRY(vertexArrayVk->drawElements(context, drawCallParams, commandBuffer, else
shouldApplyVertexArray)); {
ANGLE_TRY(setupIndexedDraw(context, drawCallParams, &commandBuffer));
commandBuffer->drawIndexed(count, 1, 0, 0, 0);
}
return gl::NoError(); return gl::NoError();
} }
...@@ -456,12 +628,9 @@ gl::Error ContextVk::syncState(const gl::Context *context, const gl::State::Dirt ...@@ -456,12 +628,9 @@ gl::Error ContextVk::syncState(const gl::Context *context, const gl::State::Dirt
invalidateCurrentPipeline(); invalidateCurrentPipeline();
} }
const auto &glState = context->getGLState(); const gl::State &glState = context->getGLState();
// TODO(jmadill): Full dirty bits implementation.
bool dirtyTextures = false;
for (auto dirtyBit : dirtyBits) for (size_t dirtyBit : dirtyBits)
{ {
switch (dirtyBit) switch (dirtyBit)
{ {
...@@ -626,8 +795,7 @@ gl::Error ContextVk::syncState(const gl::Context *context, const gl::State::Dirt ...@@ -626,8 +795,7 @@ gl::Error ContextVk::syncState(const gl::Context *context, const gl::State::Dirt
break; break;
case gl::State::DIRTY_BIT_VERTEX_ARRAY_BINDING: case gl::State::DIRTY_BIT_VERTEX_ARRAY_BINDING:
{ {
mVertexArrayBindingHasChanged = true; invalidateDefaultAttributes(context->getStateCache().getActiveDefaultAttribsMask());
mDirtyDefaultAttribs = context->getStateCache().getActiveDefaultAttribsMask();
break; break;
} }
case gl::State::DIRTY_BIT_DRAW_INDIRECT_BUFFER_BINDING: case gl::State::DIRTY_BIT_DRAW_INDIRECT_BUFFER_BINDING:
...@@ -638,17 +806,16 @@ gl::Error ContextVk::syncState(const gl::Context *context, const gl::State::Dirt ...@@ -638,17 +806,16 @@ gl::Error ContextVk::syncState(const gl::Context *context, const gl::State::Dirt
break; break;
case gl::State::DIRTY_BIT_PROGRAM_EXECUTABLE: case gl::State::DIRTY_BIT_PROGRAM_EXECUTABLE:
{ {
dirtyTextures = true; mTexturesDirty = true;
// No additional work is needed here. We will update the pipeline desc later. // No additional work is needed here. We will update the pipeline desc later.
mDirtyDefaultAttribs = context->getStateCache().getActiveDefaultAttribsMask(); invalidateDefaultAttributes(context->getStateCache().getActiveDefaultAttribsMask());
break; break;
} }
case gl::State::DIRTY_BIT_TEXTURE_BINDINGS: case gl::State::DIRTY_BIT_TEXTURE_BINDINGS:
dirtyTextures = true; mTexturesDirty = true;
break; break;
case gl::State::DIRTY_BIT_SAMPLER_BINDINGS: case gl::State::DIRTY_BIT_SAMPLER_BINDINGS:
dirtyTextures = true; mTexturesDirty = true;
break; break;
case gl::State::DIRTY_BIT_TRANSFORM_FEEDBACK_BINDING: case gl::State::DIRTY_BIT_TRANSFORM_FEEDBACK_BINDING:
break; break;
...@@ -672,10 +839,7 @@ gl::Error ContextVk::syncState(const gl::Context *context, const gl::State::Dirt ...@@ -672,10 +839,7 @@ gl::Error ContextVk::syncState(const gl::Context *context, const gl::State::Dirt
break; break;
case gl::State::DIRTY_BIT_CURRENT_VALUES: case gl::State::DIRTY_BIT_CURRENT_VALUES:
{ {
for (size_t attribIndex : glState.getAndResetDirtyCurrentValues()) invalidateDefaultAttributes(glState.getAndResetDirtyCurrentValues());
{
invalidateDefaultAttribute(attribIndex);
}
break; break;
} }
break; break;
...@@ -685,15 +849,6 @@ gl::Error ContextVk::syncState(const gl::Context *context, const gl::State::Dirt ...@@ -685,15 +849,6 @@ gl::Error ContextVk::syncState(const gl::Context *context, const gl::State::Dirt
} }
} }
if (dirtyTextures)
{
ANGLE_TRY(updateActiveTextures(context));
ProgramVk *programVk = vk::GetImpl(glState.getProgram());
programVk->invalidateTextures();
mTexturesDirty = true;
}
return gl::NoError(); return gl::NoError();
} }
...@@ -836,6 +991,7 @@ std::vector<PathImpl *> ContextVk::createPaths(GLsizei) ...@@ -836,6 +991,7 @@ std::vector<PathImpl *> ContextVk::createPaths(GLsizei)
void ContextVk::invalidateCurrentPipeline() void ContextVk::invalidateCurrentPipeline()
{ {
mPipelineDirty = true;
mCurrentPipeline = nullptr; mCurrentPipeline = nullptr;
} }
...@@ -968,7 +1124,7 @@ void ContextVk::handleError(VkResult errorCode, const char *file, unsigned int l ...@@ -968,7 +1124,7 @@ void ContextVk::handleError(VkResult errorCode, const char *file, unsigned int l
mErrors->handleError(gl::Error(glErrorCode, glErrorCode, errorStream.str())); mErrors->handleError(gl::Error(glErrorCode, glErrorCode, errorStream.str()));
} }
gl::Error ContextVk::updateActiveTextures(const gl::Context *context) angle::Result ContextVk::updateActiveTextures(const gl::Context *context)
{ {
const gl::State &glState = mState.getState(); const gl::State &glState = mState.getState();
const gl::Program *program = glState.getProgram(); const gl::Program *program = glState.getProgram();
...@@ -987,13 +1143,13 @@ gl::Error ContextVk::updateActiveTextures(const gl::Context *context) ...@@ -987,13 +1143,13 @@ gl::Error ContextVk::updateActiveTextures(const gl::Context *context)
// Null textures represent incomplete textures. // Null textures represent incomplete textures.
if (texture == nullptr) if (texture == nullptr)
{ {
ANGLE_TRY(getIncompleteTexture(context, textureType, &texture)); ANGLE_TRY_HANDLE(context, getIncompleteTexture(context, textureType, &texture));
} }
mActiveTextures[textureUnit] = vk::GetImpl(texture); mActiveTextures[textureUnit] = vk::GetImpl(texture);
} }
return gl::NoError(); return angle::Result::Continue();
} }
const gl::ActiveTextureArray<TextureVk *> &ContextVk::getActiveTextures() const const gl::ActiveTextureArray<TextureVk *> &ContextVk::getActiveTextures() const
...@@ -1003,19 +1159,29 @@ const gl::ActiveTextureArray<TextureVk *> &ContextVk::getActiveTextures() const ...@@ -1003,19 +1159,29 @@ const gl::ActiveTextureArray<TextureVk *> &ContextVk::getActiveTextures() const
void ContextVk::invalidateDefaultAttribute(size_t attribIndex) void ContextVk::invalidateDefaultAttribute(size_t attribIndex)
{ {
mDirtyDefaultAttribs.set(attribIndex); mDirtyDefaultAttribsMask.set(attribIndex);
mDirtyDefaultAttribs = true;
}
void ContextVk::invalidateDefaultAttributes(const gl::AttributesMask &dirtyMask)
{
if (dirtyMask.any())
{
mDirtyDefaultAttribsMask = dirtyMask;
mDirtyDefaultAttribs = true;
}
} }
angle::Result ContextVk::updateDefaultAttributes() angle::Result ContextVk::updateDefaultAttributes()
{ {
ASSERT(mDirtyDefaultAttribs.any()); ASSERT(mDirtyDefaultAttribsMask.any());
for (size_t attribIndex : mDirtyDefaultAttribs) for (size_t attribIndex : mDirtyDefaultAttribsMask)
{ {
ANGLE_TRY(updateDefaultAttribute(attribIndex)) ANGLE_TRY(updateDefaultAttribute(attribIndex))
} }
mDirtyDefaultAttribs.reset(); mDirtyDefaultAttribsMask.reset();
return angle::Result::Continue(); return angle::Result::Continue();
} }
......
...@@ -158,6 +158,7 @@ class ContextVk : public ContextImpl, public vk::Context ...@@ -158,6 +158,7 @@ class ContextVk : public ContextImpl, public vk::Context
void invalidateCurrentPipeline(); void invalidateCurrentPipeline();
void invalidateDefaultAttribute(size_t attribIndex); void invalidateDefaultAttribute(size_t attribIndex);
void invalidateDefaultAttributes(const gl::AttributesMask &dirtyMask);
vk::DynamicDescriptorPool *getDynamicDescriptorPool(uint32_t descriptorSetIndex); vk::DynamicDescriptorPool *getDynamicDescriptorPool(uint32_t descriptorSetIndex);
...@@ -173,19 +174,27 @@ class ContextVk : public ContextImpl, public vk::Context ...@@ -173,19 +174,27 @@ class ContextVk : public ContextImpl, public vk::Context
void handleError(VkResult errorCode, const char *file, unsigned int line) override; void handleError(VkResult errorCode, const char *file, unsigned int line) override;
const gl::ActiveTextureArray<TextureVk *> &getActiveTextures() const; const gl::ActiveTextureArray<TextureVk *> &getActiveTextures() const;
void setIndexBufferDirty() { mIndexBufferDirty = true; }
private: private:
gl::Error initPipeline(const gl::DrawCallParams &drawCallParams); angle::Result initPipeline(const gl::DrawCallParams &drawCallParams);
gl::Error setupDraw(const gl::Context *context, angle::Result setupDraw(const gl::Context *context,
const gl::DrawCallParams &drawCallParams, const gl::DrawCallParams &drawCallParams,
vk::CommandBuffer **commandBufferOut, bool useIndexBuffer,
bool *shouldApplyVertexArrayOut); vk::CommandBuffer **commandBufferOut);
angle::Result setupIndexedDraw(const gl::Context *context,
const gl::DrawCallParams &drawCallParams,
vk::CommandBuffer **commandBufferOut);
angle::Result setupLineLoopDraw(const gl::Context *context,
const gl::DrawCallParams &drawCallParams,
vk::CommandBuffer **commandBufferOut);
void updateScissor(const gl::State &glState) const; void updateScissor(const gl::State &glState) const;
void updateFlipViewportDrawFramebuffer(const gl::State &glState); void updateFlipViewportDrawFramebuffer(const gl::State &glState);
void updateFlipViewportReadFramebuffer(const gl::State &glState); void updateFlipViewportReadFramebuffer(const gl::State &glState);
angle::Result updateDriverUniforms(const gl::State &glState); angle::Result updateDriverUniforms(const gl::State &glState);
gl::Error updateActiveTextures(const gl::Context *context); angle::Result updateActiveTextures(const gl::Context *context);
angle::Result updateDefaultAttributes(); angle::Result updateDefaultAttributes();
angle::Result updateDefaultAttribute(size_t attribIndex); angle::Result updateDefaultAttribute(size_t attribIndex);
...@@ -200,9 +209,18 @@ class ContextVk : public ContextImpl, public vk::Context ...@@ -200,9 +209,18 @@ class ContextVk : public ContextImpl, public vk::Context
// threads simultaneously. Hence, we keep them in the ContextVk instead of the RendererVk. // threads simultaneously. Hence, we keep them in the ContextVk instead of the RendererVk.
vk::DescriptorSetLayoutArray<vk::DynamicDescriptorPool> mDynamicDescriptorPools; vk::DescriptorSetLayoutArray<vk::DynamicDescriptorPool> mDynamicDescriptorPools;
// Triggers adding dependencies to the command graph. // Dirty bits.
// TODO(jmadill): Make this into a dirty bit set. http://anglebug.com/2786
bool mDirtyDefaultAttribs;
bool mPipelineDirty;
bool mTexturesDirty; bool mTexturesDirty;
bool mVertexArrayBindingHasChanged; bool mVertexBuffersDirty;
bool mIndexBufferDirty;
bool mDescriptorSetsDirty;
// The offset we had the last time we bound the index buffer.
const GLvoid *mLastIndexBufferOffset;
GLenum mCurrentDrawElementsType;
// Cached clear value/mask for color and depth/stencil. // Cached clear value/mask for color and depth/stencil.
VkClearValue mClearColorValue; VkClearValue mClearColorValue;
...@@ -235,7 +253,7 @@ class ContextVk : public ContextImpl, public vk::Context ...@@ -235,7 +253,7 @@ class ContextVk : public ContextImpl, public vk::Context
gl::ActiveTextureArray<TextureVk *> mActiveTextures; gl::ActiveTextureArray<TextureVk *> mActiveTextures;
// "Current Value" aka default vertex attribute state. // "Current Value" aka default vertex attribute state.
gl::AttributesMask mDirtyDefaultAttribs; gl::AttributesMask mDirtyDefaultAttribsMask;
gl::AttribArray<vk::DynamicBuffer> mDefaultAttribBuffers; gl::AttribArray<vk::DynamicBuffer> mDefaultAttribBuffers;
}; };
} // namespace rx } // namespace rx
......
...@@ -183,8 +183,7 @@ ProgramVk::DefaultUniformBlock::DefaultUniformBlock() ...@@ -183,8 +183,7 @@ ProgramVk::DefaultUniformBlock::DefaultUniformBlock()
ProgramVk::DefaultUniformBlock::~DefaultUniformBlock() = default; ProgramVk::DefaultUniformBlock::~DefaultUniformBlock() = default;
ProgramVk::ProgramVk(const gl::ProgramState &state) ProgramVk::ProgramVk(const gl::ProgramState &state) : ProgramImpl(state), mUniformBlocksOffsets{}
: ProgramImpl(state), mUniformBlocksOffsets{}, mDirtyTextures(false)
{ {
mUsedDescriptorSetRange.invalidate(); mUsedDescriptorSetRange.invalidate();
} }
...@@ -222,7 +221,6 @@ angle::Result ProgramVk::reset(ContextVk *contextVk) ...@@ -222,7 +221,6 @@ angle::Result ProgramVk::reset(ContextVk *contextVk)
mDescriptorSets.clear(); mDescriptorSets.clear();
mUsedDescriptorSetRange.invalidate(); mUsedDescriptorSetRange.invalidate();
mDirtyTextures = false;
return angle::Result::Continue(); return angle::Result::Continue();
} }
...@@ -276,7 +274,6 @@ gl::LinkResult ProgramVk::linkImpl(const gl::Context *glContext, ...@@ -276,7 +274,6 @@ gl::LinkResult ProgramVk::linkImpl(const gl::Context *glContext,
{ {
// Ensure the descriptor set range includes the textures at position 1. // Ensure the descriptor set range includes the textures at position 1.
mUsedDescriptorSetRange.extend(kTextureDescriptorSetIndex); mUsedDescriptorSetRange.extend(kTextureDescriptorSetIndex);
mDirtyTextures = true;
} }
// Store a reference to the pipeline and descriptor set layouts. This will create them if they // Store a reference to the pipeline and descriptor set layouts. This will create them if they
...@@ -334,7 +331,6 @@ gl::LinkResult ProgramVk::linkImpl(const gl::Context *glContext, ...@@ -334,7 +331,6 @@ gl::LinkResult ProgramVk::linkImpl(const gl::Context *glContext,
{ {
// Ensure the descriptor set range includes the textures at position 1. // Ensure the descriptor set range includes the textures at position 1.
mUsedDescriptorSetRange.extend(kTextureDescriptorSetIndex); mUsedDescriptorSetRange.extend(kTextureDescriptorSetIndex);
mDirtyTextures = true;
} }
} }
...@@ -783,11 +779,7 @@ void ProgramVk::getUniformuiv(const gl::Context *context, GLint location, GLuint ...@@ -783,11 +779,7 @@ void ProgramVk::getUniformuiv(const gl::Context *context, GLint location, GLuint
angle::Result ProgramVk::updateUniforms(ContextVk *contextVk) angle::Result ProgramVk::updateUniforms(ContextVk *contextVk)
{ {
if (!mDefaultUniformBlocks[vk::ShaderType::VertexShader].uniformsDirty && ASSERT(dirtyUniforms());
!mDefaultUniformBlocks[vk::ShaderType::FragmentShader].uniformsDirty)
{
return angle::Result::Continue();
}
// Update buffer memory by immediate mapping. This immediate update only works once. // Update buffer memory by immediate mapping. This immediate update only works once.
bool anyNewBufferAllocated = false; bool anyNewBufferAllocated = false;
...@@ -865,11 +857,7 @@ angle::Result ProgramVk::updateDefaultUniformsDescriptorSet(ContextVk *contextVk ...@@ -865,11 +857,7 @@ angle::Result ProgramVk::updateDefaultUniformsDescriptorSet(ContextVk *contextVk
angle::Result ProgramVk::updateTexturesDescriptorSet(ContextVk *contextVk) angle::Result ProgramVk::updateTexturesDescriptorSet(ContextVk *contextVk)
{ {
if (mState.getSamplerBindings().empty() || !mDirtyTextures) ASSERT(hasTextures());
{
return angle::Result::Continue();
}
ANGLE_TRY(allocateDescriptorSet(contextVk, kTextureDescriptorSetIndex)); ANGLE_TRY(allocateDescriptorSet(contextVk, kTextureDescriptorSetIndex));
ASSERT(mUsedDescriptorSetRange.contains(1)); ASSERT(mUsedDescriptorSetRange.contains(1));
...@@ -923,15 +911,9 @@ angle::Result ProgramVk::updateTexturesDescriptorSet(ContextVk *contextVk) ...@@ -923,15 +911,9 @@ angle::Result ProgramVk::updateTexturesDescriptorSet(ContextVk *contextVk)
ASSERT(writeCount > 0); ASSERT(writeCount > 0);
vkUpdateDescriptorSets(device, writeCount, writeDescriptorInfo.data(), 0, nullptr); vkUpdateDescriptorSets(device, writeCount, writeDescriptorInfo.data(), 0, nullptr);
mDirtyTextures = false;
return angle::Result::Continue(); return angle::Result::Continue();
} }
void ProgramVk::invalidateTextures()
{
mDirtyTextures = true;
}
void ProgramVk::setDefaultUniformBlocksMinSizeForTesting(size_t minSize) void ProgramVk::setDefaultUniformBlocksMinSizeForTesting(size_t minSize)
{ {
for (DefaultUniformBlock &block : mDefaultUniformBlocks) for (DefaultUniformBlock &block : mDefaultUniformBlocks)
...@@ -942,17 +924,10 @@ void ProgramVk::setDefaultUniformBlocksMinSizeForTesting(size_t minSize) ...@@ -942,17 +924,10 @@ void ProgramVk::setDefaultUniformBlocksMinSizeForTesting(size_t minSize)
angle::Result ProgramVk::updateDescriptorSets(ContextVk *contextVk, angle::Result ProgramVk::updateDescriptorSets(ContextVk *contextVk,
const gl::DrawCallParams &drawCallParams, const gl::DrawCallParams &drawCallParams,
VkDescriptorSet driverUniformsDescriptorSet,
vk::CommandBuffer *commandBuffer) vk::CommandBuffer *commandBuffer)
{ {
// TODO(jmadill): Line rasterization emulation shaders. http://anglebug.com/2598 // TODO(jmadill): Line rasterization emulation shaders. http://anglebug.com/2598
// Can probably use better dirty bits here. // Can probably use better dirty bits here.
ANGLE_TRY(updateUniforms(contextVk));
ANGLE_TRY(updateTexturesDescriptorSet(contextVk));
commandBuffer->bindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, mPipelineLayout.get(),
kDriverUniformsDescriptorSetIndex, 1,
&driverUniformsDescriptorSet, 0, nullptr);
if (mUsedDescriptorSetRange.empty()) if (mUsedDescriptorSetRange.empty())
return angle::Result::Continue(); return angle::Result::Continue();
......
...@@ -103,17 +103,25 @@ class ProgramVk : public ProgramImpl ...@@ -103,17 +103,25 @@ class ProgramVk : public ProgramImpl
const vk::PipelineLayout **pipelineLayoutOut); const vk::PipelineLayout **pipelineLayoutOut);
angle::Result updateUniforms(ContextVk *contextVk); angle::Result updateUniforms(ContextVk *contextVk);
angle::Result updateTexturesDescriptorSet(ContextVk *contextVk);
void invalidateTextures();
angle::Result updateDescriptorSets(ContextVk *contextVk, angle::Result updateDescriptorSets(ContextVk *contextVk,
const gl::DrawCallParams &drawCallParams, const gl::DrawCallParams &drawCallParams,
VkDescriptorSet driverUniformsDescriptorSet,
vk::CommandBuffer *commandBuffer); vk::CommandBuffer *commandBuffer);
// For testing only. // For testing only.
void setDefaultUniformBlocksMinSizeForTesting(size_t minSize); void setDefaultUniformBlocksMinSizeForTesting(size_t minSize);
const vk::PipelineLayout &getPipelineLayout() const { return mPipelineLayout.get(); }
bool hasTextures() const { return !mState.getSamplerBindings().empty(); }
bool dirtyUniforms() const
{
return (mDefaultUniformBlocks[vk::ShaderType::VertexShader].uniformsDirty ||
mDefaultUniformBlocks[vk::ShaderType::FragmentShader].uniformsDirty);
}
private: private:
template <int cols, int rows> template <int cols, int rows>
void setUniformMatrixfv(GLint location, void setUniformMatrixfv(GLint location,
...@@ -126,7 +134,6 @@ class ProgramVk : public ProgramImpl ...@@ -126,7 +134,6 @@ class ProgramVk : public ProgramImpl
angle::Result initDefaultUniformBlocks(const gl::Context *glContext); angle::Result initDefaultUniformBlocks(const gl::Context *glContext);
angle::Result updateDefaultUniformsDescriptorSet(ContextVk *contextVk); angle::Result updateDefaultUniformsDescriptorSet(ContextVk *contextVk);
angle::Result updateTexturesDescriptorSet(ContextVk *contextVk);
template <class T> template <class T>
void getUniformImpl(GLint location, T *v, GLenum entryPointType) const; void getUniformImpl(GLint location, T *v, GLenum entryPointType) const;
...@@ -165,7 +172,6 @@ class ProgramVk : public ProgramImpl ...@@ -165,7 +172,6 @@ class ProgramVk : public ProgramImpl
// Descriptor sets for uniform blocks and textures for this program. // Descriptor sets for uniform blocks and textures for this program.
std::vector<VkDescriptorSet> mDescriptorSets; std::vector<VkDescriptorSet> mDescriptorSets;
gl::RangeUI mUsedDescriptorSetRange; gl::RangeUI mUsedDescriptorSetRange;
bool mDirtyTextures;
// We keep a reference to the pipeline and descriptor set layouts. This ensures they don't get // We keep a reference to the pipeline and descriptor set layouts. This ensures they don't get
// deleted while this program is in use. // deleted while this program is in use.
......
...@@ -51,46 +51,6 @@ angle::Result StreamVertexData(ContextVk *contextVk, ...@@ -51,46 +51,6 @@ angle::Result StreamVertexData(ContextVk *contextVk,
ANGLE_TRY(dynamicBuffer->flush(contextVk)); ANGLE_TRY(dynamicBuffer->flush(contextVk));
return angle::Result::Continue(); return angle::Result::Continue();
} }
void BindNonNullVertexBufferRanges(vk::CommandBuffer *commandBuffer,
const gl::AttributesMask &nonNullAttribMask,
uint32_t maxAttrib,
const gl::AttribArray<VkBuffer> &arrayBufferHandles,
const gl::AttribArray<VkDeviceSize> &arrayBufferOffsets)
{
// Vulkan does not allow binding a null vertex buffer but the default state of null buffers is
// valid.
// We can detect if there are no gaps in active attributes by using the mask of the program
// attribs and the max enabled attrib.
ASSERT(maxAttrib > 0);
if (nonNullAttribMask.to_ulong() == (maxAttrib - 1))
{
commandBuffer->bindVertexBuffers(0, maxAttrib, arrayBufferHandles.data(),
arrayBufferOffsets.data());
return;
}
// Find ranges of non-null buffers and bind them all together.
for (uint32_t attribIdx = 0; attribIdx < maxAttrib; attribIdx++)
{
if (arrayBufferHandles[attribIdx] != VK_NULL_HANDLE)
{
// Find the end of this range of non-null handles
uint32_t rangeCount = 1;
while (attribIdx + rangeCount < maxAttrib &&
arrayBufferHandles[attribIdx + rangeCount] != VK_NULL_HANDLE)
{
rangeCount++;
}
commandBuffer->bindVertexBuffers(attribIdx, rangeCount, &arrayBufferHandles[attribIdx],
&arrayBufferOffsets[attribIdx]);
attribIdx += rangeCount;
}
}
}
} // anonymous namespace } // anonymous namespace
#define INIT \ #define INIT \
...@@ -117,10 +77,7 @@ VertexArrayVk::VertexArrayVk(const gl::VertexArrayState &state, RendererVk *rend ...@@ -117,10 +77,7 @@ VertexArrayVk::VertexArrayVk(const gl::VertexArrayState &state, RendererVk *rend
mDynamicIndexData(VK_BUFFER_USAGE_INDEX_BUFFER_BIT, kDynamicIndexDataSize), mDynamicIndexData(VK_BUFFER_USAGE_INDEX_BUFFER_BIT, kDynamicIndexDataSize),
mTranslatedByteIndexData(VK_BUFFER_USAGE_INDEX_BUFFER_BIT, kDynamicIndexDataSize), mTranslatedByteIndexData(VK_BUFFER_USAGE_INDEX_BUFFER_BIT, kDynamicIndexDataSize),
mLineLoopHelper(renderer), mLineLoopHelper(renderer),
mDirtyLineLoopTranslation(true), mDirtyLineLoopTranslation(true)
mVertexBuffersDirty(false),
mIndexBufferDirty(false),
mLastIndexBufferOffset(0)
{ {
mCurrentArrayBufferHandles.fill(VK_NULL_HANDLE); mCurrentArrayBufferHandles.fill(VK_NULL_HANDLE);
mCurrentArrayBufferOffsets.fill(0); mCurrentArrayBufferOffsets.fill(0);
...@@ -303,7 +260,7 @@ gl::Error VertexArrayVk::syncState(const gl::Context *context, ...@@ -303,7 +260,7 @@ gl::Error VertexArrayVk::syncState(const gl::Context *context,
mCurrentElementArrayBufferOffset = 0; mCurrentElementArrayBufferOffset = 0;
mLineLoopBufferFirstIndex.reset(); mLineLoopBufferFirstIndex.reset();
mLineLoopBufferLastIndex.reset(); mLineLoopBufferLastIndex.reset();
mIndexBufferDirty = true; contextVk->setIndexBufferDirty();
mDirtyLineLoopTranslation = true; mDirtyLineLoopTranslation = true;
break; break;
} }
...@@ -326,7 +283,6 @@ gl::Error VertexArrayVk::syncState(const gl::Context *context, ...@@ -326,7 +283,6 @@ gl::Error VertexArrayVk::syncState(const gl::Context *context,
if (invalidatePipeline) if (invalidatePipeline)
{ {
mVertexBuffersDirty = true;
contextVk->invalidateCurrentPipeline(); contextVk->invalidateCurrentPipeline();
} }
...@@ -402,29 +358,6 @@ angle::Result VertexArrayVk::syncDirtyAttrib(ContextVk *contextVk, ...@@ -402,29 +358,6 @@ angle::Result VertexArrayVk::syncDirtyAttrib(ContextVk *contextVk,
return angle::Result::Continue(); return angle::Result::Continue();
} }
void VertexArrayVk::updateArrayBufferReadDependencies(vk::CommandGraphResource *drawFramebuffer,
const gl::AttributesMask &activeAttribsMask,
Serial serial)
{
// Handle the bound array buffers.
for (size_t attribIndex : activeAttribsMask)
{
if (mCurrentArrayBufferResources[attribIndex])
mCurrentArrayBufferResources[attribIndex]->addReadDependency(drawFramebuffer);
}
}
void VertexArrayVk::updateElementArrayBufferReadDependency(
vk::CommandGraphResource *drawFramebuffer,
Serial serial)
{
// Handle the bound element array buffer.
if (mCurrentElementArrayBufferResource)
{
mCurrentElementArrayBufferResource->addReadDependency(drawFramebuffer);
}
}
void VertexArrayVk::getPackedInputDescriptions(vk::PipelineDesc *pipelineDesc) void VertexArrayVk::getPackedInputDescriptions(vk::PipelineDesc *pipelineDesc)
{ {
updatePackedInputDescriptions(); updatePackedInputDescriptions();
...@@ -489,241 +422,141 @@ void VertexArrayVk::updatePackedInputInfo(uint32_t attribIndex, ...@@ -489,241 +422,141 @@ void VertexArrayVk::updatePackedInputInfo(uint32_t attribIndex,
attribDesc.offset = static_cast<uint32_t>(attrib.relativeOffset); attribDesc.offset = static_cast<uint32_t>(attrib.relativeOffset);
} }
gl::Error VertexArrayVk::drawArrays(const gl::Context *context, angle::Result VertexArrayVk::updateClientAttribs(const gl::Context *context,
const gl::DrawCallParams &drawCallParams, const gl::DrawCallParams &drawCallParams)
vk::CommandBuffer *commandBuffer,
bool newCommandBuffer)
{ {
ASSERT(commandBuffer->valid());
ContextVk *contextVk = vk::GetImpl(context); ContextVk *contextVk = vk::GetImpl(context);
const gl::AttributesMask &clientAttribs = context->getStateCache().getActiveClientAttribsMask();
ANGLE_TRY(onDraw(context, drawCallParams, commandBuffer, newCommandBuffer)); ASSERT(clientAttribs.any());
ANGLE_TRY_HANDLE(context, drawCallParams.ensureIndexRangeResolved(context));
// Note: Vertex indexes can be arbitrarily large. mDynamicVertexData.releaseRetainedBuffers(contextVk->getRenderer());
uint32_t clampedVertexCount = drawCallParams.getClampedVertexCount<uint32_t>();
if (drawCallParams.mode() != gl::PrimitiveMode::LineLoop) const auto &attribs = mState.getVertexAttributes();
{ const auto &bindings = mState.getVertexBindings();
commandBuffer->draw(clampedVertexCount, 1, drawCallParams.firstVertex(), 0);
return gl::NoError();
}
// Handle GL_LINE_LOOP drawArrays. // TODO(fjhenigman): When we have a bunch of interleaved attributes, they end up
size_t lastVertex = static_cast<size_t>(drawCallParams.firstVertex() + clampedVertexCount); // un-interleaved, wasting space and copying time. Consider improving on that.
if (!mLineLoopBufferFirstIndex.valid() || !mLineLoopBufferLastIndex.valid() || for (size_t attribIndex : clientAttribs)
mLineLoopBufferFirstIndex != drawCallParams.firstVertex() ||
mLineLoopBufferLastIndex != lastVertex)
{ {
ANGLE_TRY(mLineLoopHelper.getIndexBufferForDrawArrays(contextVk, drawCallParams, const gl::VertexAttribute &attrib = attribs[attribIndex];
&mCurrentElementArrayBufferHandle, const gl::VertexBinding &binding = bindings[attrib.bindingIndex];
&mCurrentElementArrayBufferOffset)); ASSERT(attrib.enabled && binding.getBuffer().get() == nullptr);
mLineLoopBufferFirstIndex = drawCallParams.firstVertex(); const size_t bytesToAllocate =
mLineLoopBufferLastIndex = lastVertex; (drawCallParams.firstVertex() + drawCallParams.vertexCount()) *
mCurrentArrayBufferStrides[attribIndex];
const uint8_t *src = static_cast<const uint8_t *>(attrib.pointer) +
drawCallParams.firstVertex() * binding.getStride();
size_t destOffset = drawCallParams.firstVertex() * mCurrentArrayBufferStrides[attribIndex];
// Only vertexCount() vertices will be used by the upcoming draw. so that is all we copy.
// We allocate space for firstVertex() + 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,
drawCallParams.vertexCount(), binding.getStride(),
mCurrentArrayBufferFormats[attribIndex]->vertexLoadFunction,
&mCurrentArrayBufferHandles[attribIndex],
&mCurrentArrayBufferOffsets[attribIndex]));
} }
commandBuffer->bindIndexBuffer(mCurrentElementArrayBufferHandle, return angle::Result::Continue();
mCurrentElementArrayBufferOffset, VK_INDEX_TYPE_UINT32);
vk::LineLoopHelper::Draw(clampedVertexCount, commandBuffer);
return gl::NoError();
} }
gl::Error VertexArrayVk::drawElements(const gl::Context *context, angle::Result VertexArrayVk::handleLineLoop(ContextVk *contextVk,
const gl::DrawCallParams &drawCallParams, const gl::DrawCallParams &drawCallParams)
vk::CommandBuffer *commandBuffer,
bool newCommandBuffer)
{ {
ASSERT(commandBuffer->valid()); if (drawCallParams.isDrawElements())
ContextVk *contextVk = vk::GetImpl(context);
if (drawCallParams.mode() != gl::PrimitiveMode::LineLoop)
{
ANGLE_TRY(onIndexedDraw(context, drawCallParams, commandBuffer, newCommandBuffer));
commandBuffer->drawIndexed(drawCallParams.indexCount(), 1, 0, 0, 0);
return gl::NoError();
}
// Handle GL_LINE_LOOP drawElements.
if (mDirtyLineLoopTranslation)
{ {
gl::Buffer *elementArrayBuffer = mState.getElementArrayBuffer().get(); // Handle GL_LINE_LOOP drawElements.
VkIndexType indexType = gl_vk::GetIndexType(drawCallParams.type()); if (mDirtyLineLoopTranslation)
if (!elementArrayBuffer)
{
ANGLE_TRY(mLineLoopHelper.getIndexBufferForClientElementArray(
contextVk, drawCallParams, &mCurrentElementArrayBufferHandle,
&mCurrentElementArrayBufferOffset));
}
else
{ {
// When using an element array buffer, 'indices' is an offset to the first element. gl::Buffer *elementArrayBuffer = mState.getElementArrayBuffer().get();
intptr_t offset = reinterpret_cast<intptr_t>(drawCallParams.indices()); VkIndexType indexType = gl_vk::GetIndexType(drawCallParams.type());
BufferVk *elementArrayBufferVk = vk::GetImpl(elementArrayBuffer);
ANGLE_TRY(mLineLoopHelper.getIndexBufferForElementArrayBuffer(
contextVk, elementArrayBufferVk, indexType, drawCallParams.indexCount(), offset,
&mCurrentElementArrayBufferHandle, &mCurrentElementArrayBufferOffset));
}
}
ANGLE_TRY(onIndexedDraw(context, drawCallParams, commandBuffer, newCommandBuffer));
vk::LineLoopHelper::Draw(drawCallParams.indexCount(), commandBuffer);
return gl::NoError(); if (!elementArrayBuffer)
} {
ANGLE_TRY(mLineLoopHelper.getIndexBufferForClientElementArray(
gl::Error VertexArrayVk::onDraw(const gl::Context *context, contextVk, drawCallParams, &mCurrentElementArrayBufferHandle,
const gl::DrawCallParams &drawCallParams, &mCurrentElementArrayBufferOffset));
vk::CommandBuffer *commandBuffer, }
bool newCommandBuffer) else
{ {
ContextVk *contextVk = vk::GetImpl(context); // When using an element array buffer, 'indices' is an offset to the first element.
const gl::State &state = context->getGLState(); intptr_t offset = reinterpret_cast<intptr_t>(drawCallParams.indices());
const gl::Program *programGL = state.getProgram(); BufferVk *elementArrayBufferVk = vk::GetImpl(elementArrayBuffer);
const gl::AttributesMask &programAttribsMask = programGL->getActiveAttribLocationsMask(); ANGLE_TRY(mLineLoopHelper.getIndexBufferForElementArrayBuffer(
const gl::AttributesMask &clientAttribs = context->getStateCache().getActiveClientAttribsMask(); contextVk, elementArrayBufferVk, indexType, drawCallParams.indexCount(), offset,
uint32_t maxAttrib = programGL->getState().getMaxActiveAttribLocation(); &mCurrentElementArrayBufferHandle, &mCurrentElementArrayBufferOffset));
}
if (clientAttribs.any())
{
ANGLE_TRY(drawCallParams.ensureIndexRangeResolved(context));
mDynamicVertexData.releaseRetainedBuffers(contextVk->getRenderer());
const auto &attribs = mState.getVertexAttributes();
const auto &bindings = mState.getVertexBindings();
// TODO(fjhenigman): When we have a bunch of interleaved attributes, they end up
// un-interleaved, wasting space and copying time. Consider improving on that.
for (size_t attribIndex : clientAttribs)
{
const gl::VertexAttribute &attrib = attribs[attribIndex];
const gl::VertexBinding &binding = bindings[attrib.bindingIndex];
ASSERT(attrib.enabled && binding.getBuffer().get() == nullptr);
const size_t bytesToAllocate =
(drawCallParams.firstVertex() + drawCallParams.vertexCount()) *
mCurrentArrayBufferStrides[attribIndex];
const uint8_t *src = static_cast<const uint8_t *>(attrib.pointer) +
drawCallParams.firstVertex() * binding.getStride();
size_t destOffset =
drawCallParams.firstVertex() * mCurrentArrayBufferStrides[attribIndex];
// Only vertexCount() vertices will be used by the upcoming draw so that is all we copy,
// but we allocate space firstVertex() + 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, drawCallParams.vertexCount(),
binding.getStride(),
mCurrentArrayBufferFormats[attribIndex]->vertexLoadFunction,
&mCurrentArrayBufferHandles[attribIndex],
&mCurrentArrayBufferOffsets[attribIndex]));
} }
BindNonNullVertexBufferRanges(commandBuffer, programAttribsMask, maxAttrib, // If we've had a drawArrays call with a line loop before, we want to make sure this is
mCurrentArrayBufferHandles, mCurrentArrayBufferOffsets); // invalidated the next time drawArrays is called since we use the same index buffer for
// both calls.
mLineLoopBufferFirstIndex.reset();
mLineLoopBufferLastIndex.reset();
return angle::Result::Continue();
} }
else if (mVertexBuffersDirty || newCommandBuffer)
{
if (maxAttrib > 0)
{
BindNonNullVertexBufferRanges(commandBuffer, programAttribsMask, maxAttrib,
mCurrentArrayBufferHandles, mCurrentArrayBufferOffsets);
const gl::AttributesMask &bufferedAttribs = // Note: Vertex indexes can be arbitrarily large.
context->getStateCache().getActiveBufferedAttribsMask(); uint32_t clampedVertexCount = drawCallParams.getClampedVertexCount<uint32_t>();
vk::CommandGraphResource *drawFramebuffer = vk::GetImpl(state.getDrawFramebuffer());
updateArrayBufferReadDependencies(drawFramebuffer, bufferedAttribs,
contextVk->getRenderer()->getCurrentQueueSerial());
}
mVertexBuffersDirty = false;
// This forces the binding to happen if we follow a drawElement call from a drawArrays call. // Handle GL_LINE_LOOP drawArrays.
mIndexBufferDirty = true; size_t lastVertex = static_cast<size_t>(drawCallParams.firstVertex() + clampedVertexCount);
if (!mLineLoopBufferFirstIndex.valid() || !mLineLoopBufferLastIndex.valid() ||
mLineLoopBufferFirstIndex != drawCallParams.firstVertex() ||
mLineLoopBufferLastIndex != lastVertex)
{
ANGLE_TRY(mLineLoopHelper.getIndexBufferForDrawArrays(contextVk, drawCallParams,
&mCurrentElementArrayBufferHandle,
&mCurrentElementArrayBufferOffset));
// If we've had a drawElements call with a line loop before, we want to make sure this is mLineLoopBufferFirstIndex = drawCallParams.firstVertex();
// invalidated the next time drawElements is called since we use the same index buffer for mLineLoopBufferLastIndex = lastVertex;
// both calls.
mDirtyLineLoopTranslation = true;
} }
return gl::NoError(); return angle::Result::Continue();
} }
gl::Error VertexArrayVk::onIndexedDraw(const gl::Context *context, angle::Result VertexArrayVk::updateIndexTranslation(ContextVk *contextVk,
const gl::DrawCallParams &drawCallParams, const gl::DrawCallParams &drawCallParams)
vk::CommandBuffer *commandBuffer,
bool newCommandBuffer)
{ {
ContextVk *contextVk = vk::GetImpl(context); ASSERT(drawCallParams.isDrawElements());
ANGLE_TRY(onDraw(context, drawCallParams, commandBuffer, newCommandBuffer)); ASSERT(drawCallParams.mode() != gl::PrimitiveMode::LineLoop);
bool isLineLoop = drawCallParams.mode() == gl::PrimitiveMode::LineLoop;
gl::Buffer *glBuffer = mState.getElementArrayBuffer().get(); gl::Buffer *glBuffer = mState.getElementArrayBuffer().get();
uintptr_t offset =
glBuffer && !isLineLoop ? reinterpret_cast<uintptr_t>(drawCallParams.indices()) : 0;
if (!glBuffer && !isLineLoop) if (!glBuffer)
{ {
ANGLE_TRY(streamIndexData(contextVk, drawCallParams.type(), drawCallParams.indexCount(), ANGLE_TRY(streamIndexData(contextVk, drawCallParams.type(), drawCallParams.indexCount(),
drawCallParams.indices(), &mDynamicIndexData)); drawCallParams.indices(), &mDynamicIndexData));
commandBuffer->bindIndexBuffer(mCurrentElementArrayBufferHandle,
mCurrentElementArrayBufferOffset,
gl_vk::GetIndexType(drawCallParams.type()));
} }
else if (mIndexBufferDirty || newCommandBuffer || offset != mLastIndexBufferOffset) else
{ {
mLastIndexBufferOffset = offset; ASSERT(drawCallParams.type() == GL_UNSIGNED_BYTE);
// Unsigned bytes don't have direct support in Vulkan so we have to expand the
if (drawCallParams.type() == GL_UNSIGNED_BYTE && !isLineLoop) // memory to a GLushort.
{ BufferVk *bufferVk = vk::GetImpl(glBuffer);
// Unsigned bytes don't have direct support in Vulkan so we have to expand the void *srcDataMapping = nullptr;
// memory to a GLushort. ASSERT(!glBuffer->isMapped());
BufferVk *bufferVk = vk::GetImpl(glBuffer); ANGLE_TRY(bufferVk->mapImpl(contextVk, &srcDataMapping));
void *srcDataMapping = nullptr; uint8_t *srcData = static_cast<uint8_t *>(srcDataMapping);
ASSERT(!glBuffer->isMapped()); intptr_t offsetIntoSrcData = reinterpret_cast<intptr_t>(drawCallParams.indices());
ANGLE_TRY(bufferVk->mapImpl(contextVk, &srcDataMapping)); srcData += offsetIntoSrcData;
uint8_t *srcData = static_cast<uint8_t *>(srcDataMapping);
intptr_t offsetIntoSrcData = reinterpret_cast<intptr_t>(drawCallParams.indices()); ANGLE_TRY(streamIndexData(contextVk, drawCallParams.type(),
srcData += offsetIntoSrcData; static_cast<size_t>(bufferVk->getSize()) - offsetIntoSrcData,
srcData, &mTranslatedByteIndexData));
ANGLE_TRY(streamIndexData(contextVk, drawCallParams.type(),
static_cast<size_t>(bufferVk->getSize()) - offsetIntoSrcData, ANGLE_TRY(bufferVk->unmapImpl(contextVk));
srcData, &mTranslatedByteIndexData));
ANGLE_TRY(bufferVk->unmapImpl(contextVk));
// We do not add the offset from the drawCallParams here because we've already copied
// the source starting at the offset requested.
offset = 0;
}
commandBuffer->bindIndexBuffer(mCurrentElementArrayBufferHandle,
mCurrentElementArrayBufferOffset + offset,
gl_vk::GetIndexType(drawCallParams.type()));
const gl::State &glState = context->getGLState();
vk::CommandGraphResource *drawFramebuffer = vk::GetImpl(glState.getDrawFramebuffer());
updateElementArrayBufferReadDependency(drawFramebuffer,
contextVk->getRenderer()->getCurrentQueueSerial());
mIndexBufferDirty = false;
// If we've had a drawArrays call with a line loop before, we want to make sure this is
// invalidated the next time drawArrays is called since we use the same index buffer for
// both calls.
mLineLoopBufferFirstIndex.reset();
mLineLoopBufferLastIndex.reset();
} }
return gl::NoError(); return angle::Result::Continue();
} }
void VertexArrayVk::updateDefaultAttrib(RendererVk *renderer, void VertexArrayVk::updateDefaultAttrib(RendererVk *renderer,
......
...@@ -41,19 +41,8 @@ class VertexArrayVk : public VertexArrayImpl ...@@ -41,19 +41,8 @@ class VertexArrayVk : public VertexArrayImpl
const gl::VertexArray::DirtyAttribBitsArray &attribBits, const gl::VertexArray::DirtyAttribBitsArray &attribBits,
const gl::VertexArray::DirtyBindingBitsArray &bindingBits) override; const gl::VertexArray::DirtyBindingBitsArray &bindingBits) override;
void updateDrawDependencies(vk::CommandGraphResource *drawFramebuffer,
const gl::AttributesMask &activeAttribsMask,
vk::CommandGraphResource *elementArrayBufferOverride,
Serial serial,
bool isDrawElements);
void getPackedInputDescriptions(vk::PipelineDesc *pipelineDesc); void getPackedInputDescriptions(vk::PipelineDesc *pipelineDesc);
// Draw call handling.
gl::Error drawArrays(const gl::Context *context,
const gl::DrawCallParams &drawCallParams,
vk::CommandBuffer *commandBuffer,
bool shouldApplyVertexArray);
gl::Error drawElements(const gl::Context *context, gl::Error drawElements(const gl::Context *context,
const gl::DrawCallParams &drawCallParams, const gl::DrawCallParams &drawCallParams,
vk::CommandBuffer *commandBuffer, vk::CommandBuffer *commandBuffer,
...@@ -64,6 +53,46 @@ class VertexArrayVk : public VertexArrayImpl ...@@ -64,6 +53,46 @@ class VertexArrayVk : public VertexArrayImpl
VkBuffer bufferHandle, VkBuffer bufferHandle,
uint32_t offset); uint32_t offset);
angle::Result updateClientAttribs(const gl::Context *context,
const gl::DrawCallParams &drawCallParams);
angle::Result handleLineLoop(ContextVk *contextVk, const gl::DrawCallParams &drawCallParams);
const gl::AttribArray<VkBuffer> &getCurrentArrayBufferHandles() const
{
return mCurrentArrayBufferHandles;
}
const gl::AttribArray<VkDeviceSize> &getCurrentArrayBufferOffsets() const
{
return mCurrentArrayBufferOffsets;
}
const gl::AttribArray<vk::CommandGraphResource *> &getCurrentArrayBufferResources() const
{
return mCurrentArrayBufferResources;
}
VkBuffer getCurrentElementArrayBufferHandle() const { return mCurrentElementArrayBufferHandle; }
VkDeviceSize getCurrentElementArrayBufferOffset() const
{
return mCurrentElementArrayBufferOffset;
}
void updateCurrentElementArrayBufferOffset(const GLvoid *offset)
{
mCurrentElementArrayBufferOffset = reinterpret_cast<VkDeviceSize>(offset);
}
vk::CommandGraphResource *getCurrentElementArrayBufferResource() const
{
return mCurrentElementArrayBufferResource;
}
angle::Result updateIndexTranslation(ContextVk *contextVk,
const gl::DrawCallParams &drawCallParams);
private: private:
// This will update any dirty packed input descriptions, regardless if they're used by the // This will update any dirty packed input descriptions, regardless if they're used by the
// active program. This could lead to slight inefficiencies when the app would repeatedly // active program. This could lead to slight inefficiencies when the app would repeatedly
...@@ -79,9 +108,6 @@ class VertexArrayVk : public VertexArrayImpl ...@@ -79,9 +108,6 @@ class VertexArrayVk : public VertexArrayImpl
const gl::AttributesMask &activeAttribsMask, const gl::AttributesMask &activeAttribsMask,
Serial serial); Serial serial);
void updateElementArrayBufferReadDependency(vk::CommandGraphResource *drawFramebuffer,
Serial serial);
angle::Result streamIndexData(ContextVk *contextVk, angle::Result streamIndexData(ContextVk *contextVk,
GLenum indexType, GLenum indexType,
size_t indexCount, size_t indexCount,
...@@ -93,15 +119,6 @@ class VertexArrayVk : public VertexArrayImpl ...@@ -93,15 +119,6 @@ class VertexArrayVk : public VertexArrayImpl
size_t attribIndex); size_t attribIndex);
void ensureConversionReleased(RendererVk *renderer, size_t attribIndex); void ensureConversionReleased(RendererVk *renderer, size_t attribIndex);
gl::Error onDraw(const gl::Context *context,
const gl::DrawCallParams &drawCallParams,
vk::CommandBuffer *commandBuffer,
bool newCommandBuffer);
gl::Error onIndexedDraw(const gl::Context *context,
const gl::DrawCallParams &drawCallParams,
vk::CommandBuffer *commandBuffer,
bool newCommandBuffer);
angle::Result syncDirtyAttrib(ContextVk *contextVk, angle::Result syncDirtyAttrib(ContextVk *contextVk,
const gl::VertexAttribute &attrib, const gl::VertexAttribute &attrib,
const gl::VertexBinding &binding, const gl::VertexBinding &binding,
...@@ -132,15 +149,7 @@ class VertexArrayVk : public VertexArrayImpl ...@@ -132,15 +149,7 @@ class VertexArrayVk : public VertexArrayImpl
Optional<GLint> mLineLoopBufferFirstIndex; Optional<GLint> mLineLoopBufferFirstIndex;
Optional<size_t> mLineLoopBufferLastIndex; Optional<size_t> mLineLoopBufferLastIndex;
bool mDirtyLineLoopTranslation; bool mDirtyLineLoopTranslation;
// Cache variable for determining whether or not to store new dependencies in the node.
bool mVertexBuffersDirty;
bool mIndexBufferDirty;
// The offset we had the last time we bound the index buffer.
uintptr_t mLastIndexBufferOffset;
}; };
} // namespace rx } // namespace rx
#endif // LIBANGLE_RENDERER_VULKAN_VERTEXARRAYVK_H_ #endif // LIBANGLE_RENDERER_VULKAN_VERTEXARRAYVK_H_
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