blob: 222ef487884a5382f4ddf423e16e53c9eefe91b8 [file] [log] [blame]
//
// 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.
//
// TransformFeedbackVk.cpp:
// Implements the class methods for TransformFeedbackVk.
//
#include "libANGLE/renderer/vulkan/TransformFeedbackVk.h"
#include "libANGLE/Context.h"
#include "libANGLE/Query.h"
#include "libANGLE/renderer/vulkan/BufferVk.h"
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/FramebufferVk.h"
#include "libANGLE/renderer/vulkan/ProgramVk.h"
#include "libANGLE/renderer/vulkan/QueryVk.h"
#include "libANGLE/renderer/vulkan/ShaderInterfaceVariableInfoMap.h"
#include "common/debug.h"
namespace rx
{
TransformFeedbackVk::TransformFeedbackVk(const gl::TransformFeedbackState &state)
: TransformFeedbackImpl(state),
mRebindTransformFeedbackBuffer(false),
mBufferHelpers{},
mBufferHandles{},
mBufferOffsets{},
mBufferSizes{},
mCounterBufferHandles{},
mCounterBufferOffsets{}
{
for (angle::SubjectIndex bufferIndex = 0;
bufferIndex < gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS; ++bufferIndex)
{
mBufferObserverBindings.emplace_back(this, bufferIndex);
}
}
TransformFeedbackVk::~TransformFeedbackVk() {}
void TransformFeedbackVk::onDestroy(const gl::Context *context)
{
ContextVk *contextVk = vk::GetImpl(context);
RendererVk *rendererVk = contextVk->getRenderer();
releaseCounterBuffers(rendererVk);
}
void TransformFeedbackVk::releaseCounterBuffers(RendererVk *renderer)
{
for (vk::BufferHelper &bufferHelper : mCounterBufferHelpers)
{
bufferHelper.release(renderer);
}
for (VkBuffer &buffer : mCounterBufferHandles)
{
buffer = VK_NULL_HANDLE;
}
for (VkDeviceSize &offset : mCounterBufferOffsets)
{
offset = 0;
}
}
void TransformFeedbackVk::initializeXFBVariables(ContextVk *contextVk, uint32_t xfbBufferCount)
{
for (uint32_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
{
const gl::OffsetBindingPointer<gl::Buffer> &binding = mState.getIndexedBuffer(bufferIndex);
ASSERT(binding.get());
BufferVk *bufferVk = vk::GetImpl(binding.get());
if (bufferVk->isBufferValid())
{
mBufferHelpers[bufferIndex] = &bufferVk->getBuffer();
mBufferOffsets[bufferIndex] =
binding.getOffset() + mBufferHelpers[bufferIndex]->getOffset();
mBufferSizes[bufferIndex] = gl::GetBoundBufferAvailableSize(binding);
mBufferObserverBindings[bufferIndex].bind(bufferVk);
}
else
{
// This can happen in error conditions.
vk::BufferHelper &nullBuffer = contextVk->getEmptyBuffer();
mBufferHelpers[bufferIndex] = &nullBuffer;
mBufferOffsets[bufferIndex] = 0;
mBufferSizes[bufferIndex] = nullBuffer.getSize();
mBufferObserverBindings[bufferIndex].reset();
}
}
}
angle::Result TransformFeedbackVk::begin(const gl::Context *context,
gl::PrimitiveMode primitiveMode)
{
ContextVk *contextVk = vk::GetImpl(context);
const gl::ProgramExecutable *executable = contextVk->getState().getProgramExecutable();
ASSERT(executable);
size_t xfbBufferCount = executable->getTransformFeedbackBufferCount();
initializeXFBVariables(contextVk, static_cast<uint32_t>(xfbBufferCount));
for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
{
mBufferHandles[bufferIndex] = mBufferHelpers[bufferIndex]->getBuffer().getHandle();
if (contextVk->getFeatures().supportsTransformFeedbackExtension.enabled)
{
if (mCounterBufferHandles[bufferIndex] == VK_NULL_HANDLE)
{
vk::BufferHelper &bufferHelper = mCounterBufferHelpers[bufferIndex];
ANGLE_TRY(bufferHelper.initSuballocation(
contextVk, contextVk->getRenderer()->getDeviceLocalMemoryTypeIndex(), 16,
contextVk->getRenderer()->getDefaultBufferAlignment(),
BufferUsageType::Static));
mCounterBufferHandles[bufferIndex] = bufferHelper.getBuffer().getHandle();
mCounterBufferOffsets[bufferIndex] = bufferHelper.getOffset();
}
}
}
if (contextVk->getFeatures().supportsTransformFeedbackExtension.enabled)
{
mRebindTransformFeedbackBuffer = true;
}
return contextVk->onBeginTransformFeedback(xfbBufferCount, mBufferHelpers,
mCounterBufferHelpers);
}
angle::Result TransformFeedbackVk::end(const gl::Context *context)
{
ContextVk *contextVk = vk::GetImpl(context);
// If there's an active transform feedback query, accumulate the primitives drawn.
const gl::State &glState = context->getState();
gl::Query *transformFeedbackQuery =
glState.getActiveQuery(gl::QueryType::TransformFeedbackPrimitivesWritten);
if (transformFeedbackQuery && contextVk->getFeatures().emulateTransformFeedback.enabled)
{
vk::GetImpl(transformFeedbackQuery)->onTransformFeedbackEnd(mState.getPrimitivesDrawn());
}
for (angle::ObserverBinding &bufferBinding : mBufferObserverBindings)
{
bufferBinding.reset();
}
contextVk->onEndTransformFeedback();
releaseCounterBuffers(contextVk->getRenderer());
return angle::Result::Continue;
}
angle::Result TransformFeedbackVk::pause(const gl::Context *context)
{
ContextVk *contextVk = vk::GetImpl(context);
return contextVk->onPauseTransformFeedback();
}
angle::Result TransformFeedbackVk::resume(const gl::Context *context)
{
ContextVk *contextVk = vk::GetImpl(context);
const gl::ProgramExecutable *executable = contextVk->getState().getProgramExecutable();
ASSERT(executable);
size_t xfbBufferCount = executable->getTransformFeedbackBufferCount();
if (contextVk->getFeatures().emulateTransformFeedback.enabled)
{
initializeXFBVariables(contextVk, static_cast<uint32_t>(xfbBufferCount));
}
return contextVk->onBeginTransformFeedback(xfbBufferCount, mBufferHelpers,
mCounterBufferHelpers);
}
angle::Result TransformFeedbackVk::bindIndexedBuffer(
const gl::Context *context,
size_t index,
const gl::OffsetBindingPointer<gl::Buffer> &binding)
{
ContextVk *contextVk = vk::GetImpl(context);
// Make sure the transform feedback buffers are bound to the program descriptor sets.
contextVk->invalidateCurrentTransformFeedbackBuffers();
return angle::Result::Continue;
}
void TransformFeedbackVk::getBufferOffsets(ContextVk *contextVk,
GLint drawCallFirstVertex,
int32_t *offsetsOut,
size_t offsetsSize) const
{
if (!contextVk->getFeatures().emulateTransformFeedback.enabled)
{
return;
}
GLsizeiptr verticesDrawn = mState.getVerticesDrawn();
const gl::ProgramExecutable *executable = contextVk->getState().getProgramExecutable();
ASSERT(executable);
const std::vector<GLsizei> &bufferStrides = executable->getTransformFeedbackStrides();
size_t xfbBufferCount = executable->getTransformFeedbackBufferCount();
const VkDeviceSize offsetAlignment = contextVk->getRenderer()
->getPhysicalDeviceProperties()
.limits.minStorageBufferOffsetAlignment;
ASSERT(xfbBufferCount > 0);
// The caller should make sure the offsets array has enough space. The maximum possible
// number of outputs is gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_BUFFERS.
ASSERT(offsetsSize >= xfbBufferCount);
for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
{
int64_t offsetFromDescriptor =
static_cast<int64_t>(mBufferOffsets[bufferIndex] % offsetAlignment);
int64_t drawCallVertexOffset = static_cast<int64_t>(verticesDrawn) - drawCallFirstVertex;
int64_t writeOffset =
(offsetFromDescriptor + drawCallVertexOffset * bufferStrides[bufferIndex]) /
static_cast<int64_t>(sizeof(uint32_t));
offsetsOut[bufferIndex] = static_cast<int32_t>(writeOffset);
// Assert on overflow. For now, support transform feedback up to 2GB.
ASSERT(offsetsOut[bufferIndex] == writeOffset);
}
}
void TransformFeedbackVk::onSubjectStateChange(angle::SubjectIndex index,
angle::SubjectMessage message)
{
if (message == angle::SubjectMessage::InternalMemoryAllocationChanged)
{
ASSERT(index < mBufferObserverBindings.size());
const gl::OffsetBindingPointer<gl::Buffer> &binding = mState.getIndexedBuffer(index);
ASSERT(binding.get());
BufferVk *bufferVk = vk::GetImpl(binding.get());
ASSERT(bufferVk->isBufferValid());
mBufferHelpers[index] = &bufferVk->getBuffer();
mBufferOffsets[index] = binding.getOffset() + mBufferHelpers[index]->getOffset();
mBufferSizes[index] = std::min<VkDeviceSize>(gl::GetBoundBufferAvailableSize(binding),
mBufferHelpers[index]->getSize());
mBufferObserverBindings[index].bind(bufferVk);
mBufferHandles[index] = mBufferHelpers[index]->getBuffer().getHandle();
}
}
void TransformFeedbackVk::updateTransformFeedbackDescriptorDesc(
const vk::Context *context,
const gl::ProgramExecutable &executable,
const ShaderInterfaceVariableInfoMap &variableInfoMap,
const vk::WriteDescriptorDescs &writeDescriptorDescs,
const vk::BufferHelper &emptyBuffer,
bool activeUnpaused,
vk::DescriptorSetDescBuilder *builder) const
{
size_t xfbBufferCount = executable.getTransformFeedbackBufferCount();
for (uint32_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
{
if (mBufferHelpers[bufferIndex] && activeUnpaused)
{
builder->updateTransformFeedbackBuffer(context, variableInfoMap, writeDescriptorDescs,
bufferIndex, *mBufferHelpers[bufferIndex],
mBufferOffsets[bufferIndex],
mBufferSizes[bufferIndex]);
}
else
{
builder->updateTransformFeedbackBuffer(context, variableInfoMap, writeDescriptorDescs,
bufferIndex, emptyBuffer, 0,
emptyBuffer.getSize());
}
}
}
void TransformFeedbackVk::onNewDescriptorSet(const gl::ProgramExecutable &executable,
const vk::SharedDescriptorSetCacheKey &sharedCacheKey)
{
size_t xfbBufferCount = executable.getTransformFeedbackBufferCount();
for (uint32_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
{
const gl::OffsetBindingPointer<gl::Buffer> &binding = mState.getIndexedBuffer(bufferIndex);
if (binding.get())
{
BufferVk *bufferVk = vk::GetImpl(binding.get());
if (bufferVk->getBuffer().valid())
{
bufferVk->getBuffer().getBufferBlock()->onNewDescriptorSet(sharedCacheKey);
}
}
}
}
} // namespace rx