mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 06:08:30 +09:00
[Feat] (MG_Impl, MG_Backend, MG_State): implement glMultiDrawArrays and glGetBufferSubData
Two previously-stubbed GL 3.3 Core entry points. glMultiDrawArrays: mirrors the existing glMultiDrawElements(BaseVertex) architecture end to end -- a new MultiDrawArrays backend function-table slot dispatched from the frontend after program/primitive-mode validation (plus a drawcount < 0 -> GL_INVALID_VALUE guard). - DirectGLES: PrepareForDraw once, then loop native glDrawArrays with the same per-range client-side array upload the single DrawArrays does. - DirectVulkan: build a MultiDrawCmd payload and hand it to a new VulkanRenderer::MultiDrawArrays, which does one SetupDraw over the union of the sub-draw vertex ranges and then a vkCmdDraw per range (mirrors VulkanRenderer::MultiDrawElements). glGetBufferSubData: reads a range of the bound buffer's CPU shadow into client memory via a new BufferObject::DownloadSubData, with the same validation shape as BufferSubData (INVALID_VALUE for negative/overflowing range, INVALID_OPERATION for no bound buffer or a non-persistent mapped buffer). The shadow reflects CPU writes and backend write-backs but not arbitrary GPU-side writes, which is documented on the method. Tests: 2 BufferTest cases for glGetBufferSubData (round-trip read of a middle range and the whole buffer, plus out-of-range/negative/no-buffer errors). BufferTest 32/32, SanityTest 30/30, VertexArrayTest 42/42; library builds clean. (The glMultiDrawArrays draw paths are not runtime-testable on this host and are compile-verified against the tested MultiDrawElements pattern.)
This commit is contained in:
@@ -86,6 +86,7 @@ namespace MobileGL {
|
|||||||
void (*DrawElements)(GLenum mode, GLsizei count, GLenum type, const void* indices);
|
void (*DrawElements)(GLenum mode, GLsizei count, GLenum type, const void* indices);
|
||||||
void (*DrawElementsBaseVertex)(GLenum mode, GLsizei count, GLenum type, const void* indices,
|
void (*DrawElementsBaseVertex)(GLenum mode, GLsizei count, GLenum type, const void* indices,
|
||||||
GLint basevertex);
|
GLint basevertex);
|
||||||
|
void (*MultiDrawArrays)(GLenum mode, const GLint* first, const GLsizei* count, GLsizei drawcount);
|
||||||
void (*MultiDrawElements)(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
|
void (*MultiDrawElements)(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
|
||||||
GLsizei drawcount);
|
GLsizei drawcount);
|
||||||
void (*MultiDrawElementsBaseVertex)(GLenum mode, const GLsizei* count, GLenum type,
|
void (*MultiDrawElementsBaseVertex)(GLenum mode, const GLsizei* count, GLenum type,
|
||||||
|
|||||||
@@ -874,6 +874,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
funcsTable.GL.DrawArrays = DrawArrays;
|
funcsTable.GL.DrawArrays = DrawArrays;
|
||||||
funcsTable.GL.DrawElements = DrawElements;
|
funcsTable.GL.DrawElements = DrawElements;
|
||||||
funcsTable.GL.DrawElementsBaseVertex = DrawElementsBaseVertex;
|
funcsTable.GL.DrawElementsBaseVertex = DrawElementsBaseVertex;
|
||||||
|
funcsTable.GL.MultiDrawArrays = MultiDrawArrays;
|
||||||
funcsTable.GL.MultiDrawElements = MultiDrawElements;
|
funcsTable.GL.MultiDrawElements = MultiDrawElements;
|
||||||
funcsTable.GL.MultiDrawElementsBaseVertex = MultiDrawElementsBaseVertex;
|
funcsTable.GL.MultiDrawElementsBaseVertex = MultiDrawElementsBaseVertex;
|
||||||
funcsTable.GL.MultiDrawElementsIndirect = MultiDrawElementsIndirect;
|
funcsTable.GL.MultiDrawElementsIndirect = MultiDrawElementsIndirect;
|
||||||
|
|||||||
@@ -1363,6 +1363,26 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
g_GLESFuncs.glDrawElementsBaseVertex(mode, count, type, indices, basevertex);
|
g_GLESFuncs.glDrawElementsBaseVertex(mode, count, type, indices, basevertex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MultiDrawArrays(GLenum mode, const GLint* first, const GLsizei* count, GLsizei drawcount) {
|
||||||
|
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG && MOBILEGL_ENABLE_SCOPE_MARKER
|
||||||
|
DebugImpl::OpenGLScopeMarker marker(__func__);
|
||||||
|
#endif
|
||||||
|
DrawSyncBit syncBit = DrawSyncBit::None;
|
||||||
|
PrepareForDraw(syncBit);
|
||||||
|
|
||||||
|
const auto& currentVAO = MG_State::pGLContext->GetBoundVertexArray();
|
||||||
|
for (GLsizei i = 0; i < drawcount; ++i) {
|
||||||
|
// Client-side arrays are uploaded per sub-draw range, like the single DrawArrays path.
|
||||||
|
if (currentVAO) {
|
||||||
|
const auto& backendVAOIt = VertexArrayImpl::g_backendVertexArrayObjects.find(currentVAO.get());
|
||||||
|
if (backendVAOIt != VertexArrayImpl::g_backendVertexArrayObjects.end()) {
|
||||||
|
backendVAOIt->second->SyncClientSideAttributesForDrawArrays(currentVAO, first[i], count[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_GLESFuncs.glDrawArrays(mode, first[i], count[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
|
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
|
||||||
GLsizei drawcount) {
|
GLsizei drawcount) {
|
||||||
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG && MOBILEGL_ENABLE_SCOPE_MARKER
|
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG && MOBILEGL_ENABLE_SCOPE_MARKER
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices);
|
void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices);
|
||||||
void DrawArrays(GLenum mode, GLint first, GLsizei count);
|
void DrawArrays(GLenum mode, GLint first, GLsizei count);
|
||||||
void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices, GLint basevertex);
|
void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices, GLint basevertex);
|
||||||
|
void MultiDrawArrays(GLenum mode, const GLint* first, const GLsizei* count, GLsizei drawcount);
|
||||||
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
|
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
|
||||||
GLsizei drawcount);
|
GLsizei drawcount);
|
||||||
void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
|
void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
|
||||||
|
|||||||
@@ -555,6 +555,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
funcsTable.GL.DrawArrays = DrawArrays;
|
funcsTable.GL.DrawArrays = DrawArrays;
|
||||||
funcsTable.GL.DrawElements = DrawElements;
|
funcsTable.GL.DrawElements = DrawElements;
|
||||||
funcsTable.GL.DrawElementsBaseVertex = DrawElementsBaseVertex;
|
funcsTable.GL.DrawElementsBaseVertex = DrawElementsBaseVertex;
|
||||||
|
funcsTable.GL.MultiDrawArrays = MultiDrawArrays;
|
||||||
funcsTable.GL.MultiDrawElements = MultiDrawElements;
|
funcsTable.GL.MultiDrawElements = MultiDrawElements;
|
||||||
funcsTable.GL.MultiDrawElementsBaseVertex = MultiDrawElementsBaseVertex;
|
funcsTable.GL.MultiDrawElementsBaseVertex = MultiDrawElementsBaseVertex;
|
||||||
funcsTable.GL.MultiDrawElementsIndirect = MultiDrawElementsIndirect;
|
funcsTable.GL.MultiDrawElementsIndirect = MultiDrawElementsIndirect;
|
||||||
|
|||||||
@@ -1266,6 +1266,33 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
pVulkanRenderer->DrawElements(payload);
|
pVulkanRenderer->DrawElements(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MultiDrawArrays(GLenum mode, const GLint* first, const GLsizei* count, GLsizei drawcount) {
|
||||||
|
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawArrays called with null VulkanRenderer");
|
||||||
|
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::MultiDrawArrays called with null GL context");
|
||||||
|
if (drawcount <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiDrawCmd payload{};
|
||||||
|
payload.mode = mode;
|
||||||
|
|
||||||
|
// TODO: allocate draw cmd buf elsewhere
|
||||||
|
static Vector<DrawCmdParam> params;
|
||||||
|
params.clear();
|
||||||
|
params.resize(drawcount);
|
||||||
|
|
||||||
|
for (GLsizei i = 0; i < drawcount; ++i) {
|
||||||
|
auto& param = params[i];
|
||||||
|
param.vertexCount = count[i] > 0 ? static_cast<Uint32>(count[i]) : 0;
|
||||||
|
param.instanceCount = 1;
|
||||||
|
param.firstVertex = first[i] > 0 ? static_cast<Uint32>(first[i]) : 0;
|
||||||
|
param.firstInstance = 0;
|
||||||
|
}
|
||||||
|
payload.drawCount = static_cast<Uint32>(drawcount);
|
||||||
|
payload.pParams = params.data();
|
||||||
|
pVulkanRenderer->MultiDrawArrays(payload);
|
||||||
|
}
|
||||||
|
|
||||||
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
|
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
|
||||||
GLsizei drawcount) {
|
GLsizei drawcount) {
|
||||||
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawElements called with null VulkanRenderer");
|
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawElements called with null VulkanRenderer");
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices);
|
void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices);
|
||||||
void DrawArrays(GLenum mode, GLint first, GLsizei count);
|
void DrawArrays(GLenum mode, GLint first, GLsizei count);
|
||||||
void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices, GLint basevertex);
|
void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices, GLint basevertex);
|
||||||
|
void MultiDrawArrays(GLenum mode, const GLint* first, const GLsizei* count, GLsizei drawcount);
|
||||||
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
|
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
|
||||||
GLsizei drawcount);
|
GLsizei drawcount);
|
||||||
void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
|
void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
|
||||||
|
|||||||
@@ -5195,6 +5195,36 @@ void main() {
|
|||||||
payload.params.firstInstance);
|
payload.params.firstInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VulkanRenderer::MultiDrawArrays(const MultiDrawCmd& payload) {
|
||||||
|
auto& frame = m_frameContext.GetCurrent();
|
||||||
|
|
||||||
|
// One state/pipeline setup covering the union of all sub-draw vertex ranges, then a vkCmdDraw
|
||||||
|
// per range -- mirrors MultiDrawElements.
|
||||||
|
DrawCmdParam vertexRange{};
|
||||||
|
for (Uint32 idraw = 0; idraw < payload.drawCount; ++idraw) {
|
||||||
|
vertexRange.vertexCount = std::max(vertexRange.vertexCount,
|
||||||
|
payload.pParams[idraw].firstVertex + payload.pParams[idraw].vertexCount);
|
||||||
|
vertexRange.instanceCount = std::max(vertexRange.instanceCount, payload.pParams[idraw].instanceCount);
|
||||||
|
vertexRange.firstInstance = std::max(vertexRange.firstInstance, payload.pParams[idraw].firstInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SetupDraw(frame, payload.mode, 0, vertexRange)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOBILEGL_ASSERT(frame.isCommandRecording, "%s: frame recording was not started", __func__);
|
||||||
|
|
||||||
|
VkCommandBuffer& commandBuffer = frame.commandBuffer;
|
||||||
|
|
||||||
|
for (Uint32 idraw = 0; idraw < payload.drawCount; ++idraw) {
|
||||||
|
vkCmdDraw(commandBuffer,
|
||||||
|
payload.pParams[idraw].vertexCount,
|
||||||
|
payload.pParams[idraw].instanceCount,
|
||||||
|
payload.pParams[idraw].firstVertex,
|
||||||
|
payload.pParams[idraw].firstInstance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void VulkanRenderer::MultiDrawElements(const MultiDrawIndexedCmd& payload) {
|
void VulkanRenderer::MultiDrawElements(const MultiDrawIndexedCmd& payload) {
|
||||||
auto& frame = m_frameContext.GetCurrent();
|
auto& frame = m_frameContext.GetCurrent();
|
||||||
|
|
||||||
|
|||||||
@@ -87,6 +87,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
DrawIndexedCmdParam* pParams = nullptr;
|
DrawIndexedCmdParam* pParams = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct MultiDrawCmd {
|
||||||
|
GLenum mode = GL_TRIANGLES;
|
||||||
|
Uint32 drawCount = 0;
|
||||||
|
DrawCmdParam* pParams = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
struct QueueFamilyIndices {
|
struct QueueFamilyIndices {
|
||||||
Int32 graphicsFamily = -1;
|
Int32 graphicsFamily = -1;
|
||||||
Int32 presentFamily = -1;
|
Int32 presentFamily = -1;
|
||||||
@@ -161,6 +167,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
static VkMemoryBarrier BuildMemoryBarrierForGlBarriers(GLbitfield barriers);
|
static VkMemoryBarrier BuildMemoryBarrierForGlBarriers(GLbitfield barriers);
|
||||||
void DrawArrays(const DrawCmd& payload);
|
void DrawArrays(const DrawCmd& payload);
|
||||||
void DrawElements(const DrawIndexedCmd& payload);
|
void DrawElements(const DrawIndexedCmd& payload);
|
||||||
|
void MultiDrawArrays(const MultiDrawCmd& payload);
|
||||||
void MultiDrawElements(const MultiDrawIndexedCmd& payloads);
|
void MultiDrawElements(const MultiDrawIndexedCmd& payloads);
|
||||||
void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount,
|
void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount,
|
||||||
GLsizei stride);
|
GLsizei stride);
|
||||||
|
|||||||
@@ -799,6 +799,55 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
bufferObject->UploadSubData({(void*)data, (SizeT)size}, offset);
|
bufferObject->UploadSubData({(void*)data, (SizeT)size}, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GetBufferSubData_State(GLenum target, GLintptr offset, GLsizeiptr size, void* data) {
|
||||||
|
MGLOG_D("%s: target = %s, offset = %d, size = %d, data = %p", __func__,
|
||||||
|
MG_Util::ConvertGLEnumToString(target).c_str(), offset, size, data);
|
||||||
|
if (!data) {
|
||||||
|
// Match BufferSubData_State: a null pointer is a caller bug, not a GL-specified error.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size < 0 || offset < 0) {
|
||||||
|
MG_State::pGLContext->RecordError(ErrorCode::InvalidValue,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetBufferSubData_State",
|
||||||
|
"Offset and size must be non-negative."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target);
|
||||||
|
if (!BufferImpl::ValidateBufferTarget(bufferTarget)) return;
|
||||||
|
auto& bindingSlot = GetBufferBindingSlot(bufferTarget);
|
||||||
|
|
||||||
|
auto& bufferObject = bindingSlot.GetBoundObject();
|
||||||
|
if (!bufferObject) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidOperation,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetBufferSubData_State",
|
||||||
|
"Buffer target is bound to no buffer object."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SizeT bufferSize = bufferObject->GetSize();
|
||||||
|
if (static_cast<SizeT>(offset) + static_cast<SizeT>(size) > bufferSize) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidValue,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetBufferSubData_State",
|
||||||
|
"Offset and size exceed buffer size."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bufferObject->IsMapped() &&
|
||||||
|
!(bufferObject->GetMappingAccess() & BufferMappingAccessBit::Persistent)) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidOperation,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetBufferSubData_State",
|
||||||
|
"Cannot read from a buffer object mapped without GL_MAP_PERSISTENT_BIT."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferObject->DownloadSubData(data, static_cast<SizeT>(offset), static_cast<SizeT>(size));
|
||||||
|
}
|
||||||
|
|
||||||
void BufferData_State(GLenum target, GLsizeiptr size, const void* data, GLenum usage) {
|
void BufferData_State(GLenum target, GLsizeiptr size, const void* data, GLenum usage) {
|
||||||
MGLOG_D("%s: %s, size = %d, data = %p, usage = %s", __func__, MG_Util::ConvertGLEnumToString(target).c_str(),
|
MGLOG_D("%s: %s, size = %d, data = %p, usage = %s", __func__, MG_Util::ConvertGLEnumToString(target).c_str(),
|
||||||
size, data, MG_Util::ConvertGLEnumToString(usage).c_str());
|
size, data, MG_Util::ConvertGLEnumToString(usage).c_str());
|
||||||
@@ -1425,6 +1474,10 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
BufferSubData_State(target, offset, size, data);
|
BufferSubData_State(target, offset, size, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, void* data) {
|
||||||
|
GetBufferSubData_State(target, offset, size, data);
|
||||||
|
}
|
||||||
|
|
||||||
void BufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage) {
|
void BufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage) {
|
||||||
BufferData_State(target, size, data, usage);
|
BufferData_State(target, size, data, usage);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
void CopyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset,
|
void CopyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset,
|
||||||
GLsizeiptr size);
|
GLsizeiptr size);
|
||||||
void BufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void* data);
|
void BufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void* data);
|
||||||
|
void GetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, void* data);
|
||||||
void BufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage);
|
void BufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage);
|
||||||
void BindBuffer(GLenum target, GLuint buffer);
|
void BindBuffer(GLenum target, GLuint buffer);
|
||||||
void GenBuffers(GLsizei n, GLuint* buffers);
|
void GenBuffers(GLsizei n, GLuint* buffers);
|
||||||
|
|||||||
@@ -116,6 +116,13 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
MG_Backend::gBackendFunctionsTable.GL.DrawArrays(mode, first, count);
|
MG_Backend::gBackendFunctionsTable.GL.DrawArrays(mode, first, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MultiDrawArrays_Backend(GLenum mode, const GLint* first, const GLsizei* count, GLsizei drawcount) {
|
||||||
|
#ifdef TRACY_ENABLE
|
||||||
|
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
||||||
|
#endif
|
||||||
|
MG_Backend::gBackendFunctionsTable.GL.MultiDrawArrays(mode, first, count, drawcount);
|
||||||
|
}
|
||||||
|
|
||||||
void DrawElementsBaseVertex_Backend(GLenum mode, GLsizei count, GLenum type, const void* indices,
|
void DrawElementsBaseVertex_Backend(GLenum mode, GLsizei count, GLenum type, const void* indices,
|
||||||
GLint basevertex) {
|
GLint basevertex) {
|
||||||
#ifdef TRACY_ENABLE
|
#ifdef TRACY_ENABLE
|
||||||
@@ -426,6 +433,18 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
DrawArrays_Backend(mode, first, count);
|
DrawArrays_Backend(mode, first, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MultiDrawArrays(GLenum mode, const GLint* first, const GLsizei* count, GLsizei drawcount) {
|
||||||
|
if (!ValidateCurrentProgramForExecution(__func__)) return;
|
||||||
|
if (!ValidatePrimitiveModeForBackend(__func__, mode)) return;
|
||||||
|
if (drawcount < 0) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidValue,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "drawcount must be non-negative."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MultiDrawArrays_Backend(mode, first, count, drawcount);
|
||||||
|
}
|
||||||
|
|
||||||
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const void* const* indices,
|
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const void* const* indices,
|
||||||
GLsizei drawcount) {
|
GLsizei drawcount) {
|
||||||
if (!ValidateCurrentProgramForExecution(__func__)) return;
|
if (!ValidateCurrentProgramForExecution(__func__)) return;
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
void DrawArraysIndirect(GLenum mode, const void* indirect);
|
void DrawArraysIndirect(GLenum mode, const void* indirect);
|
||||||
void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices, GLint basevertex);
|
void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices, GLint basevertex);
|
||||||
void DrawArrays(GLenum mode, GLint first, GLsizei count);
|
void DrawArrays(GLenum mode, GLint first, GLsizei count);
|
||||||
|
void MultiDrawArrays(GLenum mode, const GLint* first, const GLsizei* count, GLsizei drawcount);
|
||||||
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const void* const* indices,
|
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const void* const* indices,
|
||||||
GLsizei drawcount);
|
GLsizei drawcount);
|
||||||
void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const void* const* indices,
|
void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const void* const* indices,
|
||||||
|
|||||||
@@ -785,7 +785,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, LoadTransposeMatrixf, const GLfloat* m) DECL
|
|||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, LoadTransposeMatrixd, const GLdouble* m) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, LoadTransposeMatrixd, m)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, LoadTransposeMatrixd, const GLdouble* m) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, LoadTransposeMatrixd, m)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, MultTransposeMatrixf, const GLfloat* m) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MultTransposeMatrixf, m)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, MultTransposeMatrixf, const GLfloat* m) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MultTransposeMatrixf, m)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, MultTransposeMatrixd, const GLdouble* m) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MultTransposeMatrixd, m)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, MultTransposeMatrixd, const GLdouble* m) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MultTransposeMatrixd, m)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, MultiDrawArrays, GLenum mode, const GLint* first, const GLsizei* count, GLsizei drawcount) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MultiDrawArrays, mode, first, count, drawcount)
|
DECLARE_GL_FUNCTION_HEAD(void, MultiDrawArrays, GLenum mode, const GLint* first, const GLsizei* count, GLsizei drawcount) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MultiDrawArrays, mode, first, count, drawcount)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, MultiDrawElements, GLenum mode, const GLsizei* count, GLenum type, const void* const* indices, GLsizei drawcount) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MultiDrawElements, mode, count, type, indices, drawcount)
|
DECLARE_GL_FUNCTION_HEAD(void, MultiDrawElements, GLenum mode, const GLsizei* count, GLenum type, const void* const* indices, GLsizei drawcount) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MultiDrawElements, mode, count, type, indices, drawcount)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, PointParameterf, GLenum pname, GLfloat param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, PointParameterf, pname, param)
|
DECLARE_GL_FUNCTION_HEAD(void, PointParameterf, GLenum pname, GLfloat param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, PointParameterf, pname, param)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, PointParameterfv, GLenum pname, const GLfloat* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, PointParameterfv, pname, params)
|
DECLARE_GL_FUNCTION_HEAD(void, PointParameterfv, GLenum pname, const GLfloat* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, PointParameterfv, pname, params)
|
||||||
@@ -830,7 +830,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, WindowPos3iv, const GLint* v) DECLARE_GL_FUN
|
|||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, WindowPos3s, GLshort x, GLshort y, GLshort z) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, WindowPos3s, x, y, z)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, WindowPos3s, GLshort x, GLshort y, GLshort z) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, WindowPos3s, x, y, z)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, WindowPos3sv, const GLshort* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, WindowPos3sv, v)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, WindowPos3sv, const GLshort* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, WindowPos3sv, v)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, GetQueryObjectiv, GLuint id, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetQueryObjectiv, id, pname, params)
|
DECLARE_GL_FUNCTION_HEAD(void, GetQueryObjectiv, GLuint id, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetQueryObjectiv, id, pname, params)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetBufferSubData, GLenum target, GLintptr offset, GLsizeiptr size, void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetBufferSubData, target, offset, size, data)
|
DECLARE_GL_FUNCTION_HEAD(void, GetBufferSubData, GLenum target, GLintptr offset, GLsizeiptr size, void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetBufferSubData, target, offset, size, data)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, GetVertexAttribdv, GLuint index, GLenum pname, GLdouble* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetVertexAttribdv, index, pname, params)
|
DECLARE_GL_FUNCTION_HEAD(void, GetVertexAttribdv, GLuint index, GLenum pname, GLdouble* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetVertexAttribdv, index, pname, params)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, VertexAttrib1d, GLuint index, GLdouble x) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttrib1d, index, x)
|
DECLARE_GL_FUNCTION_HEAD(void, VertexAttrib1d, GLuint index, GLdouble x) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttrib1d, index, x)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, VertexAttrib1dv, GLuint index, const GLdouble* v) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttrib1dv, index, v)
|
DECLARE_GL_FUNCTION_HEAD(void, VertexAttrib1dv, GLuint index, const GLdouble* v) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttrib1dv, index, v)
|
||||||
|
|||||||
@@ -185,6 +185,13 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
NotifySubData(atOffset, data.size);
|
NotifySubData(atOffset, data.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BufferObject::DownloadSubData(void* dst, SizeT atOffset, SizeT size) const {
|
||||||
|
MOBILEGL_ASSERT(atOffset + size <= m_size,
|
||||||
|
"DownloadSubData out of bounds: atOffset (%zu) + size (%zu) > m_size (%zu)", atOffset, size,
|
||||||
|
m_size);
|
||||||
|
Memcpy(dst, m_dataPtr->data() + atOffset, size);
|
||||||
|
}
|
||||||
|
|
||||||
void BufferObject::CopyDataFrom(const SharedPtr<BufferObject>& src, SizeT srcOffset, SizeT dstOffset, SizeT size) {
|
void BufferObject::CopyDataFrom(const SharedPtr<BufferObject>& src, SizeT srcOffset, SizeT dstOffset, SizeT size) {
|
||||||
MOBILEGL_ASSERT(!m_isMapped || (m_mappingAccess & BufferMappingAccessBit::Persistent),
|
MOBILEGL_ASSERT(!m_isMapped || (m_mappingAccess & BufferMappingAccessBit::Persistent),
|
||||||
"Cannot copy data while destination buffer is non-persistently mapped.");
|
"Cannot copy data while destination buffer is non-persistently mapped.");
|
||||||
|
|||||||
@@ -117,6 +117,10 @@ namespace MobileGL {
|
|||||||
|
|
||||||
void UploadData(DataPtr data, SizeT atOffset);
|
void UploadData(DataPtr data, SizeT atOffset);
|
||||||
void UploadSubData(DataPtr data, SizeT atOffset);
|
void UploadSubData(DataPtr data, SizeT atOffset);
|
||||||
|
// Reads `size` bytes from the CPU shadow at `atOffset` into `dst` (glGetBufferSubData).
|
||||||
|
// The shadow reflects CPU writes (BufferData/SubData/maps) and backend write-backs, but not
|
||||||
|
// arbitrary GPU-side writes.
|
||||||
|
void DownloadSubData(void* dst, SizeT atOffset, SizeT size) const;
|
||||||
void CopyDataFrom(const SharedPtr<BufferObject>& src, SizeT srcOffset, SizeT dstOffset, SizeT size);
|
void CopyDataFrom(const SharedPtr<BufferObject>& src, SizeT srcOffset, SizeT dstOffset, SizeT size);
|
||||||
|
|
||||||
void* AcquireMemory(Bool markMapped, Bool read, Bool write);
|
void* AcquireMemory(Bool markMapped, Bool read, Bool write);
|
||||||
|
|||||||
@@ -238,6 +238,47 @@ TEST_F(BufferTest, CopyBufferSubData) {
|
|||||||
ASSERT_EQ(srcObj->GetChangeSerial(), srcSerial);
|
ASSERT_EQ(srcObj->GetChangeSerial(), srcSerial);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(BufferTest, GetBufferSubDataRoundTrip) {
|
||||||
|
using namespace MobileGL::MG_Impl::GLImpl;
|
||||||
|
GLuint buf;
|
||||||
|
GenBuffers(1, &buf);
|
||||||
|
BindBuffer(GL_ARRAY_BUFFER, buf);
|
||||||
|
|
||||||
|
const Vector<Int> src{10, 20, 30, 40, 50, 60, 70, 80};
|
||||||
|
const SizeT bytes = src.size() * sizeof(Int);
|
||||||
|
BufferData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(bytes), src.data(), GL_STATIC_DRAW);
|
||||||
|
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||||
|
|
||||||
|
// Read a middle range [2..6).
|
||||||
|
Vector<Int> mid(4, -1);
|
||||||
|
GetBufferSubData(GL_ARRAY_BUFFER, 2 * sizeof(Int), 4 * sizeof(Int), mid.data());
|
||||||
|
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||||
|
EXPECT_EQ(mid, (Vector<Int>{30, 40, 50, 60}));
|
||||||
|
|
||||||
|
// Read the whole buffer back.
|
||||||
|
Vector<Int> whole(src.size(), 0);
|
||||||
|
GetBufferSubData(GL_ARRAY_BUFFER, 0, static_cast<GLsizeiptr>(bytes), whole.data());
|
||||||
|
EXPECT_EQ(whole, src);
|
||||||
|
|
||||||
|
// Out-of-range range -> GL_INVALID_VALUE, destination untouched.
|
||||||
|
Vector<Int> guard(2, 999);
|
||||||
|
GetBufferSubData(GL_ARRAY_BUFFER, static_cast<GLintptr>(bytes) - sizeof(Int), 2 * sizeof(Int), guard.data());
|
||||||
|
EXPECT_EQ(GetError(), GL_INVALID_VALUE);
|
||||||
|
EXPECT_EQ(guard, (Vector<Int>{999, 999}));
|
||||||
|
|
||||||
|
// Negative offset -> GL_INVALID_VALUE.
|
||||||
|
GetBufferSubData(GL_ARRAY_BUFFER, -1, sizeof(Int), guard.data());
|
||||||
|
EXPECT_EQ(GetError(), GL_INVALID_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(BufferTest, GetBufferSubDataNoBufferBound) {
|
||||||
|
using namespace MobileGL::MG_Impl::GLImpl;
|
||||||
|
BindBuffer(GL_ARRAY_BUFFER, 0); // ensure nothing is bound
|
||||||
|
Int dst = 0;
|
||||||
|
GetBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Int), &dst);
|
||||||
|
EXPECT_EQ(GetError(), GL_INVALID_OPERATION);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(BufferTest, WriteWhileMapped) {
|
TEST_F(BufferTest, WriteWhileMapped) {
|
||||||
auto& slot = MobileGL::MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::ShaderStorage);
|
auto& slot = MobileGL::MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::ShaderStorage);
|
||||||
Vector<Uint> bufferNames;
|
Vector<Uint> bufferNames;
|
||||||
|
|||||||
Reference in New Issue
Block a user