[Fix] (MG_Backend/DirectVulkan): make glmark2 work on Magma

This commit is contained in:
2026-06-07 16:24:30 +08:00
parent ad1ca4ea92
commit b0de886f8e
15 changed files with 157 additions and 34 deletions
+22
View File
@@ -110,10 +110,32 @@
#define VK_USE_PLATFORM_WIN32_KHR
#elif defined(__APPLE__)
#define VK_USE_PLATFORM_METAL_EXT
#elif defined(__linux__)
#define VK_USE_PLATFORM_XLIB_KHR
typedef struct _XDisplay Display;
typedef unsigned long XID;
typedef XID Window;
typedef unsigned long VisualID;
#else
#warning "VK_USE_PLATFORM_*_KHR not defined for this platform!"
#endif
#if defined(VK_USE_PLATFORM_XLIB_KHR)
#pragma push_macro("Bool")
#pragma push_macro("None")
#pragma push_macro("Always")
#pragma push_macro("Status")
#pragma push_macro("LSBFirst")
#pragma push_macro("DestroyAll")
#endif
#include <vulkan/vulkan.h>
#if defined(VK_USE_PLATFORM_XLIB_KHR)
#pragma pop_macro("DestroyAll")
#pragma pop_macro("LSBFirst")
#pragma pop_macro("Status")
#pragma pop_macro("Always")
#pragma pop_macro("None")
#pragma pop_macro("Bool")
#endif
#ifdef TRACY_ENABLE
#include <tracy/Tracy.hpp>
+8
View File
@@ -164,6 +164,14 @@ namespace MobileGL::MG_Backend {
return true;
}
void BackendObject::ReleaseEGLResources() {
const std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
ResetEGLRuntimeState();
m_eglDisplay = EGL_NO_DISPLAY;
m_eglDisplayInitialized = false;
m_windowHandle = {};
}
void BackendObject::SetWindowHandle(const WindowHandle& handle) {
const std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
m_windowHandle = handle;
+1
View File
@@ -118,6 +118,7 @@ namespace MobileGL {
virtual Bool CreateEGLPbufferSurface(EGLint width, EGLint height);
virtual Bool MakeEGLCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
virtual Bool SwapEGLBuffers(EGLDisplay dpy, EGLSurface draw);
virtual void ReleaseEGLResources();
void SetWindowHandle(const WindowHandle& handle);
@@ -113,6 +113,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
return BackendObject::SwapEGLBuffers(dpy, draw);
}
void BackendObject_DirectGLES::ReleaseEGLResources() {
const std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
DestroyEGLContext();
BackendObject::ReleaseEGLResources();
}
const RendererInfo& BackendObject_DirectGLES::GetRendererInfo() const {
static RendererInfo RendererInfo = {
.RendererName = "Espryt", // Renderer Name
@@ -23,6 +23,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
Bool CreateEGLWindowSurface(const WindowHandle& handle) override;
Bool MakeEGLCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) override;
Bool SwapEGLBuffers(EGLDisplay dpy, EGLSurface draw) override;
void ReleaseEGLResources() override;
const RendererInfo& GetRendererInfo() const override;
String GetBackendAPIVersionString() const override;
@@ -1943,7 +1943,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
using XOpenDisplayFn = Display* (*)(const char*);
using XGetWindowAttributesFn = Status (*)(Display*, Window, XWindowAttributes*);
using XGetWindowAttributesFn = int (*)(Display*, Window, XWindowAttributes*);
using XVisualIDFromVisualFn = unsigned long (*)(Visual*);
using XCloseDisplayFn = int (*)(Display*);
@@ -66,8 +66,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
MGLOG_E("DirectVulkan backend not initialized");
return false;
}
if (handle.Backend != WindowBackend::Android || !handle.Handle) {
MGLOG_E("DirectVulkan backend only supports Android native windows");
if (!handle.Handle || (handle.Backend != WindowBackend::Android && handle.Backend != WindowBackend::X11)) {
MGLOG_E("DirectVulkan backend only supports Android and X11 native windows");
return false;
}
@@ -106,6 +106,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return BackendObject::SwapEGLBuffers(dpy, draw);
}
void BackendObject_DirectVulkan::ReleaseEGLResources() {
const std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
pVulkanRenderer.reset();
BackendObject::ReleaseEGLResources();
}
const RendererInfo& BackendObject_DirectVulkan::GetRendererInfo() const {
static RendererInfo RendererInfo = {
.RendererName = "Magma", // Renderer Name
@@ -118,7 +124,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
.Extensions = {V_OpenGL30, V_OpenGL31, V_OpenGL32, // OpenGL Extensions
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_program_interface_query, E_GL_ARB_framebuffer_object,
E_GL_EXT_framebuffer_object, E_GL_ARB_depth_texture},
.IsCompatibilityProfile = false // Is Compatibility Profile
},
.StaticBackendCapability = {.AllowVSOnlyPrograms = false} // Backend Capability
@@ -23,6 +23,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Bool CreateEGLWindowSurface(const WindowHandle& handle) override;
Bool MakeEGLCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) override;
Bool SwapEGLBuffers(EGLDisplay dpy, EGLSurface draw) override;
void ReleaseEGLResources() override;
const RendererInfo& GetRendererInfo() const override;
String GetBackendAPIVersionString() const override;
@@ -52,10 +52,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
VertexInputStateBuilder builder;
UnorderedMap<SizeT, Uint32> bindingByBufferKey;
UnorderedMap<SizeT, Uint32> strideByBufferKey;
UnorderedMap<SizeT, VkVertexInputRate> inputRateByBufferKey;
Vector<SizeT> bindingBufferKeys;
Vector<SizeT> bindingBaseOffsets;
for (Uint32 location = 0; location < MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS; ++location) {
const auto& attr = vao.GetAttribute(location);
@@ -84,29 +82,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
(attr.Divisor == 0) ? VK_VERTEX_INPUT_RATE_VERTEX : VK_VERTEX_INPUT_RATE_INSTANCE;
const SizeT bufferKey = reinterpret_cast<SizeT>(attr.Buffer.get());
Uint32 binding = 0;
auto itBinding = bindingByBufferKey.find(bufferKey);
if (itBinding == bindingByBufferKey.end()) {
binding = static_cast<Uint32>(bindingByBufferKey.size());
bindingByBufferKey.emplace(bufferKey, binding);
strideByBufferKey.emplace(bufferKey, stride);
inputRateByBufferKey.emplace(bufferKey, inputRate);
bindingBufferKeys.push_back(bufferKey);
builder.AddBinding(binding, stride, inputRate);
} else {
binding = itBinding->second;
if (strideByBufferKey[bufferKey] != stride) {
MGLOG_D("Skipping vertex attribute at location %u: stride mismatch (%u vs %u) on same buffer",
location, stride, strideByBufferKey[bufferKey]);
continue;
}
if (inputRateByBufferKey[bufferKey] != inputRate) {
MGLOG_D("Skipping vertex attribute at location %u: input-rate mismatch on same buffer", location);
continue;
}
}
builder.AddAttribute(location, binding, vkFormat, static_cast<Uint32>(attr.Offset));
const Uint32 binding = static_cast<Uint32>(bindingBufferKeys.size());
bindingBufferKeys.push_back(bufferKey);
bindingBaseOffsets.push_back(attr.Offset);
builder.AddBinding(binding, stride, inputRate);
builder.AddAttribute(location, binding, vkFormat, 0);
}
const auto& state = builder.Build();
@@ -116,6 +96,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
entry.bindings = builder.GetBindings();
entry.attributes = builder.GetAttributes();
entry.bindingBufferKeys = std::move(bindingBufferKeys);
entry.bindingBaseOffsets = std::move(bindingBaseOffsets);
entry.state = state;
entry.state.pVertexBindingDescriptions = entry.bindings.empty() ? nullptr : entry.bindings.data();
entry.state.pVertexAttributeDescriptions = entry.attributes.empty() ? nullptr : entry.attributes.data();
@@ -24,6 +24,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Vector<VkVertexInputBindingDescription> bindings;
Vector<VkVertexInputAttributeDescription> attributes;
Vector<SizeT> bindingBufferKeys;
Vector<SizeT> bindingBaseOffsets;
VkPipelineVertexInputStateCreateInfo state{
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO
};
@@ -1331,6 +1331,22 @@ void main() {
m_surface = VK_NULL_HANDLE;
}
#if defined(VK_USE_PLATFORM_XLIB_KHR)
if (m_platformDisplay != nullptr) {
using XCloseDisplayFn = int (*)(Display*);
auto* closeDisplay = reinterpret_cast<XCloseDisplayFn>(m_platformCloseDisplay);
if (closeDisplay) {
closeDisplay(static_cast<Display*>(m_platformDisplay));
}
m_platformDisplay = nullptr;
}
m_platformCloseDisplay = nullptr;
if (m_platformLibrary != nullptr) {
dlclose(m_platformLibrary);
m_platformLibrary = nullptr;
}
#endif
if (m_debugMessenger != VK_NULL_HANDLE) {
DestroyDebugMessenger();
m_debugMessenger = VK_NULL_HANDLE;
@@ -1413,7 +1429,12 @@ void main() {
}
}
vkBuffers[binding] = slice.buffer;
vkOffsets[binding] = slice.offset;
const SizeT baseOffset =
binding < vertexInputState.bindingBaseOffsets.size() ? vertexInputState.bindingBaseOffsets[binding] : 0;
MOBILEGL_ASSERT(baseOffset <= sourceSize,
"UploadAndBindVertexStreams skipped: binding %zu base offset %zu exceeds buffer size %zu",
binding, baseOffset, sourceSize);
vkOffsets[binding] = slice.offset + static_cast<VkDeviceSize>(baseOffset);
}
SizeT syntheticBinding = vertexInputState.bindings.size();
@@ -3515,8 +3536,15 @@ void main() {
resource = m_textureManager->SyncTextureAndGetDescriptor(*texture);
MOBILEGL_ASSERT(resource != nullptr && resource->image != VK_NULL_HANDLE,
"GenerateMipmap failed to resync the backend texture after allocating mip storage.");
MOBILEGL_ASSERT(resource->layout != VK_IMAGE_LAYOUT_UNDEFINED,
"GenerateMipmap requires initialized base-level image data.");
if (resource->layout == VK_IMAGE_LAYOUT_UNDEFINED) {
const VkImageLayout finalLayout = ResolveGenerateMipmapFinalLayout(resource->aspect);
Bool transitioned = VkTextureManager::TransitionImageLayout(
frame.commandBuffer, resource->image, resource->layout, finalLayout,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0, VK_ACCESS_SHADER_READ_BIT, resource->aspect, 0, resource->mipLevels, resource->arrayLayers);
MOBILEGL_ASSERT(transitioned, "GenerateMipmap: failed to transition uninitialized mip chain");
return;
}
const IntVec3 storageBaseTexelSize = {
static_cast<Int>(resource->extent.width),
@@ -3801,6 +3829,8 @@ void main() {
VK_KHR_WIN32_SURFACE_EXTENSION_NAME
#elif defined VK_USE_PLATFORM_METAL_EXT
VK_EXT_METAL_SURFACE_EXTENSION_NAME
#elif defined VK_USE_PLATFORM_XLIB_KHR
VK_KHR_XLIB_SURFACE_EXTENSION_NAME
#else
#warning "VulkanContext::CreateInstance: VK_KHR_*_surface extension not defined on this platform"
#endif
@@ -4195,6 +4225,31 @@ void main() {
VkMetalSurfaceCreateInfoEXT sci{VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT};
sci.pLayer = reinterpret_cast<const void*>(m_window);
VK_VERIFY(vkCreateMetalSurfaceEXT(m_instance, &sci, nullptr, &m_surface), "vkCreateMetalSurfaceEXT failed");
#elif defined VK_USE_PLATFORM_XLIB_KHR
MOBILEGL_ASSERT(m_window, "X11 Window is null");
void* x11Lib = dlopen("libX11.so.6", RTLD_LOCAL | RTLD_NOW);
if (!x11Lib) {
x11Lib = dlopen("libX11.so", RTLD_LOCAL | RTLD_NOW);
}
MOBILEGL_ASSERT(x11Lib != nullptr, "Failed to open libX11 while creating Vulkan Xlib surface");
using XOpenDisplayFn = Display* (*)(const char*);
using XCloseDisplayFn = int (*)(Display*);
auto* xOpenDisplay = reinterpret_cast<XOpenDisplayFn>(dlsym(x11Lib, "XOpenDisplay"));
auto* xCloseDisplay = reinterpret_cast<XCloseDisplayFn>(dlsym(x11Lib, "XCloseDisplay"));
MOBILEGL_ASSERT(xOpenDisplay != nullptr && xCloseDisplay != nullptr,
"Failed to resolve XOpenDisplay/XCloseDisplay while creating Vulkan Xlib surface");
auto* display = xOpenDisplay(std::getenv("DISPLAY"));
MOBILEGL_ASSERT(display != nullptr, "XOpenDisplay failed while creating Vulkan Xlib surface");
m_platformDisplay = display;
m_platformLibrary = x11Lib;
m_platformCloseDisplay = reinterpret_cast<void*>(xCloseDisplay);
VkXlibSurfaceCreateInfoKHR sci{VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR};
sci.dpy = display;
sci.window = static_cast<Window>(m_window);
VK_VERIFY(vkCreateXlibSurfaceKHR(m_instance, &sci, nullptr, &m_surface), "vkCreateXlibSurfaceKHR failed");
#else
// #warning "VulkanRenderer::Initialize called on a platform which is not supported yet"
MGLOG_W("VulkanRenderer::Initialize called on a platform which is not supported yet"); // TODO: support more
@@ -174,6 +174,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void QueueClearBufferPayload(GLenum buffer, GLint drawbuffer, const ClearAttachmentPayload& clearPayload);
NativeWindowType m_window = 0;
void* m_platformDisplay = nullptr;
void* m_platformLibrary = nullptr;
void* m_platformCloseDisplay = nullptr;
VulkanRendererConfig m_config;
// Vulkan objects
+7 -1
View File
@@ -244,7 +244,13 @@ namespace MobileGL::MG_Impl::EGLImpl {
if (!state) {
return EGL_FALSE;
}
return state->TerminateDisplay(dpy) ? EGL_TRUE : EGL_FALSE;
if (!state->TerminateDisplay(dpy)) {
return EGL_FALSE;
}
if (auto* backendObject = MG_Backend::pActiveBackendObject.get()) {
backendObject->ReleaseEGLResources();
}
return EGL_TRUE;
}
EGLBoolean ReleaseThread() {
@@ -29,6 +29,7 @@
namespace MobileGL::MG_Impl::GLImpl {
static SharedPtr<MG_State::GLState::ITextureObject> nullTextureObject;
static UnorderedMap<Uint, Bool> g_autoGenerateMipmapByTextureId;
const SharedPtr<MG_State::GLState::ITextureObject>& GetTextureObjectByTarget(
TextureUploadTarget textureUploadTarget, TextureTarget textureTarget) {
@@ -50,6 +51,18 @@ namespace MobileGL::MG_Impl::GLImpl {
MG_Backend::gBackendFunctionsTable.GL.GenerateMipmap(target);
}
void MaybeAutoGenerateMipmap(GLenum target, const SharedPtr<MG_State::GLState::ITextureObject>& textureObject,
Bool isProxy, GLint level) {
if (isProxy || level != 0 || !textureObject) {
return;
}
const auto it = g_autoGenerateMipmapByTextureId.find(textureObject->GetExternalIndex());
if (it == g_autoGenerateMipmapByTextureId.end() || !it->second) {
return;
}
GenerateMipmap_Backend(target);
}
void TexSubImage3D_State(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width,
GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels) {
// TODO: implement
@@ -169,6 +182,7 @@ namespace MobileGL::MG_Impl::GLImpl {
MGLOG_D("%s: mark mip %d as dirty", __func__, level);
textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, true);
MaybeAutoGenerateMipmap(target, textureObject, false, level);
}
void TexSubImage1D_State(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type,
@@ -240,6 +254,9 @@ namespace MobileGL::MG_Impl::GLImpl {
case GL_TEXTURE_LOD_BIAS:
textureObject->GetSamplerObject()->SetLodBias(param);
break;
case GL_GENERATE_MIPMAP:
g_autoGenerateMipmapByTextureId[textureObject->GetExternalIndex()] = (param != 0.0f);
break;
case GL_TEXTURE_SWIZZLE_RGBA:
// Not supported in this function
case GL_TEXTURE_BORDER_COLOR:
@@ -314,6 +331,9 @@ namespace MobileGL::MG_Impl::GLImpl {
case GL_TEXTURE_LOD_BIAS:
textureObject->GetSamplerObject()->SetLodBias((GLfloat)param);
break;
case GL_GENERATE_MIPMAP:
g_autoGenerateMipmapByTextureId[textureObject->GetExternalIndex()] = (param != GL_FALSE);
break;
case GL_TEXTURE_SWIZZLE_RGBA:
// Not supported in this function
case GL_TEXTURE_BORDER_COLOR:
@@ -546,6 +566,10 @@ namespace MobileGL::MG_Impl::GLImpl {
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
// ======================= Processing ================================
if (internalformat == GL_ALPHA || format == GL_ALPHA) {
textureObject->SetSwizzleParamRGBA({TextureSwizzleParam::Zero, TextureSwizzleParam::Zero,
TextureSwizzleParam::Zero, TextureSwizzleParam::Red});
}
SizeT imageSize = 0;
const SizeT inputBpp = MG_Util::GetInputBytesPerPixel(textureInputFormat, texturePixelDataType);
@@ -655,6 +679,10 @@ namespace MobileGL::MG_Impl::GLImpl {
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
// ======================= Processing ================================
if (internalformat == GL_ALPHA || format == GL_ALPHA) {
textureObject->SetSwizzleParamRGBA({TextureSwizzleParam::Zero, TextureSwizzleParam::Zero,
TextureSwizzleParam::Zero, TextureSwizzleParam::Red});
}
SizeT imageSize = 0;
const SizeT inputBpp = MG_Util::GetInputBytesPerPixel(textureInputFormat, texturePixelDataType);
@@ -719,6 +747,7 @@ namespace MobileGL::MG_Impl::GLImpl {
MGLOG_D("%s: mark mip %d as dirty", __func__, level);
textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, true);
MaybeAutoGenerateMipmap(target, textureObject, isProxy, level);
}
void TexImage1D_State(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLint border, GLenum format,
@@ -48,6 +48,7 @@ namespace MobileGL {
TextureInputFormat ConvertGLEnumToTextureInputFormat(GLenum format) {
switch (format) {
case GL_ALPHA:
case GL_RED:
return TextureInputFormat::Red;
case GL_RG:
@@ -225,6 +226,7 @@ namespace MobileGL {
return TextureInternalFormat::DepthComponent;
case GL_DEPTH_STENCIL:
return TextureInternalFormat::DepthStencil;
case GL_ALPHA:
case GL_RED:
return TextureInternalFormat::Red;
case GL_RG: