Implement Metal BufferObjects

This commit is contained in:
Benjamin Doherty
2021-04-02 16:27:23 -07:00
parent e29bfc2c86
commit bcb5b2d790
3 changed files with 63 additions and 29 deletions

View File

@@ -174,7 +174,7 @@ void MetalDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elem
void MetalDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType) {
// TODO
construct_handle<MetalBufferObject>(mHandleMap, boh, *mContext, byteCount);
}
void MetalDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
@@ -327,8 +327,7 @@ Handle<HwIndexBuffer> MetalDriver::createIndexBufferS() noexcept {
}
Handle<HwBufferObject> MetalDriver::createBufferObjectS() noexcept {
// TODO
return {};
return alloc_handle<MetalBufferObject, HwBufferObject>();
}
Handle<HwTexture> MetalDriver::createTextureS() noexcept {
@@ -411,7 +410,7 @@ void MetalDriver::destroyIndexBuffer(Handle<HwIndexBuffer> ibh) {
void MetalDriver::destroyBufferObject(Handle<HwBufferObject> boh) {
if (boh) {
// TODO
destruct_handle<MetalBufferObject>(mHandleMap, boh);
}
}
@@ -668,12 +667,18 @@ void MetalDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&
void MetalDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescriptor&& data,
uint32_t byteOffset) {
// TODO
auto* bo = handle_cast<MetalBufferObject>(mHandleMap, boh);
bo->updateBuffer(data.buffer, data.size, byteOffset);
scheduleDestroy(std::move(data));
}
void MetalDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, size_t index,
Handle<HwBufferObject> boh) {
// TODO
auto* vertexBuffer = handle_cast<MetalVertexBuffer>(mHandleMap, vbh);
auto* bufferObject = handle_cast<MetalBufferObject>(mHandleMap, boh);
assert_invariant(index < vertexBuffer->buffers.size());
assert_invariant(bufferObject->getBuffer());
vertexBuffer->buffers[index] = bufferObject->getBuffer();
}
void MetalDriver::update2DImage(Handle<HwTexture> th, uint32_t level, uint32_t xoffset,
@@ -1289,9 +1294,28 @@ void MetalDriver::draw(backend::PipelineState ps, Handle<HwRenderPrimitive> rph)
withRange:samplerRange];
// Bind the vertex buffers.
MetalBuffer* buffers[MAX_VERTEX_ATTRIBUTE_COUNT];
size_t vertexBufferOffsets[MAX_VERTEX_ATTRIBUTE_COUNT];
size_t bufferIndex = 0;
auto vb = primitive->vertexBuffer;
for (uint32_t attributeIndex = 0; attributeIndex < vb->attributes.size(); attributeIndex++) {
const auto& attribute = vb->attributes[attributeIndex];
if (attribute.buffer == Attribute::BUFFER_UNUSED) {
continue;
}
assert_invariant(vb->buffers[attribute.buffer]);
buffers[bufferIndex] = vb->buffers[attribute.buffer].get();
vertexBufferOffsets[bufferIndex] = attribute.offset;
bufferIndex++;
}
const auto bufferCount = bufferIndex;
MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder,
VERTEX_BUFFER_START, MetalBuffer::Stage::VERTEX, primitive->buffers.data(),
primitive->offsets.data(), primitive->buffers.size());
VERTEX_BUFFER_START, MetalBuffer::Stage::VERTEX, buffers,
vertexBufferOffsets, bufferCount);
// Bind the zero buffer, used for missing vertex attributes.
static const char bytes[16] = { 0 };

View File

@@ -112,12 +112,24 @@ private:
void* frameCompletedUserData = nullptr;
};
class MetalBufferObject : public HwBufferObject {
public:
MetalBufferObject(MetalContext& context, uint32_t byteCount);
void updateBuffer(void* data, size_t size, uint32_t byteOffset);
const std::shared_ptr<MetalBuffer>& getBuffer() const { return buffer; }
private:
uint32_t byteCount;
std::shared_ptr<MetalBuffer> buffer = nullptr;
};
struct MetalVertexBuffer : public HwVertexBuffer {
MetalVertexBuffer(MetalContext& context, uint8_t bufferCount, uint8_t attributeCount,
uint32_t vertexCount, AttributeArray const& attributes, bool bufferObjectsEnabled);
~MetalVertexBuffer();
std::vector<MetalBuffer*> buffers;
std::vector<std::shared_ptr<MetalBuffer>> buffers;
};
struct MetalIndexBuffer : public HwIndexBuffer {
@@ -142,9 +154,6 @@ struct MetalRenderPrimitive : public HwRenderPrimitive {
// This struct is used to create the pipeline description to describe vertex assembly.
VertexDescription vertexDescription = {};
std::vector<MetalBuffer*> buffers;
std::vector<NSUInteger> offsets;
};
struct MetalProgram : public HwProgram {

View File

@@ -266,12 +266,27 @@ void MetalSwapChain::scheduleFrameCompletedCallback() {
}];
}
MetalBufferObject::MetalBufferObject(MetalContext& context, uint32_t byteCount)
: byteCount(byteCount), buffer(std::make_shared<MetalBuffer>(context, byteCount)) {}
void MetalBufferObject::updateBuffer(void* data, size_t size, uint32_t byteOffset) {
assert_invariant(byteOffset + size <= byteCount);
buffer->copyIntoBuffer(data, size);
}
MetalVertexBuffer::MetalVertexBuffer(MetalContext& context, uint8_t bufferCount,
uint8_t attributeCount, uint32_t vertexCount, AttributeArray const& attributes,
bool bufferObjectsEnabled)
: HwVertexBuffer(bufferCount, attributeCount, vertexCount, attributes, bufferObjectsEnabled) {
buffers.reserve(bufferCount);
if (bufferObjectsEnabled) {
// If BufferObjects are used, we don't need to make any allocations. Simply reserve enough
// "slots" for future calls to setVertexBufferObject.
buffers.resize(bufferCount);
return;
}
for (uint8_t bufferIndex = 0; bufferIndex < bufferCount; ++bufferIndex) {
// Calculate buffer size.
uint32_t size = 0;
@@ -282,21 +297,14 @@ MetalVertexBuffer::MetalVertexBuffer(MetalContext& context, uint8_t bufferCount,
}
}
MetalBuffer* buffer = nullptr;
std::shared_ptr<MetalBuffer> buffer = nullptr;
if (size > 0) {
buffer = new MetalBuffer(context, size);
buffer = std::make_shared<MetalBuffer>(context, size);
}
buffers.push_back(buffer);
}
}
MetalVertexBuffer::~MetalVertexBuffer() {
for (auto* b : buffers) {
delete b;
}
buffers.clear();
}
MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, uint8_t elementSize, uint32_t indexCount)
: HwIndexBuffer(elementSize, indexCount), buffer(context, elementSize * indexCount, true) { }
@@ -310,10 +318,6 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalInde
const size_t attributeCount = vertexBuffer->attributes.size();
buffers.clear();
buffers.reserve(attributeCount);
offsets.clear();
offsets.reserve(attributeCount);
vertexDescription = {};
// Each attribute gets its own vertex buffer.
@@ -340,9 +344,6 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalInde
continue;
}
buffers.push_back(vertexBuffer->buffers[attribute.buffer]);
offsets.push_back(attribute.offset);
vertexDescription.attributes[attributeIndex] = {
.format = getMetalFormat(attribute.type,
attribute.flags & Attribute::FLAG_NORMALIZED),