mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 05:38:31 +09:00
Compare commits
14
Commits
dd745d7547
...
8025745fa3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8025745fa3 | ||
|
|
4a533a215a | ||
|
|
0a5d7ceb6c | ||
|
|
ac81185968 | ||
|
|
9e861f3f7a | ||
|
|
da6f75dbd1 | ||
|
|
951da362f7 | ||
|
|
4c929b9b3f | ||
|
|
8d5072543a | ||
|
|
5de2b9e3e9 | ||
|
|
9192d156d1 | ||
|
|
27cdfbc0ca | ||
|
|
4567c3b468 | ||
|
|
d18c6a1bae |
+1
-1
@@ -14,7 +14,7 @@ namespace MobileGL::MG_Config {
|
||||
inline const String ProjectName = "MobileGL";
|
||||
inline const String CoreName = "MobileGL Core";
|
||||
inline const String CoreVendor = "MobileGL-Dev (BZLZHH, Swung0x48, Tungsten)";
|
||||
inline const Version CoreVersion = {26, 7, 0, "-dev", VersionType::Development};
|
||||
inline const Version CoreVersion = {26, 8, 0, "-dev", VersionType::Development};
|
||||
inline const VersionStringFormatAttrib DefaultVersionStringFormatAttrib = {2, 2, 0, true, true};
|
||||
inline const Uint64 CacheVersion = 0;
|
||||
|
||||
|
||||
@@ -935,11 +935,16 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
funcsTable.GL.BeginTimeElapsedQuery = BeginTimeElapsedQuery;
|
||||
funcsTable.GL.EndTimeElapsedQuery = EndTimeElapsedQuery;
|
||||
funcsTable.GL.QueryCounterTimestamp = QueryCounterTimestamp;
|
||||
funcsTable.GL.IsQueryResultAvailable = IsQueryResultAvailable;
|
||||
funcsTable.GL.GetQueryResult64 = GetQueryResult64;
|
||||
funcsTable.GL.DeleteBackendQuery = DeleteBackendQuery;
|
||||
funcsTable.GL.GetGpuTimestampNs = GetGpuTimestampNs;
|
||||
}
|
||||
// Occlusion queries are core ES3 (independent of MOBILEGL_DISABLE_TIMERQUERY)
|
||||
// and share the handle-based result/delete entries, which must exist even
|
||||
// when the timer-query group above is disabled.
|
||||
funcsTable.GL.BeginOcclusionQuery = BeginOcclusionQuery;
|
||||
funcsTable.GL.EndOcclusionQuery = EndOcclusionQuery;
|
||||
funcsTable.GL.IsQueryResultAvailable = IsQueryResultAvailable;
|
||||
funcsTable.GL.GetQueryResult64 = GetQueryResult64;
|
||||
funcsTable.GL.DeleteBackendQuery = DeleteBackendQuery;
|
||||
funcsTableInitialized = true;
|
||||
}
|
||||
return funcsTable;
|
||||
|
||||
@@ -3632,12 +3632,16 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
|
||||
static Bool IsLegacyNativeReadPixelsFormat(GLenum format) {
|
||||
return format == GL_RGBA || format == GL_RGBA_INTEGER || format == GL_RED || format == GL_RED_INTEGER ||
|
||||
format == GL_DEPTH_COMPONENT || format == GL_STENCIL_INDEX;
|
||||
format == GL_DEPTH_COMPONENT || format == GL_STENCIL_INDEX || format == GL_DEPTH_STENCIL;
|
||||
}
|
||||
|
||||
static Bool IsLegacyNativeReadPixelsType(GLenum type) {
|
||||
// GL_UNSIGNED_INT_24_8 / GL_FLOAT_32_UNSIGNED_INT_24_8_REV are only ever valid
|
||||
// paired with GL_DEPTH_STENCIL (packed_depth_stencil.verify_read_pixels); the real
|
||||
// driver already implements this readback natively.
|
||||
return type == GL_UNSIGNED_BYTE || type == GL_UNSIGNED_INT || type == GL_UNSIGNED_INT_2_10_10_10_REV ||
|
||||
type == GL_INT || type == GL_FLOAT;
|
||||
type == GL_INT || type == GL_FLOAT || type == GL_UNSIGNED_INT_24_8 ||
|
||||
type == GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
|
||||
}
|
||||
|
||||
void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) {
|
||||
@@ -3782,6 +3786,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
if (format == GL_RGBA_INTEGER) {
|
||||
return type == GL_INT || type == GL_UNSIGNED_INT || type == GL_UNSIGNED_INT_2_10_10_10_REV;
|
||||
}
|
||||
if (format == GL_DEPTH_STENCIL) {
|
||||
return type == GL_UNSIGNED_INT_24_8 || type == GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3850,7 +3857,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
MGLOG_D("GetTexImage: attaching level %d to the scratch FBO", level);
|
||||
const GLenum backendAttachTarget = TextureImpl::ConvertTextureUploadTargetToBackendGLEnum(
|
||||
MG_Util::ConvertGLEnumToTextureUploadTarget(target));
|
||||
if (backendAttachTarget == GL_TEXTURE_3D || backendAttachTarget == GL_TEXTURE_2D_ARRAY) {
|
||||
// GL_DEPTH_STENCIL can't be attached as a color attachment (glCheckFramebufferStatus
|
||||
// would report it incomplete); it has its own combined depth+stencil attachment point.
|
||||
// glReadBuffer only selects among color attachments, so it does not apply here.
|
||||
if (format == GL_DEPTH_STENCIL) {
|
||||
ScratchFBOImpl::EnsureDepthAttachment2D(
|
||||
tempFB, GL_READ_FRAMEBUFFER, backendTexId,
|
||||
backendAttachTarget == GL_UNKNOWN_MGL ? target : backendAttachTarget, level, /*withStencil=*/true);
|
||||
} else if (backendAttachTarget == GL_TEXTURE_3D || backendAttachTarget == GL_TEXTURE_2D_ARRAY) {
|
||||
// ES cannot attach 3D/array textures through glFramebufferTexture2D; read layer 0. Reads
|
||||
// of deeper slices are served from the CPU shadow instead (see the shadow-first branch).
|
||||
ScratchFBOImpl::EnsureColorAttachmentLayer(tempFB, GL_READ_FRAMEBUFFER, backendTexId, level, 0);
|
||||
@@ -3859,8 +3873,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
tempFB, GL_READ_FRAMEBUFFER, backendTexId,
|
||||
backendAttachTarget == GL_UNKNOWN_MGL ? target : backendAttachTarget, level);
|
||||
}
|
||||
MGLOG_D("GetTexImage: glReadBuffer(GL_COLOR_ATTACHMENT0)");
|
||||
ScratchFBOImpl::EnsureReadBuffer(tempFB, GL_COLOR_ATTACHMENT0);
|
||||
if (format != GL_DEPTH_STENCIL) {
|
||||
MGLOG_D("GetTexImage: glReadBuffer(GL_COLOR_ATTACHMENT0)");
|
||||
ScratchFBOImpl::EnsureReadBuffer(tempFB, GL_COLOR_ATTACHMENT0);
|
||||
}
|
||||
|
||||
GLenum fbStatus = g_GLESFuncs.glCheckFramebufferStatus(GL_READ_FRAMEBUFFER);
|
||||
MGLOG_D("GetTexImage: GL_READ_FRAMEBUFFER status = %s", MG_Util::ConvertGLEnumToString(fbStatus).c_str());
|
||||
@@ -4344,6 +4360,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
struct GLESQueryObject {
|
||||
GLuint queryId = 0;
|
||||
Uint contextGeneration = 0;
|
||||
// GL_ANY_SAMPLES_PASSED result is 0/1 and only ever reachable through the
|
||||
// core (non-extension) glGetQueryObjectuiv getter - GL_EXT_disjoint_timer_query's
|
||||
// 64-bit glGetQueryObjectui64vEXT is timer-specific and may be entirely absent
|
||||
// on drivers that otherwise fully support core ES3 occlusion queries.
|
||||
Bool isOcclusion = false;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4536,6 +4557,38 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
return new GLESQueryObject{queryId, g_syncContextGeneration};
|
||||
}
|
||||
|
||||
// GL_SAMPLES_PASSED/GL_ANY_SAMPLES_PASSED(_CONSERVATIVE) occlusion queries. Unlike the
|
||||
// timer queries above, these are core ES 3.0 (no GL_EXT_disjoint_timer_query needed).
|
||||
Bool AreOcclusionQueriesSupported() {
|
||||
return g_GLESFuncs.glGenQueries && g_GLESFuncs.glDeleteQueries && g_GLESFuncs.glBeginQuery &&
|
||||
g_GLESFuncs.glEndQuery && g_GLESFuncs.glGetQueryObjectuiv;
|
||||
}
|
||||
|
||||
BackendQueryHandle BeginOcclusionQuery() {
|
||||
if (!IsBackendContextCurrentOnThisThread() || !AreOcclusionQueriesSupported()) {
|
||||
return nullptr;
|
||||
}
|
||||
GLuint queryId = 0;
|
||||
g_GLESFuncs.glGenQueries(1, &queryId);
|
||||
if (queryId == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
// ES only implements the boolean ANY_SAMPLES_PASSED variant, not an exact
|
||||
// GL_SAMPLES_PASSED count; the frontend already coerces ANY_SAMPLES_PASSED*
|
||||
// targets to boolean, and desktop GL_SAMPLES_PASSED reads a 0/1 approximation.
|
||||
g_GLESFuncs.glBeginQuery(GL_ANY_SAMPLES_PASSED, queryId);
|
||||
return new GLESQueryObject{queryId, g_syncContextGeneration, /*isOcclusion=*/true};
|
||||
}
|
||||
|
||||
void EndOcclusionQuery(BackendQueryHandle handle) {
|
||||
const auto* query = static_cast<GLESQueryObject*>(handle);
|
||||
if (query == nullptr || query->contextGeneration != g_syncContextGeneration ||
|
||||
!IsBackendContextCurrentOnThisThread() || !g_GLESFuncs.glEndQuery) {
|
||||
return;
|
||||
}
|
||||
g_GLESFuncs.glEndQuery(GL_ANY_SAMPLES_PASSED);
|
||||
}
|
||||
|
||||
Bool IsQueryResultAvailable(BackendQueryHandle handle) {
|
||||
const auto* query = static_cast<GLESQueryObject*>(handle);
|
||||
// Null/stale handles report available so the frontend proceeds to
|
||||
@@ -4562,7 +4615,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// report it as produced and let the frontend cache it and release
|
||||
// the handle.
|
||||
if (query == nullptr || query->contextGeneration != g_syncContextGeneration ||
|
||||
!g_GLESFuncs.glGetQueryObjectuiv || !g_GLESFuncs.glGetQueryObjectui64vEXT) {
|
||||
!g_GLESFuncs.glGetQueryObjectuiv ||
|
||||
(!query->isOcclusion && !g_GLESFuncs.glGetQueryObjectui64vEXT)) {
|
||||
return true;
|
||||
}
|
||||
// A thread that does not own the ES context cannot issue GL calls,
|
||||
@@ -4600,6 +4654,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// context switch) the result may be garbage, which is tolerable for
|
||||
// an F3 GPU% readout, and consuming the latched flag here could hide
|
||||
// the event from another observer.
|
||||
if (query->isOcclusion) {
|
||||
GLuint result32 = 0;
|
||||
g_GLESFuncs.glGetQueryObjectuiv(query->queryId, GL_QUERY_RESULT, &result32);
|
||||
*outNanoseconds = static_cast<Uint64>(result32);
|
||||
return true;
|
||||
}
|
||||
GLuint64 result = 0;
|
||||
g_GLESFuncs.glGetQueryObjectui64vEXT(query->queryId, GL_QUERY_RESULT, &result);
|
||||
*outNanoseconds = static_cast<Uint64>(result);
|
||||
|
||||
@@ -130,6 +130,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
BackendQueryHandle BeginTimeElapsedQuery();
|
||||
void EndTimeElapsedQuery(BackendQueryHandle query);
|
||||
BackendQueryHandle QueryCounterTimestamp();
|
||||
// GL_ANY_SAMPLES_PASSED(_CONSERVATIVE) occlusion queries: core ES3, independent of
|
||||
// GL_EXT_disjoint_timer_query and of MOBILEGL_DISABLE_TIMERQUERY. Results/deletion
|
||||
// flow through GetQueryResult64/DeleteBackendQuery like the timer queries above.
|
||||
BackendQueryHandle BeginOcclusionQuery();
|
||||
void EndOcclusionQuery(BackendQueryHandle query);
|
||||
Bool IsQueryResultAvailable(BackendQueryHandle query);
|
||||
// Returns true when a final value landed in *outNanoseconds (a zero for
|
||||
// null or stale-generation handles IS final: the frontend may cache it
|
||||
|
||||
@@ -526,10 +526,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
E_GL_ARB_multi_draw_indirect, E_GL_ARB_indirect_parameters,
|
||||
E_GL_EXT_framebuffer_object, E_GL_ARB_depth_texture, E_GL_ARB_buffer_storage,
|
||||
E_GL_ARB_texture_storage, E_GL_ARB_texture_storage_multisample,
|
||||
E_GL_ARB_clear_texture, E_GL_ARB_direct_state_access,
|
||||
E_GL_ARB_texture_multisample, E_GL_ARB_clear_texture, E_GL_ARB_direct_state_access,
|
||||
E_GL_ARB_shader_draw_parameters, E_GL_ARB_gpu_shader_int64, E_GL_KHR_debug,
|
||||
E_GL_ARB_gpu_shader5, E_GL_ARB_multi_bind, E_GL_ARB_shading_language_420pack,
|
||||
E_GL_ARB_vertex_attrib_binding, E_GL_ARB_shader_image_size};
|
||||
E_GL_ARB_vertex_attrib_binding, E_GL_ARB_shader_image_size,
|
||||
E_GL_ARB_explicit_attrib_location};
|
||||
if (shaderSubgroupSupported && !MG_Config::Features.DisableSubgroup) {
|
||||
extensions.push_back(E_GL_KHR_shader_subgroup);
|
||||
}
|
||||
|
||||
@@ -2866,6 +2866,15 @@ void main() {
|
||||
|
||||
#if defined(VK_USE_PLATFORM_XLIB_KHR)
|
||||
if (m_platformDisplay != nullptr) {
|
||||
if (m_ownsFallbackXlibWindow && m_window != 0 && m_platformLibrary != nullptr) {
|
||||
using XDestroyWindowFn = int (*)(Display*, Window);
|
||||
auto* destroyWindow = reinterpret_cast<XDestroyWindowFn>(dlsym(m_platformLibrary, "XDestroyWindow"));
|
||||
if (destroyWindow) {
|
||||
destroyWindow(static_cast<Display*>(m_platformDisplay), static_cast<Window>(m_window));
|
||||
}
|
||||
m_window = 0;
|
||||
m_ownsFallbackXlibWindow = false;
|
||||
}
|
||||
using XCloseDisplayFn = int (*)(Display*);
|
||||
auto* closeDisplay = reinterpret_cast<XCloseDisplayFn>(m_platformCloseDisplay);
|
||||
if (closeDisplay) {
|
||||
@@ -6973,11 +6982,14 @@ void main() {
|
||||
}
|
||||
|
||||
const Bool readIsDefaultFbo = readFbo->IsDefaultFramebuffer();
|
||||
BlitImageBinding srcBinding{};
|
||||
if (!ResolveColorBlitBinding(*readFbo, true, m_imageIndexAcquired, m_swapchainObject, *m_textureManager,
|
||||
*m_renderPassManager, srcBinding)) {
|
||||
return;
|
||||
}
|
||||
// Materialize any pending clear on the read-buffer attachment BEFORE resolving the
|
||||
// blit binding below: for a renderbuffer/texture that has never been part of any
|
||||
// render pass yet (e.g. a GL_NONE draw buffer slot whose attachment is only ever
|
||||
// touched via an explicit glReadBuffer), materializing lazily creates its backing
|
||||
// Vulkan resource for the first time. UnorderedMap (FastSTL, open-addressing) may
|
||||
// rehash on that insertion, invalidating any RenderbufferResource*/TextureResource*
|
||||
// obtained beforehand - so ResolveColorBlitBinding's cached `trackedLayout` pointer
|
||||
// must be taken AFTER this, never before it.
|
||||
if (!readIsDefaultFbo) {
|
||||
const auto& sourceAttachment = readFbo->GetAttachment(readFbo->GetReadBuffer());
|
||||
auto sourceTexture = sourceAttachment.GetTexture();
|
||||
@@ -6995,6 +7007,12 @@ void main() {
|
||||
}
|
||||
}
|
||||
|
||||
BlitImageBinding srcBinding{};
|
||||
if (!ResolveColorBlitBinding(*readFbo, true, m_imageIndexAcquired, m_swapchainObject, *m_textureManager,
|
||||
*m_renderPassManager, srcBinding)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const VkImageLayout srcOriginalLayout = readIsDefaultFbo
|
||||
? m_swapchainObject.GetImageLayout(m_imageIndexAcquired)
|
||||
: *srcBinding.trackedLayout;
|
||||
@@ -9284,6 +9302,18 @@ void main() {
|
||||
if (!m_window) {
|
||||
#ifdef VK_USE_PLATFORM_METAL_EXT
|
||||
exts.push_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME);
|
||||
#elif defined VK_USE_PLATFORM_XLIB_KHR
|
||||
m_headlessSurfaceSupported = IsExtensionSupported(m_extensions, VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME);
|
||||
if (m_headlessSurfaceSupported) {
|
||||
exts.push_back(VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME);
|
||||
} else {
|
||||
// Real ICDs (e.g. NVIDIA's proprietary Linux driver) may not implement
|
||||
// VK_EXT_headless_surface at all. CreateSurface() falls back to a
|
||||
// hidden Xlib window in that case, so request that extension instead.
|
||||
MGLOG_I("%s not available; falling back to a hidden %s surface for the pbuffer context.",
|
||||
VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME, VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
|
||||
exts.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
|
||||
}
|
||||
#else
|
||||
exts.push_back(VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME);
|
||||
#endif
|
||||
@@ -9903,7 +9933,17 @@ void main() {
|
||||
VkPhysicalDeviceProperties2 properties2{};
|
||||
properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
|
||||
properties2.pNext = &xfbProperties;
|
||||
vkGetPhysicalDeviceProperties2(m_physicalDevice.handle, &properties2);
|
||||
// Resolved via proc addr: vkGetPhysicalDeviceProperties2 is Vulkan 1.1, and Android's
|
||||
// libvulkan.so only exports it from API 28 while minSdk is 26.
|
||||
auto getPhysicalDeviceProperties2 = reinterpret_cast<PFN_vkGetPhysicalDeviceProperties2>(
|
||||
vkGetInstanceProcAddr(m_instance, "vkGetPhysicalDeviceProperties2"));
|
||||
if (getPhysicalDeviceProperties2 == nullptr) {
|
||||
getPhysicalDeviceProperties2 = reinterpret_cast<PFN_vkGetPhysicalDeviceProperties2>(
|
||||
vkGetInstanceProcAddr(m_instance, "vkGetPhysicalDeviceProperties2KHR"));
|
||||
}
|
||||
if (getPhysicalDeviceProperties2 != nullptr) {
|
||||
getPhysicalDeviceProperties2(m_physicalDevice.handle, &properties2);
|
||||
}
|
||||
m_xfbQueriesSupported = xfbProperties.transformFeedbackQueries == VK_TRUE &&
|
||||
s_vkCmdBeginQueryIndexedEXT != nullptr && s_vkCmdEndQueryIndexedEXT != nullptr;
|
||||
}
|
||||
@@ -9985,6 +10025,21 @@ void main() {
|
||||
m_window = reinterpret_cast<NativeWindowType>(
|
||||
CreateInternalMetalLayer(m_config.SurfaceWidth, m_config.SurfaceHeight, &m_platformDisplay));
|
||||
m_platformLibrary = reinterpret_cast<void*>(m_window);
|
||||
#elif defined VK_USE_PLATFORM_XLIB_KHR
|
||||
if (m_headlessSurfaceSupported) {
|
||||
auto* createHeadlessSurface =
|
||||
reinterpret_cast<PFN_vkCreateHeadlessSurfaceEXT>(
|
||||
vkGetInstanceProcAddr(m_instance, "vkCreateHeadlessSurfaceEXT"));
|
||||
MOBILEGL_ASSERT(createHeadlessSurface != nullptr,
|
||||
"VK_EXT_headless_surface is not available for DirectVulkan pbuffer surface");
|
||||
VkHeadlessSurfaceCreateInfoEXT sci{VK_STRUCTURE_TYPE_HEADLESS_SURFACE_CREATE_INFO_EXT};
|
||||
VK_VERIFY(createHeadlessSurface(m_instance, &sci, nullptr, &m_surface),
|
||||
"vkCreateHeadlessSurfaceEXT failed");
|
||||
return;
|
||||
}
|
||||
// VK_EXT_headless_surface unavailable on this ICD: fall through to the
|
||||
// Xlib branch below, which creates a hidden window since m_window is
|
||||
// still null here.
|
||||
#else
|
||||
auto* createHeadlessSurface =
|
||||
reinterpret_cast<PFN_vkCreateHeadlessSurfaceEXT>(
|
||||
@@ -10019,7 +10074,8 @@ void main() {
|
||||
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");
|
||||
// m_window may legitimately still be null here: the pbuffer/headless-fallback
|
||||
// path below creates its own window when the caller didn't provide one.
|
||||
|
||||
void* x11Lib = dlopen("libX11.so.6", RTLD_LOCAL | RTLD_NOW);
|
||||
if (!x11Lib) {
|
||||
@@ -10039,6 +10095,38 @@ void main() {
|
||||
m_platformLibrary = x11Lib;
|
||||
m_platformCloseDisplay = reinterpret_cast<void*>(xCloseDisplay);
|
||||
|
||||
if (!m_window) {
|
||||
// Pbuffer/headless fallback: the ICD didn't implement
|
||||
// VK_EXT_headless_surface (e.g. NVIDIA's proprietary Linux driver), so
|
||||
// CreateInstance() requested VK_KHR_xlib_surface instead and left
|
||||
// m_window null for us to fill in here. This window is never mapped -
|
||||
// it exists only to give the WSI a valid drawable - so nothing is ever
|
||||
// shown on screen; the swapchain image backs the GL default framebuffer
|
||||
// exactly like the headless-surface path does.
|
||||
using XDefaultRootWindowFn = Window (*)(Display*);
|
||||
using XDefaultScreenFn = int (*)(Display*);
|
||||
using XBlackPixelFn = unsigned long (*)(Display*, int);
|
||||
using XCreateSimpleWindowFn = Window (*)(Display*, Window, int, int, unsigned int, unsigned int,
|
||||
unsigned int, unsigned long, unsigned long);
|
||||
auto* xDefaultRootWindow = reinterpret_cast<XDefaultRootWindowFn>(dlsym(x11Lib, "XDefaultRootWindow"));
|
||||
auto* xDefaultScreen = reinterpret_cast<XDefaultScreenFn>(dlsym(x11Lib, "XDefaultScreen"));
|
||||
auto* xBlackPixel = reinterpret_cast<XBlackPixelFn>(dlsym(x11Lib, "XBlackPixel"));
|
||||
auto* xCreateSimpleWindow =
|
||||
reinterpret_cast<XCreateSimpleWindowFn>(dlsym(x11Lib, "XCreateSimpleWindow"));
|
||||
MOBILEGL_ASSERT(xDefaultRootWindow && xDefaultScreen && xBlackPixel && xCreateSimpleWindow,
|
||||
"Failed to resolve XCreateSimpleWindow dependencies for the Xlib pbuffer fallback");
|
||||
|
||||
const int screen = xDefaultScreen(display);
|
||||
const Uint32 width = std::max<Uint32>(m_config.SurfaceWidth, 1);
|
||||
const Uint32 height = std::max<Uint32>(m_config.SurfaceHeight, 1);
|
||||
const Window fallbackWindow = xCreateSimpleWindow(display, xDefaultRootWindow(display), 0, 0, width,
|
||||
height, 0, xBlackPixel(display, screen),
|
||||
xBlackPixel(display, screen));
|
||||
MOBILEGL_ASSERT(fallbackWindow != 0, "XCreateSimpleWindow failed for the Xlib pbuffer fallback");
|
||||
m_window = static_cast<NativeWindowType>(fallbackWindow);
|
||||
m_ownsFallbackXlibWindow = true;
|
||||
}
|
||||
|
||||
VkXlibSurfaceCreateInfoKHR sci{VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR};
|
||||
sci.dpy = display;
|
||||
sci.window = static_cast<Window>(m_window);
|
||||
|
||||
@@ -431,6 +431,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
void* m_platformDisplay = nullptr;
|
||||
void* m_platformLibrary = nullptr;
|
||||
void* m_platformCloseDisplay = nullptr;
|
||||
// Some real ICDs (e.g. NVIDIA's proprietary Linux driver) don't implement
|
||||
// VK_EXT_headless_surface at all. Detected once in CreateInstance() from the
|
||||
// enumerated instance extensions; when false, CreateSurface() falls back to a
|
||||
// hidden Xlib window instead of vkCreateHeadlessSurfaceEXT.
|
||||
Bool m_headlessSurfaceSupported = true;
|
||||
// Set when CreateSurface() had to create its own Xlib window for the fallback
|
||||
// above (rather than being handed one by the caller), so Shutdown() knows it
|
||||
// owns that window and must destroy it.
|
||||
Bool m_ownsFallbackXlibWindow = false;
|
||||
VulkanRendererConfig m_config;
|
||||
Bool m_swapchainResizeRequested = false;
|
||||
// Presentation is suspended while the window is zero-area (minimized): the
|
||||
|
||||
@@ -929,6 +929,15 @@ namespace MobileGL::MG_Util::BackendLoader {
|
||||
glesFuncs.glGetIntegerv(GL_MAX_INTEGER_SAMPLES, &maxIntegerSamples);
|
||||
glesFuncs.glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
|
||||
glesFuncs.glGetIntegerv(GL_MAX_SAMPLE_MASK_WORDS, &maxSampleMaskWords);
|
||||
// MobileGL's sample-mask state only stores a single 32-bit word (see
|
||||
// RenderState::SampleMaskValue) and SampleMaski_State() rejects any
|
||||
// maskNumber other than 0. Advertising the real driver's value here (e.g.
|
||||
// NVIDIA's GLES driver reports 2) makes glSampleMaski(1, ...) - which
|
||||
// dEQP's per-case gluStateReset always issues up to GL_MAX_SAMPLE_MASK_WORDS -
|
||||
// raise GL_INVALID_VALUE and aborts the whole glcts process after every
|
||||
// single test case. 1 is a spec-legal value (the minimum required), so cap
|
||||
// to what is actually implemented instead of forwarding the raw driver limit.
|
||||
maxSampleMaskWords = std::min(maxSampleMaskWords, 1);
|
||||
glesFuncs.glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureImageUnits);
|
||||
glesFuncs.glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxVertexTextureImageUnits);
|
||||
glesFuncs.glGetIntegerv(GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS, &maxComputeTextureImageUnits);
|
||||
|
||||
@@ -55,7 +55,7 @@ val releaseSigningReady = signingStoreFile.exists()
|
||||
val debuggableRelease = (findProperty("mobilegl.debuggableRelease") ?: "false").toString().toBoolean()
|
||||
|
||||
val mobileGlVersionMajor = 26
|
||||
val mobileGlVersionMinor = 7
|
||||
val mobileGlVersionMinor = 8
|
||||
val mobileGlGitShortHash = runGit("rev-parse", "--short=7", "HEAD") ?: "nogit"
|
||||
val mobileGlMonthlyRevision = runGit(
|
||||
"rev-list",
|
||||
|
||||
+1
-1
Submodule include/FastSTL updated: f8567f66ae...022211c998
@@ -65,6 +65,8 @@ def main():
|
||||
help="seconds before killing one glcts invocation (a wedged case never returns)")
|
||||
ap.add_argument("--skip-file", default=None,
|
||||
help="file of case names to exclude, e.g. cases known to wedge the host")
|
||||
ap.add_argument("--waiver-file", default=None,
|
||||
help="--deqp-waiver-file value, e.g. tools/cts/waivers/mobilegl-fbo-harness.xml")
|
||||
ap.add_argument("--env", action="append", default=[], metavar="K=V",
|
||||
help="extra environment variable for glcts (repeatable)")
|
||||
args = ap.parse_args()
|
||||
@@ -135,6 +137,8 @@ def main():
|
||||
"--deqp-log-shader-sources=disable",
|
||||
f"--deqp-log-filename={qpa}",
|
||||
]
|
||||
if args.waiver_file:
|
||||
cmd.append(f"--deqp-waiver-file={os.path.abspath(args.waiver_file)}")
|
||||
timed_out = False
|
||||
try:
|
||||
# RLIMIT_CORE=0: MobileGL asserts abort with a core dump, and writing
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<waiver_list>
|
||||
|
||||
<!--
|
||||
Waivers for known false failures caused by this local test harness's
|
||||
fbo-surface-type mode (deqp-surface-type=fbo), not by MobileGL itself.
|
||||
-->
|
||||
|
||||
<waiver vendor="MobileGL-Dev*" url="local-only: fbo-surface-type wrapper-FBO artifact, not a MobileGL conformance bug">
|
||||
<description>
|
||||
dEQP's fbo surface-type mode wraps rendering in its own
|
||||
application level framebuffer object (a real, non-zero-named FBO,
|
||||
not framebuffer 0). ApiCoverageTestCase's ReadBuffer coverage
|
||||
sub-test captures GL_READ_BUFFER while that wrapper FBO is bound
|
||||
(a valid GL_COLOR_ATTACHMENTn value there), then later deletes an
|
||||
unrelated FBO of its own; per the GL spec, deleting a bound FBO
|
||||
implicitly rebinds framebuffer target 0, the TRUE default
|
||||
framebuffer this time, not the wrapper. Restoring the captured
|
||||
GL_COLOR_ATTACHMENTn value against the true default framebuffer
|
||||
correctly raises GL_INVALID_ENUM per spec (only FRONT/BACK style
|
||||
tokens are valid there), so the test fails with a fully
|
||||
spec conformant driver. This can only happen when the harness's
|
||||
"default framebuffer" is a real dEQP created FBO instead of a
|
||||
genuine window/pbuffer backed framebuffer 0, which is unique to
|
||||
this local fbo surface-type setup. Confirmed by tracing
|
||||
framebuffer bindings/external indices across the test's
|
||||
execution (2026-08-01).
|
||||
</description>
|
||||
<renderer_list>
|
||||
<r>Magma*</r>
|
||||
<r>Espryt*</r>
|
||||
</renderer_list>
|
||||
<t>KHR-GL30.api.coverage</t>
|
||||
<t>KHR-GL31.api.coverage</t>
|
||||
<t>KHR-GL32.api.coverage</t>
|
||||
<t>KHR-GL33.api.coverage</t>
|
||||
</waiver>
|
||||
|
||||
</waiver_list>
|
||||
Reference in New Issue
Block a user