blob: acf9ccaa0f8a6e1e8d0896b68f0435a1ef4ace19 [file] [log] [blame]
//
// Copyright 2020 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.
//
// TransformFeedbackMtl.mm:
// Defines the class interface for TransformFeedbackMtl, implementing TransformFeedbackImpl.
//
#include "libANGLE/renderer/metal/TransformFeedbackMtl.h"
#include "libANGLE/renderer/metal/BufferMtl.h"
#include "libANGLE/renderer/metal/ContextMtl.h"
#include "libANGLE/renderer/metal/DisplayMtl.h"
#include "libANGLE/renderer/metal/ProgramMtl.h"
#include "libANGLE/renderer/metal/QueryMtl.h"
#include "libANGLE/renderer/metal/mtl_constants.h"
#include "libANGLE/renderer/metal/mtl_common.h"
#include "libANGLE/Context.h"
#include "libANGLE/Query.h"
#include "common/debug.h"
namespace rx
{
TransformFeedbackMtl::TransformFeedbackMtl(const gl::TransformFeedbackState &state)
: TransformFeedbackImpl(state),
mBufferHandles{},
mBufferOffsets{},
mBufferSizes{},
mAlignedBufferOffsets{}
{}
TransformFeedbackMtl::~TransformFeedbackMtl() {}
angle::Result TransformFeedbackMtl::begin(const gl::Context *context,
gl::PrimitiveMode primitiveMode)
{
ContextMtl *contextMtl = mtl::GetImpl(context);
// Not currently supported with msl compiler
if (!contextMtl->getDisplay()->getFeatures().emulateTransformFeedback.enabled)
{
return angle::Result::Stop;
}
const gl::ProgramExecutable *executable = contextMtl->getState().getProgramExecutable();
ASSERT(executable);
size_t xfbBufferCount = executable->getTransformFeedbackBufferCount();
for (size_t bufferIndex = 0; bufferIndex < xfbBufferCount; ++bufferIndex)
{
const gl::OffsetBindingPointer<gl::Buffer> &binding = mState.getIndexedBuffer(bufferIndex);
ASSERT(binding.get());
BufferMtl *bufferMtl = mtl::GetImpl(binding.get());
assert(bufferMtl);
mBufferOffsets[bufferIndex] = binding.getOffset();
mBufferSizes[bufferIndex] = gl::GetBoundBufferAvailableSize(binding);
mBufferHandles[bufferIndex] = bufferMtl;
ASSERT(contextMtl->getDisplay()->getFeatures().emulateTransformFeedback.enabled);
const MtlDeviceSize offsetAlignment = mtl::kUniformBufferSettingOffsetMinAlignment;
// Make sure there's no possible under/overflow with binding size.
static_assert(sizeof(MtlDeviceSize) >= sizeof(binding.getSize()),
"MtlDeviceSize too small");
// Set the offset as close as possible to the requested offset while remaining aligned.
mAlignedBufferOffsets[bufferIndex] =
(mBufferOffsets[bufferIndex] / offsetAlignment) * offsetAlignment;
}
return contextMtl->onBeginTransformFeedback(xfbBufferCount, mBufferHandles);
}
angle::Result TransformFeedbackMtl::end(const gl::Context *context)
{
ContextMtl *contextMtl = mtl::GetImpl(context);
// Not currently supported with msl compiler
if (!contextMtl->getDisplay()->getFeatures().emulateTransformFeedback.enabled)
{
return angle::Result::Stop;
}
// 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)
{
mtl::GetImpl(transformFeedbackQuery)->onTransformFeedbackEnd(mState.getPrimitivesDrawn());
}
contextMtl->onEndTransformFeedback();
return angle::Result::Continue;
}
angle::Result TransformFeedbackMtl::pause(const gl::Context *context)
{
ContextMtl *contextMtl = mtl::GetImpl(context);
return contextMtl->onPauseTransformFeedback();
}
angle::Result TransformFeedbackMtl::resume(const gl::Context *context)
{
ContextMtl *contextMtl = mtl::GetImpl(context);
const gl::ProgramExecutable *executable = contextMtl->getState().getProgramExecutable();
ASSERT(executable);
size_t xfbBufferCount = executable->getTransformFeedbackBufferCount();
return contextMtl->onBeginTransformFeedback(xfbBufferCount, mBufferHandles);
}
angle::Result TransformFeedbackMtl::bindIndexedBuffer(
const gl::Context *context,
size_t index,
const gl::OffsetBindingPointer<gl::Buffer> &binding)
{
UNIMPLEMENTED();
return angle::Result::Continue;
}
void TransformFeedbackMtl::getBufferOffsets(ContextMtl *contextMtl,
GLint drawCallFirstVertex,
int32_t *offsetsOut,
size_t offsetsSize) const
{
if (!contextMtl->getDisplay()->getFeatures().emulateTransformFeedback.enabled)
return;
GLsizeiptr verticesDrawn = mState.getVerticesDrawn();
const std::vector<GLsizei> &bufferStrides =
mState.getBoundProgram()->getTransformFeedbackStrides();
const gl::ProgramExecutable *executable = contextMtl->getState().getProgramExecutable();
ASSERT(executable);
size_t xfbBufferCount = executable->getTransformFeedbackBufferCount();
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] - mAlignedBufferOffsets[bufferIndex]);
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);
}
}
} // namespace rx