Implement Metal BufferObjects
This commit is contained in:
@@ -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 };
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user