mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 04:08:32 +09:00
[Fix] (MG_Backend/DirectGLES): support Voxy rendering
Implemented: - Advertise Voxy-required DirectGLES extensions without raising the reported OpenGL version. - Add DirectGLES multi draw indirect count emulation and preserve GL draw indirect baseInstance semantics on GLES. - Add DirectGLES DSA framebuffer clear/blit paths used by Minecraft and Voxy presentation. Fixed: - Rewrite gl_BaseInstance in DirectGLES vertex shaders and provide a backend uniform for indirect draw emulation. - Materialize framebuffer attachment textures during DirectGLES FBO sync so named framebuffer operations do not desync backend attachment state. - Avoid redundant texture buffer rebinding and handle texture buffers without bound storage during backend sync. Tests: - Add MG_Test coverage for DirectGLES Voxy extension advertising, baseInstance shader rewriting, and DSA named framebuffer clear/blit backend wiring.
This commit is contained in:
@@ -132,7 +132,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
V_OpenGL33, E_GL_ARB_draw_buffers_blend, E_GL_ARB_compute_shader,
|
||||
E_GL_ARB_shader_storage_buffer_object, E_GL_ARB_shader_image_load_store,
|
||||
E_GL_ARB_program_interface_query, E_GL_ARB_framebuffer_object,
|
||||
E_GL_EXT_framebuffer_object, E_GL_ARB_depth_texture, E_GL_ARB_buffer_storage},
|
||||
E_GL_EXT_framebuffer_object, E_GL_ARB_depth_texture, E_GL_ARB_buffer_storage,
|
||||
E_GL_ARB_texture_storage, E_GL_ARB_direct_state_access,
|
||||
E_GL_ARB_multi_draw_indirect, E_GL_ARB_indirect_parameters,
|
||||
E_GL_ARB_shader_draw_parameters},
|
||||
.IsCompatibilityProfile = false // Is Compatibility Profile
|
||||
},
|
||||
.StaticBackendCapability = {.AllowVSOnlyPrograms = false} // Backend Capability
|
||||
@@ -166,6 +169,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
funcsTable.GL.MultiDrawElements = MultiDrawElements;
|
||||
funcsTable.GL.MultiDrawElementsBaseVertex = MultiDrawElementsBaseVertex;
|
||||
funcsTable.GL.MultiDrawElementsIndirect = MultiDrawElementsIndirect;
|
||||
funcsTable.GL.MultiDrawElementsIndirectCount = MultiDrawElementsIndirectCount;
|
||||
funcsTable.GL.MultiDrawArraysIndirect = MultiDrawArraysIndirect;
|
||||
funcsTable.GL.DrawRangeElementsBaseVertex = DrawRangeElementsBaseVertex;
|
||||
funcsTable.GL.DrawRangeElements = DrawRangeElements;
|
||||
@@ -197,7 +201,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
funcsTable.GL.ClearBufferfv = ClearBufferfv;
|
||||
funcsTable.GL.ClearBufferuiv = ClearBufferuiv;
|
||||
funcsTable.GL.ClearBufferiv = ClearBufferiv;
|
||||
funcsTable.GL.ClearNamedFramebufferfv = ClearNamedFramebufferfv;
|
||||
funcsTable.GL.ClearNamedFramebufferfi = ClearNamedFramebufferfi;
|
||||
funcsTable.GL.BlitFramebuffer = BlitFramebuffer;
|
||||
funcsTable.GL.BlitNamedFramebuffer = BlitNamedFramebuffer;
|
||||
funcsTable.GL.CopyTexImage2D = CopyTexImage2D;
|
||||
funcsTable.GL.CopyTexSubImage2D = CopyTexSubImage2D;
|
||||
funcsTable.GL.GenerateMipmap = GenerateMipmap;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <MG_Util/Converters/MGToGL/TextureEnumConverter.h>
|
||||
#include <MG_Util/Converters/MGToStr/TextureEnumConverter.h>
|
||||
#include <MG_Util/Converters/MGToGL/RenderStateEnumConverter.h>
|
||||
#include <MG_Util/Metrics/BufferMetrics.h>
|
||||
#include <MG_Util/Texture/PixelStoreProcessor.h>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
@@ -56,6 +57,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
return a;
|
||||
}
|
||||
|
||||
struct DrawElementsIndirectCommand {
|
||||
Uint32 count = 0;
|
||||
Uint32 instanceCount = 0;
|
||||
Uint32 firstIndex = 0;
|
||||
Int32 baseVertex = 0;
|
||||
Uint32 baseInstance = 0;
|
||||
};
|
||||
|
||||
namespace DebugImpl {
|
||||
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG
|
||||
void ErrorLopper::Loop(const std::function<void(GLenum)>& func) {
|
||||
@@ -623,6 +632,41 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
}
|
||||
|
||||
void SyncAndBindFramebufferObject(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
|
||||
FramebufferTarget target, Bool forceSync = false) {
|
||||
#ifdef TRACY_ENABLE
|
||||
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
||||
#endif
|
||||
if (!framebuffer || framebuffer == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO) {
|
||||
g_GLESFuncs.glBindFramebuffer(target == FramebufferTarget::Draw ? GL_DRAW_FRAMEBUFFER : GL_READ_FRAMEBUFFER,
|
||||
0);
|
||||
return;
|
||||
}
|
||||
|
||||
auto& registry = FramebufferImpl::g_backendFramebufferObjects;
|
||||
const auto& backendFBOIt = registry.find(framebuffer.get());
|
||||
const Bool exists = backendFBOIt != registry.end();
|
||||
auto& backendObj = exists ? backendFBOIt->second : registry.GetOrCreate(framebuffer);
|
||||
if (!exists) {
|
||||
backendObj = MakeShared<FramebufferImpl::BackendFramebufferObject>();
|
||||
}
|
||||
if (forceSync) {
|
||||
backendObj->InvalidateSyncedState();
|
||||
}
|
||||
|
||||
backendObj->SyncToBackend(framebuffer, target);
|
||||
backendObj->Bind(target);
|
||||
}
|
||||
|
||||
void ForceBindCurrentFBO(FramebufferTarget target) {
|
||||
#ifdef TRACY_ENABLE
|
||||
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
||||
#endif
|
||||
auto& slot = MG_State::pGLContext->GetFramebufferBindingSlot(target);
|
||||
SyncAndBindFramebufferObject(slot.GetBoundObject(), target);
|
||||
FramebufferImpl::g_fboBindVersions[(SizeT)target] = slot.GetVersion();
|
||||
}
|
||||
|
||||
void PrepareForDraw(DrawSyncBit syncBit) {
|
||||
#ifdef TRACY_ENABLE
|
||||
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
||||
@@ -804,6 +848,17 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
}
|
||||
|
||||
void SetCurrentBaseInstance(Uint32 baseInstance) {
|
||||
const auto& currentProgram = MG_State::pGLContext->GetCurrentProgram();
|
||||
if (!currentProgram || !currentProgram->GetLinkStatus()) {
|
||||
return;
|
||||
}
|
||||
const auto& backendProgramIt = PrgramImpl::g_backendProgramObjects.find(currentProgram.get());
|
||||
if (backendProgramIt != PrgramImpl::g_backendProgramObjects.end()) {
|
||||
backendProgramIt->second->SetBaseInstance(baseInstance);
|
||||
}
|
||||
}
|
||||
|
||||
void PrepareForCompute(Bool includeDispatchIndirectBuffer) {
|
||||
#ifdef TRACY_ENABLE
|
||||
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
||||
@@ -930,14 +985,127 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG && MOBILEGL_ENABLE_SCOPE_MARKER
|
||||
DebugImpl::OpenGLScopeMarker marker(__func__);
|
||||
#endif
|
||||
DrawSyncBit syncBit = DrawSyncBit::IndexBuffer | DrawSyncBit::IndirectBuffer;
|
||||
if (drawcount <= 0) {
|
||||
return;
|
||||
}
|
||||
if (stride == 0) {
|
||||
stride = sizeof(DrawElementsIndirectCommand);
|
||||
}
|
||||
if (stride < static_cast<GLsizei>(sizeof(DrawElementsIndirectCommand))) {
|
||||
MGLOG_E("MultiDrawElementsIndirect skipped: stride %d is smaller than command size %zu",
|
||||
stride, sizeof(DrawElementsIndirectCommand));
|
||||
return;
|
||||
}
|
||||
|
||||
DrawSyncBit syncBit = DrawSyncBit::IndexBuffer | DrawSyncBit::IndirectBuffer | DrawSyncBit::Instancing;
|
||||
PrepareForDraw(syncBit);
|
||||
|
||||
for (GLsizei i = 0; i < drawcount; ++i) {
|
||||
const GLvoid* cmd = reinterpret_cast<const GLvoid*>(reinterpret_cast<const uint8_t*>(indirect) +
|
||||
i * (stride ? stride : sizeof(GLsizei) * 4));
|
||||
g_GLESFuncs.glDrawElementsIndirect(mode, type, cmd);
|
||||
const SizeT indexSize = MG_Util::GetGLTypeSize(type);
|
||||
if (indexSize == 0) {
|
||||
MGLOG_E("MultiDrawElementsIndirect skipped: unsupported index type 0x%x", type);
|
||||
return;
|
||||
}
|
||||
|
||||
auto drawBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();
|
||||
if (!drawBuffer) {
|
||||
MGLOG_E("MultiDrawElementsIndirect skipped: no GL_DRAW_INDIRECT_BUFFER is bound");
|
||||
return;
|
||||
}
|
||||
drawBuffer->MarkPersistentMappedRangeDirty();
|
||||
const auto drawData = drawBuffer->GetDataReadOnly();
|
||||
const SizeT commandOffset = reinterpret_cast<SizeT>(indirect);
|
||||
const SizeT commandBytes = commandOffset + static_cast<SizeT>(stride) * static_cast<SizeT>(drawcount - 1) +
|
||||
sizeof(DrawElementsIndirectCommand);
|
||||
if (!drawData || commandBytes > drawData->size()) {
|
||||
MGLOG_E("MultiDrawElementsIndirect skipped: invalid GL_DRAW_INDIRECT_BUFFER binding or range");
|
||||
return;
|
||||
}
|
||||
|
||||
for (GLsizei i = 0; i < drawcount; ++i) {
|
||||
DrawElementsIndirectCommand cmd{};
|
||||
std::memcpy(&cmd, drawData->data() + commandOffset + static_cast<SizeT>(i) * stride, sizeof(cmd));
|
||||
if (cmd.count == 0 || cmd.instanceCount == 0) {
|
||||
continue;
|
||||
}
|
||||
SetCurrentBaseInstance(cmd.baseInstance);
|
||||
const auto indexByteOffset = static_cast<SizeT>(cmd.firstIndex) * indexSize;
|
||||
g_GLESFuncs.glDrawElementsInstancedBaseVertex(
|
||||
mode, static_cast<GLsizei>(cmd.count), type, reinterpret_cast<const GLvoid*>(indexByteOffset),
|
||||
static_cast<GLsizei>(cmd.instanceCount), cmd.baseVertex);
|
||||
}
|
||||
SetCurrentBaseInstance(0);
|
||||
}
|
||||
|
||||
void MultiDrawElementsIndirectCount(GLenum mode, GLenum type, const void* indirect, GLintptr drawcount,
|
||||
GLsizei maxdrawcount, GLsizei stride) {
|
||||
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG && MOBILEGL_ENABLE_SCOPE_MARKER
|
||||
DebugImpl::OpenGLScopeMarker marker(__func__);
|
||||
#endif
|
||||
if (maxdrawcount <= 0) {
|
||||
return;
|
||||
}
|
||||
if (stride == 0) {
|
||||
stride = sizeof(DrawElementsIndirectCommand);
|
||||
}
|
||||
if (stride < static_cast<GLsizei>(sizeof(DrawElementsIndirectCommand))) {
|
||||
MGLOG_E("MultiDrawElementsIndirectCount skipped: stride %d is smaller than command size %zu",
|
||||
stride, sizeof(DrawElementsIndirectCommand));
|
||||
return;
|
||||
}
|
||||
|
||||
DrawSyncBit syncBit = DrawSyncBit::IndexBuffer | DrawSyncBit::IndirectBuffer | DrawSyncBit::Instancing;
|
||||
PrepareForDraw(syncBit);
|
||||
|
||||
const SizeT indexSize = MG_Util::GetGLTypeSize(type);
|
||||
if (indexSize == 0) {
|
||||
MGLOG_E("MultiDrawElementsIndirectCount skipped: unsupported index type 0x%x", type);
|
||||
return;
|
||||
}
|
||||
|
||||
auto drawBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::DrawIndirect).GetBoundObject();
|
||||
auto parameterBuffer = MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::Parameter).GetBoundObject();
|
||||
if (!drawBuffer) {
|
||||
MGLOG_E("MultiDrawElementsIndirectCount skipped: no GL_DRAW_INDIRECT_BUFFER is bound");
|
||||
return;
|
||||
}
|
||||
if (!parameterBuffer) {
|
||||
MGLOG_E("MultiDrawElementsIndirectCount skipped: no GL_PARAMETER_BUFFER is bound");
|
||||
return;
|
||||
}
|
||||
|
||||
drawBuffer->MarkPersistentMappedRangeDirty();
|
||||
parameterBuffer->MarkPersistentMappedRangeDirty();
|
||||
const auto drawData = drawBuffer->GetDataReadOnly();
|
||||
const auto parameterData = parameterBuffer->GetDataReadOnly();
|
||||
|
||||
const SizeT commandOffset = reinterpret_cast<SizeT>(indirect);
|
||||
const SizeT commandBytes = commandOffset + static_cast<SizeT>(stride) * static_cast<SizeT>(maxdrawcount - 1) +
|
||||
sizeof(DrawElementsIndirectCommand);
|
||||
if (!drawData || commandBytes > drawData->size()) {
|
||||
MGLOG_E("MultiDrawElementsIndirectCount skipped: invalid GL_DRAW_INDIRECT_BUFFER binding or range");
|
||||
return;
|
||||
}
|
||||
if (!parameterData || drawcount < 0 || static_cast<SizeT>(drawcount) + sizeof(Uint32) > parameterData->size()) {
|
||||
MGLOG_E("MultiDrawElementsIndirectCount skipped: invalid GL_PARAMETER_BUFFER binding or range");
|
||||
return;
|
||||
}
|
||||
|
||||
Uint32 actualDrawCount = 0;
|
||||
std::memcpy(&actualDrawCount, parameterData->data() + drawcount, sizeof(actualDrawCount));
|
||||
actualDrawCount = std::min<Uint32>(actualDrawCount, static_cast<Uint32>(maxdrawcount));
|
||||
for (Uint32 i = 0; i < actualDrawCount; ++i) {
|
||||
DrawElementsIndirectCommand cmd{};
|
||||
std::memcpy(&cmd, drawData->data() + commandOffset + static_cast<SizeT>(i) * stride, sizeof(cmd));
|
||||
if (cmd.count == 0 || cmd.instanceCount == 0) {
|
||||
continue;
|
||||
}
|
||||
SetCurrentBaseInstance(cmd.baseInstance);
|
||||
const auto indexByteOffset = static_cast<SizeT>(cmd.firstIndex) * indexSize;
|
||||
g_GLESFuncs.glDrawElementsInstancedBaseVertex(
|
||||
mode, static_cast<GLsizei>(cmd.count), type, reinterpret_cast<const GLvoid*>(indexByteOffset),
|
||||
static_cast<GLsizei>(cmd.instanceCount), cmd.baseVertex);
|
||||
}
|
||||
SetCurrentBaseInstance(0);
|
||||
}
|
||||
|
||||
void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride) {
|
||||
@@ -1048,6 +1216,31 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
});
|
||||
}
|
||||
|
||||
void BlitNamedFramebuffer(const SharedPtr<MG_State::GLState::FramebufferObject>& readFramebuffer,
|
||||
const SharedPtr<MG_State::GLState::FramebufferObject>& drawFramebuffer,
|
||||
GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
|
||||
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
|
||||
GLbitfield mask, GLenum filter) {
|
||||
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG && MOBILEGL_ENABLE_SCOPE_MARKER
|
||||
DebugImpl::OpenGLScopeMarker marker(__func__);
|
||||
#endif
|
||||
TextureImpl::SyncNeccessaryTextures();
|
||||
RenderStateImpl::SyncRenderState();
|
||||
|
||||
SyncAndBindFramebufferObject(readFramebuffer, FramebufferTarget::Read, true);
|
||||
SyncAndBindFramebufferObject(drawFramebuffer, FramebufferTarget::Draw, true);
|
||||
|
||||
MGLOG_D("ES %s(%d, %d, %d, %d, %d, %d, %d, %d, 0x%x, %s)", __func__, srcX0, srcY0, srcX1, srcY1,
|
||||
dstX0, dstY0, dstX1, dstY1, mask, MG_Util::ConvertGLEnumToString(filter).c_str());
|
||||
g_GLESFuncs.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
|
||||
DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
|
||||
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
|
||||
});
|
||||
|
||||
ForceBindCurrentFBO(FramebufferTarget::Read);
|
||||
ForceBindCurrentFBO(FramebufferTarget::Draw);
|
||||
}
|
||||
|
||||
Bool UpdateTextureBindingAtTarget(GLenum target) {
|
||||
#ifdef TRACY_ENABLE
|
||||
ZoneScopedNC(__func__, TRACY_ZONECOLOR_BACKEND);
|
||||
@@ -1558,6 +1751,40 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
g_GLESFuncs.glClearBufferuiv(buffer, drawbuffer, value);
|
||||
}
|
||||
|
||||
void ClearNamedFramebufferfv(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
|
||||
GLenum buffer, GLint drawbuffer, const GLfloat* value) {
|
||||
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG && MOBILEGL_ENABLE_SCOPE_MARKER
|
||||
DebugImpl::OpenGLScopeMarker marker(__func__);
|
||||
#endif
|
||||
TextureImpl::SyncNeccessaryTextures();
|
||||
RenderStateImpl::SyncRenderState();
|
||||
|
||||
SyncAndBindFramebufferObject(framebuffer, FramebufferTarget::Draw, true);
|
||||
g_GLESFuncs.glClearBufferfv(buffer, drawbuffer, value);
|
||||
DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
|
||||
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
|
||||
});
|
||||
|
||||
ForceBindCurrentFBO(FramebufferTarget::Draw);
|
||||
}
|
||||
|
||||
void ClearNamedFramebufferfi(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
|
||||
GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) {
|
||||
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG && MOBILEGL_ENABLE_SCOPE_MARKER
|
||||
DebugImpl::OpenGLScopeMarker marker(__func__);
|
||||
#endif
|
||||
TextureImpl::SyncNeccessaryTextures();
|
||||
RenderStateImpl::SyncRenderState();
|
||||
|
||||
SyncAndBindFramebufferObject(framebuffer, FramebufferTarget::Draw, true);
|
||||
g_GLESFuncs.glClearBufferfi(buffer, drawbuffer, depth, stencil);
|
||||
DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
|
||||
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
|
||||
});
|
||||
|
||||
ForceBindCurrentFBO(FramebufferTarget::Draw);
|
||||
}
|
||||
|
||||
class TempPixelStoreParameterSync {
|
||||
public:
|
||||
TempPixelStoreParameterSync(Bool isUnpack) : m_isUnpack(isUnpack) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
#include <Includes.h>
|
||||
#include <MG_State/GLState/FramebufferState/FramebufferObject.h>
|
||||
#include <MG_State/GLState/TextureState/TextureState.h>
|
||||
#include <MG_State/GLState/SamplerState/SamplerObject.h>
|
||||
#include <MG_Util/BackendLoaders/OpenGL/Loader.h>
|
||||
@@ -30,6 +31,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
|
||||
GLsizei drawcount, const GLint* basevertex);
|
||||
void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride);
|
||||
void MultiDrawElementsIndirectCount(GLenum mode, GLenum type, const void* indirect, GLintptr drawcount,
|
||||
GLsizei maxdrawcount, GLsizei stride);
|
||||
void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride);
|
||||
void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
|
||||
const void* indices, GLint basevertex);
|
||||
@@ -46,8 +49,17 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
GLuint baseinstance);
|
||||
void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount);
|
||||
void DrawArraysIndirect(GLenum mode, const void* indirect);
|
||||
void ClearNamedFramebufferfv(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
|
||||
GLenum buffer, GLint drawbuffer, const GLfloat* value);
|
||||
void ClearNamedFramebufferfi(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
|
||||
GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
|
||||
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1,
|
||||
GLint dstY1, GLbitfield mask, GLenum filter);
|
||||
void BlitNamedFramebuffer(const SharedPtr<MG_State::GLState::FramebufferObject>& readFramebuffer,
|
||||
const SharedPtr<MG_State::GLState::FramebufferObject>& drawFramebuffer,
|
||||
GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
|
||||
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
|
||||
GLbitfield mask, GLenum filter);
|
||||
void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width,
|
||||
GLsizei height, GLint border);
|
||||
void CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width,
|
||||
|
||||
@@ -23,9 +23,11 @@
|
||||
#include <MG_Util/Converters/GLToMG/FramebufferEnumConverter.h>
|
||||
#include <MG_Util/Converters/MGToGL/FramebufferEnumConverter.h>
|
||||
#include <MG_State/GLState/FramebufferState/FramebufferObject.h>
|
||||
#include <cctype>
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectGLES {
|
||||
constexpr Bool PREFER_MAP_BUFFER_RANGE_FOR_BUFFER_SYNC = true;
|
||||
constexpr const char* BASE_INSTANCE_UNIFORM_NAME = "mg_BaseInstance";
|
||||
|
||||
static Uint ResolveBackendEsslVersion() {
|
||||
const auto& version = g_GLESCapabilities.GLESVersion;
|
||||
@@ -38,6 +40,47 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
return 300;
|
||||
}
|
||||
|
||||
String ReplaceIdentifier(String source, const String& from, const String& to) {
|
||||
SizeT pos = 0;
|
||||
while ((pos = source.find(from, pos)) != String::npos) {
|
||||
const Bool leftIsIdent = pos > 0 &&
|
||||
(std::isalnum(static_cast<unsigned char>(source[pos - 1])) || source[pos - 1] == '_');
|
||||
const SizeT end = pos + from.size();
|
||||
const Bool rightIsIdent = end < source.size() &&
|
||||
(std::isalnum(static_cast<unsigned char>(source[end])) || source[end] == '_');
|
||||
if (!leftIsIdent && !rightIsIdent) {
|
||||
source.replace(pos, from.size(), to);
|
||||
pos += to.size();
|
||||
} else {
|
||||
pos = end;
|
||||
}
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
String InjectUniformAfterVersion(String source, const String& declaration) {
|
||||
const SizeT versionPos = source.find("#version");
|
||||
if (versionPos == String::npos) {
|
||||
return declaration + "\n" + source;
|
||||
}
|
||||
|
||||
const SizeT lineEnd = source.find('\n', versionPos);
|
||||
if (lineEnd == String::npos) {
|
||||
return source + "\n" + declaration + "\n";
|
||||
}
|
||||
source.insert(lineEnd + 1, declaration + "\n");
|
||||
return source;
|
||||
}
|
||||
|
||||
String EmulateBaseInstanceInVertexShader(String source, GLenum shaderType) {
|
||||
if (shaderType != GL_VERTEX_SHADER || source.find("gl_BaseInstance") == String::npos) {
|
||||
return source;
|
||||
}
|
||||
source = ReplaceIdentifier(std::move(source), "gl_BaseInstance", BASE_INSTANCE_UNIFORM_NAME);
|
||||
return InjectUniformAfterVersion(std::move(source),
|
||||
String("uniform highp int ") + BASE_INSTANCE_UNIFORM_NAME + ";");
|
||||
}
|
||||
|
||||
namespace BufferImpl {
|
||||
BackendBufferObject::BackendBufferObject() {
|
||||
#ifdef TRACY_ENABLE
|
||||
@@ -629,13 +672,15 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
static_cast<MG_State::GLState::TextureObjectBuffer*>(stateTextureObject.get());
|
||||
auto& slot = textureBufferObject->GetBufferBindingSlot();
|
||||
auto& buffer = slot.GetBoundObject();
|
||||
if (!buffer) {
|
||||
MGLOG_D("Texture buffer object with ID: %u has no bound buffer, skipping sync.",
|
||||
stateTextureObject->GetExternalIndex());
|
||||
return;
|
||||
}
|
||||
auto bufferIndex = buffer->GetExternalIndex();
|
||||
currentTextureInfo.bufferExternalIndex = bufferIndex;
|
||||
|
||||
Bool needsRegeneration = !m_isInitialized || (currentTextureInfo != m_prevTextureInfo);
|
||||
MGLOG_D("Texture state changed significantly or not initialized, regenerating texture (tex buffer) "
|
||||
"with ID: %u",
|
||||
m_backendTextureId);
|
||||
|
||||
// Need to sync texture buffer if not synced yet
|
||||
auto& backendBuffers = BufferImpl::g_backendBufferObjects;
|
||||
@@ -659,7 +704,19 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
TextureImpl::GenerateTextureFormatInfo(textureBufferObject->GetFormat(), &glInternalFormat, &glFormat,
|
||||
&glType);
|
||||
|
||||
g_GLESFuncs.glTexBuffer(GL_TEXTURE_BUFFER, glInternalFormat, backendId);
|
||||
if (needsRegeneration) {
|
||||
MGLOG_D("Texture state changed significantly or not initialized, regenerating texture buffer with "
|
||||
"ID: %u, buffer ID: %u, buffer size: %zu, format: %s",
|
||||
m_backendTextureId, backendId, buffer->GetSize(),
|
||||
MG_Util::ConvertGLEnumToString(glInternalFormat).c_str());
|
||||
g_GLESFuncs.glTexBuffer(GL_TEXTURE_BUFFER, glInternalFormat, backendId);
|
||||
DebugImpl::ErrorLopper::Loop(
|
||||
[file = __FILE__, line = __LINE__, func = __func__, glInternalFormat, backendId](GLenum err) {
|
||||
MGLOG_D("%s(%s:%d) glTexBuffer(format=%s, buffer=%u) ES error: %s",
|
||||
func, file, line, MG_Util::ConvertGLEnumToString(glInternalFormat).c_str(),
|
||||
backendId, MG_Util::ConvertGLEnumToString(err).c_str());
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -905,17 +962,37 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
g_GLESFuncs.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_backendFBOId);
|
||||
}
|
||||
|
||||
void BackendFramebufferObject::InvalidateSyncedState() {
|
||||
std::fill(std::begin(m_frontendDrawBuffers), std::end(m_frontendDrawBuffers),
|
||||
FramebufferAttachmentType::Unknown);
|
||||
std::fill(std::begin(m_backendDrawBuffers), std::end(m_backendDrawBuffers), GL_NONE);
|
||||
m_frontendReadBuffer = FramebufferAttachmentType::Unknown;
|
||||
m_backendReadBuffer = GL_NONE;
|
||||
std::fill(m_syncedFrontendAttachmentVersions.begin(), m_syncedFrontendAttachmentVersions.end(),
|
||||
static_cast<Uint16>(~0u));
|
||||
}
|
||||
|
||||
static Bool SyncAttachmentObject(GLenum glFBOTarget,
|
||||
const MG_State::GLState::FramebufferAttachmentObject& attachmentObject,
|
||||
GLenum glBackendAttachment) {
|
||||
if (attachmentObject.IsTexture()) {
|
||||
const auto& textureObject = attachmentObject.GetTexture();
|
||||
SharedPtr<TextureImpl::BackendTextureObject> backendTextureObject;
|
||||
const auto& backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject.get());
|
||||
if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) {
|
||||
auto& backendTextureSlot = TextureImpl::g_backendTextureObjects.GetOrCreate(textureObject);
|
||||
if (!backendTextureSlot) {
|
||||
backendTextureSlot = MakeShared<TextureImpl::BackendTextureObject>();
|
||||
}
|
||||
backendTextureObject = backendTextureSlot;
|
||||
} else {
|
||||
backendTextureObject = backendTextureIt->second;
|
||||
}
|
||||
if (!backendTextureObject) {
|
||||
MGLOG_E("%s: No backend texture found for FBO attachment, cannot bind texture.", __func__);
|
||||
return false;
|
||||
}
|
||||
const auto& backendTextureObject = backendTextureIt->second;
|
||||
backendTextureObject->SyncMipmapsToBackend(textureObject);
|
||||
auto glTextureTarget = MG_Util::ConvertTextureTargetToGLEnum(textureObject->GetTarget());
|
||||
backendTextureObject->Bind(glTextureTarget);
|
||||
g_GLESFuncs.glFramebufferTexture2D(glFBOTarget, glBackendAttachment, glTextureTarget,
|
||||
@@ -1023,8 +1100,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
|
||||
// relevant FRONTEND!!! version should be checked and updated
|
||||
if (m_syncedFrontendAttachmentVersions[i] != attachmentVersions[i]) {
|
||||
SyncAttachmentObject(glFBOTarget, attachmentObject, glBackendAttachment);
|
||||
m_syncedFrontendAttachmentVersions[i] = attachmentVersions[i];
|
||||
if (SyncAttachmentObject(glFBOTarget, attachmentObject, glBackendAttachment)) {
|
||||
m_syncedFrontendAttachmentVersions[i] = attachmentVersions[i];
|
||||
}
|
||||
}
|
||||
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG
|
||||
else {
|
||||
@@ -1223,6 +1301,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
source = RemoveLayoutBinding(source);
|
||||
source = ProcessOutColorLocations(source);
|
||||
source = ForceFlatIntegerVaryings(source, glShaderType);
|
||||
source = EmulateBaseInstanceInVertexShader(std::move(source), glShaderType);
|
||||
source = ForceSupporterOutput(source);
|
||||
|
||||
// Patch for Photon compiler precision issue
|
||||
@@ -1273,6 +1352,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
} else {
|
||||
MGLOG_D("Program linked successfully. ID: %u", m_backendProgramId);
|
||||
}
|
||||
m_baseInstanceUniformLocation = g_GLESFuncs.glGetUniformLocation(m_backendProgramId,
|
||||
BASE_INSTANCE_UNIFORM_NAME);
|
||||
|
||||
// Create global UBO
|
||||
if (stateProgramObject->GetUBOSize() > 0) {
|
||||
@@ -1295,6 +1376,13 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
MGLOG_D("Using program %u", m_backendProgramId);
|
||||
g_GLESFuncs.glUseProgram(m_backendProgramId);
|
||||
}
|
||||
|
||||
void BackendProgramObjectImpl::SetBaseInstance(Uint32 baseInstance) const {
|
||||
if (m_baseInstanceUniformLocation < 0) {
|
||||
return;
|
||||
}
|
||||
g_GLESFuncs.glUniform1i(m_baseInstanceUniformLocation, static_cast<GLint>(baseInstance));
|
||||
}
|
||||
} // namespace PrgramImpl
|
||||
|
||||
namespace SamplerImpl {
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#include <MG_State/GLState/Core.h>
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectGLES {
|
||||
String EmulateBaseInstanceInVertexShader(String source, GLenum shaderType);
|
||||
|
||||
template <typename StateObject, typename BackendObject>
|
||||
class StateBackendObjectRegistry {
|
||||
public:
|
||||
@@ -226,6 +228,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
BackendFramebufferObject();
|
||||
void SyncToBackend(const SharedPtr<MG_State::GLState::FramebufferObject>& stateFBOObject,
|
||||
FramebufferTarget asTarget);
|
||||
void InvalidateSyncedState();
|
||||
Uint GetBackendFramebufferId() const { return m_backendFBOId; }
|
||||
void Bind(FramebufferTarget target) const;
|
||||
// FramebufferAttachmentType GetCompactedAttachmentTypeAtDrawBufferIndex(Int index);
|
||||
@@ -267,12 +270,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
~BackendProgramObjectImpl();
|
||||
void SyncToBackend(const SharedPtr<MG_State::GLState::ProgramObject>& stateProgramObject);
|
||||
void Use() const;
|
||||
void SetBaseInstance(Uint32 baseInstance) const;
|
||||
Uint GetBackendProgramId() const { return m_backendProgramId; }
|
||||
Uint GetBackendGlobalUBOId() const { return m_backendGlobalUBOId; }
|
||||
|
||||
private:
|
||||
Uint m_backendProgramId = 0;
|
||||
Uint m_backendGlobalUBOId = 0;
|
||||
Int m_baseInstanceUniformLocation = -1;
|
||||
Bool m_isInitialized = false;
|
||||
};
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <string>
|
||||
|
||||
#include <MG_Backend/DirectGLES/BackendObject_DirectGLES.h>
|
||||
#include <MG_Backend/DirectGLES/Managers.h>
|
||||
#include <MG_Backend/DirectVulkan/BackendObject_DirectVulkan.h>
|
||||
#include <MG_Backend/BackendObjects.h>
|
||||
#include <MG_Impl/GLImpl/Getter/GL_Getter.h>
|
||||
@@ -90,6 +91,71 @@ TEST(DirectGLESSanity, AdvertisesDepthTextureForGlmarkShadowScenes) {
|
||||
EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_depth_texture), extensions.end());
|
||||
}
|
||||
|
||||
TEST(DirectGLESSanity, AdvertisesVoxyRequiredRenderingExtensionsWithoutRaisingGLVersion) {
|
||||
MobileGL::MG_Backend::DirectGLES::BackendObject_DirectGLES backend;
|
||||
const auto& rendererInfo = backend.GetRendererInfo().RendererGLInfo;
|
||||
const auto& extensions = rendererInfo.Extensions;
|
||||
|
||||
EXPECT_EQ(rendererInfo.TargetGLVersion.Major, 3);
|
||||
EXPECT_EQ(rendererInfo.TargetGLVersion.Minor, 3);
|
||||
EXPECT_EQ(rendererInfo.TargetGLVersion.Patch, 0);
|
||||
|
||||
EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_compute_shader),
|
||||
extensions.end());
|
||||
EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_shader_storage_buffer_object),
|
||||
extensions.end());
|
||||
EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_texture_storage),
|
||||
extensions.end());
|
||||
EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_direct_state_access),
|
||||
extensions.end());
|
||||
EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_multi_draw_indirect),
|
||||
extensions.end());
|
||||
EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_indirect_parameters),
|
||||
extensions.end());
|
||||
EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_shader_draw_parameters),
|
||||
extensions.end());
|
||||
EXPECT_EQ(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_gpu_shader_int64),
|
||||
extensions.end());
|
||||
EXPECT_EQ(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_KHR_shader_subgroup),
|
||||
extensions.end());
|
||||
}
|
||||
|
||||
TEST(DirectGLESSanity, ProvidesNamedFramebufferBlitForDirectStateAccess) {
|
||||
MobileGL::MG_Backend::DirectGLES::BackendObject_DirectGLES backend;
|
||||
const auto& funcs = backend.GetBackendFunctions().GL;
|
||||
|
||||
EXPECT_NE(funcs.ClearNamedFramebufferfv, nullptr);
|
||||
EXPECT_NE(funcs.ClearNamedFramebufferfi, nullptr);
|
||||
EXPECT_NE(funcs.BlitFramebuffer, nullptr);
|
||||
EXPECT_NE(funcs.BlitNamedFramebuffer, nullptr);
|
||||
}
|
||||
|
||||
TEST(DirectGLESSanity, RewritesBaseInstanceBuiltinForEsslVertexShaders) {
|
||||
const MobileGL::String source = R"(#version 320 es
|
||||
void main() {
|
||||
uint drawId = gl_BaseInstance;
|
||||
uint untouched = my_gl_BaseInstance_value;
|
||||
}
|
||||
)";
|
||||
|
||||
const auto rewritten = MobileGL::MG_Backend::DirectGLES::EmulateBaseInstanceInVertexShader(
|
||||
source, GL_VERTEX_SHADER);
|
||||
|
||||
EXPECT_NE(rewritten.find("uniform highp int mg_BaseInstance;"), MobileGL::String::npos);
|
||||
EXPECT_NE(rewritten.find("uint drawId = mg_BaseInstance;"), MobileGL::String::npos);
|
||||
EXPECT_NE(rewritten.find("my_gl_BaseInstance_value"), MobileGL::String::npos);
|
||||
EXPECT_EQ(rewritten.find("uint drawId = gl_BaseInstance;"), MobileGL::String::npos);
|
||||
}
|
||||
|
||||
TEST(DirectGLESSanity, LeavesBaseInstanceBuiltinAloneOutsideVertexShaders) {
|
||||
const MobileGL::String source = "#version 320 es\nuint value = gl_BaseInstance;\n";
|
||||
|
||||
const auto rewritten = MobileGL::MG_Backend::DirectGLES::EmulateBaseInstanceInVertexShader(
|
||||
source, GL_FRAGMENT_SHADER);
|
||||
|
||||
EXPECT_EQ(rewritten, source);
|
||||
}
|
||||
|
||||
TEST(DirectVulkanSanity, AdvertisesTextureStorageForDirectStateAccess) {
|
||||
MobileGL::MG_Backend::DirectVulkan::BackendObject_DirectVulkan backend;
|
||||
const auto& extensions = backend.GetRendererInfo().RendererGLInfo.Extensions;
|
||||
|
||||
Reference in New Issue
Block a user