mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[Perf] (Pipe): reuse client-array buffers instead of a blocking create per draw, and copy contiguous ranges in one go
This commit is contained in:
@@ -234,7 +234,9 @@ namespace MobileGL::MG_Pipe {
|
||||
m_vertices.push_back(static_cast<Uint64>(vertex));
|
||||
}
|
||||
}
|
||||
std::sort(m_vertices.begin(), m_vertices.end());
|
||||
// An array draw's single range arrives ascending already; sorting it again was most of
|
||||
// the client's time on a client-array draw.
|
||||
if (!std::is_sorted(m_vertices.begin(), m_vertices.end())) std::sort(m_vertices.begin(), m_vertices.end());
|
||||
m_vertices.erase(std::unique(m_vertices.begin(), m_vertices.end()), m_vertices.end());
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,12 @@ namespace MobileGL::MG_Pipe {
|
||||
MGPipeVertexInputEmitterInstance().EmitVertexBuffers(m_context, m_baseInstance);
|
||||
if (m_indexBindingChanged)
|
||||
MGPipeVertexInputEmitterInstance().EmitIndexBuffer(m_context);
|
||||
// The bindings no longer name them: the buffers go back to the share group for the
|
||||
// next client-array draw. Their next respecify orphans the store this draw reads.
|
||||
auto& group = *m_context.GetShareGroup();
|
||||
for (auto& buffer : m_vertexBuffers)
|
||||
if (buffer) group.ReturnClientArrayBuffer(std::move(buffer));
|
||||
if (m_indexBuffer) group.ReturnClientArrayBuffer(std::move(m_indexBuffer));
|
||||
}
|
||||
|
||||
Bool Prepare(MGPDrawInfo& info, const MGPDrawRange* ranges, Uint32 rangeCount,
|
||||
@@ -46,7 +52,8 @@ namespace MobileGL::MG_Pipe {
|
||||
// Start remains an element offset, so base vertex and draw IDs are unchanged.
|
||||
if (clientIndexBytes != 0) {
|
||||
if (!clientIndices || clientIndexBytes > std::numeric_limits<Uint32>::max()) return false;
|
||||
m_indexBuffer = MakeOwnedBuffer(clientIndices, static_cast<SizeT>(clientIndexBytes), BufferTarget::Index);
|
||||
m_indexBuffer = MakeOwnedBuffer(m_context, clientIndices, static_cast<SizeT>(clientIndexBytes),
|
||||
BufferTarget::Index);
|
||||
info.IndexResource = MGPipeResourceTrackerInstance().Find(*m_indexBuffer);
|
||||
info.Flags &= ~static_cast<Uint8>(kDrawHasUserIndices);
|
||||
MGPIndexBuffer binding{};
|
||||
@@ -115,8 +122,14 @@ namespace MobileGL::MG_Pipe {
|
||||
if (attribute.Offset == 0 || byteCount > std::numeric_limits<SizeT>::max() - attribute.Offset)
|
||||
return false;
|
||||
const auto* source = reinterpret_cast<const Uint8*>(attribute.Offset);
|
||||
const Uint64 first = referenced.front();
|
||||
if (stride == 0) {
|
||||
std::memcpy(bytes.data(), source, elementBytes);
|
||||
} else if (last - first + 1 == referenced.size()) {
|
||||
// Contiguous elements (every array draw, most indexed ones): the vertex
|
||||
// records from the first fetched one to the end of the last, in one copy.
|
||||
const SizeT begin = static_cast<SizeT>(first * stride);
|
||||
std::memcpy(bytes.data() + begin, source + begin, byteCount - begin);
|
||||
} else {
|
||||
// Only dereference fetched elements: sparse indices and first>0
|
||||
// do not grant permission to read intervening application memory.
|
||||
@@ -126,7 +139,7 @@ namespace MobileGL::MG_Pipe {
|
||||
}
|
||||
}
|
||||
}
|
||||
m_vertexBuffers[location] = MakeOwnedBuffer(bytes.data(), bytes.size(), BufferTarget::Vertex);
|
||||
m_vertexBuffers[location] = MakeOwnedBuffer(m_context, bytes.data(), bytes.size(), BufferTarget::Vertex);
|
||||
handles[location] = MGPipeResourceTrackerInstance().Find(*m_vertexBuffers[location]);
|
||||
}
|
||||
MGPipeVertexInputEmitterInstance().EmitVertexBuffers(m_context, info.StartInstance, &handles);
|
||||
@@ -173,8 +186,11 @@ namespace MobileGL::MG_Pipe {
|
||||
return attribute.Size > 0 && attribute.Size <= 4 ? component * attribute.Size : 0;
|
||||
}
|
||||
|
||||
static UniquePtr<Buffer> MakeOwnedBuffer(const void* bytes, SizeT size, BufferTarget target) {
|
||||
auto buffer = MakeUnique<Buffer>(0);
|
||||
static UniquePtr<Buffer> MakeOwnedBuffer(Context& context, const void* bytes, SizeT size, BufferTarget target) {
|
||||
// A kept buffer is already a published resource: respecifying it is fire-and-forget,
|
||||
// where a new one costs a blocking create (and a destroy after the draw).
|
||||
auto buffer = context.GetShareGroup()->TakeClientArrayBuffer();
|
||||
if (!buffer) buffer = MakeUnique<Buffer>(0);
|
||||
const auto handle = MGPipeResourceTrackerInstance().Find(*buffer);
|
||||
MGPipeResourceTrackerInstance().NoteBoundAs(handle, target);
|
||||
buffer->SetUsage(BufferUsage::StreamDraw);
|
||||
|
||||
@@ -187,4 +187,26 @@ namespace MobileGL::MG_State::GLState {
|
||||
Bool ShareGroupState::ValidateRenderbufferObject(Uint index) const {
|
||||
return m_renderbufferObjects.find(index) != m_renderbufferObjects.end();
|
||||
}
|
||||
UniquePtr<BufferObject> ShareGroupState::TakeClientArrayBuffer() {
|
||||
const std::lock_guard<std::mutex> lock(m_clientArrayBuffersMutex);
|
||||
if (m_clientArrayBuffers.empty()) return nullptr;
|
||||
UniquePtr<BufferObject> buffer = std::move(m_clientArrayBuffers.back());
|
||||
m_clientArrayBuffers.pop_back();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void ShareGroupState::ReturnClientArrayBuffer(UniquePtr<BufferObject> buffer) {
|
||||
// Enough for a draw's every attribute plus its indices; more is never in use at once.
|
||||
constexpr SizeT kKeptClientArrayBuffers = 48;
|
||||
if (!buffer) return;
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(m_clientArrayBuffersMutex);
|
||||
if (m_clientArrayBuffers.size() < kKeptClientArrayBuffers) {
|
||||
m_clientArrayBuffers.push_back(std::move(buffer));
|
||||
return;
|
||||
}
|
||||
}
|
||||
// A surplus buffer is destroyed outside the lock (its destructor emits).
|
||||
}
|
||||
|
||||
} // namespace MobileGL::MG_State::GLState
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
#include <Includes.h>
|
||||
#include <mutex>
|
||||
#include <MG_Util/Miscellany/IndexGenerator.h>
|
||||
#include "BufferState/BufferObject.h"
|
||||
#include "ProgramState/ProgramState.h"
|
||||
@@ -67,6 +68,12 @@ namespace MobileGL::MG_State::GLState {
|
||||
ProgramState& Programs() { return m_programState; }
|
||||
const ProgramState& Programs() const { return m_programState; }
|
||||
|
||||
// Unnamed buffers that carried a draw's client-memory arrays, kept for the next such draw
|
||||
// so it respecifies one of them (a non-blocking record) instead of creating a resource
|
||||
// (a round trip) and destroying it again. Buffers are share-group objects, hence here.
|
||||
UniquePtr<BufferObject> TakeClientArrayBuffer();
|
||||
void ReturnClientArrayBuffer(UniquePtr<BufferObject> buffer);
|
||||
|
||||
private:
|
||||
Uint64 m_token = 0;
|
||||
UnorderedMap<Uint, SharedPtr<BufferObject>> m_bufferObjects;
|
||||
@@ -77,5 +84,7 @@ namespace MobileGL::MG_State::GLState {
|
||||
IndexGenerator<Uint> m_renderbufferNames{1024, 1};
|
||||
SamplerState m_samplerState;
|
||||
ProgramState m_programState;
|
||||
std::mutex m_clientArrayBuffersMutex;
|
||||
Vector<UniquePtr<BufferObject>> m_clientArrayBuffers;
|
||||
};
|
||||
} // namespace MobileGL::MG_State::GLState
|
||||
|
||||
Reference in New Issue
Block a user