mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 22:28:32 +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 ProjectName = "MobileGL";
|
||||||
inline const String CoreName = "MobileGL Core";
|
inline const String CoreName = "MobileGL Core";
|
||||||
inline const String CoreVendor = "MobileGL-Dev (BZLZHH, Swung0x48, Tungsten)";
|
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 VersionStringFormatAttrib DefaultVersionStringFormatAttrib = {2, 2, 0, true, true};
|
||||||
inline const Uint64 CacheVersion = 0;
|
inline const Uint64 CacheVersion = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -935,11 +935,16 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
funcsTable.GL.BeginTimeElapsedQuery = BeginTimeElapsedQuery;
|
funcsTable.GL.BeginTimeElapsedQuery = BeginTimeElapsedQuery;
|
||||||
funcsTable.GL.EndTimeElapsedQuery = EndTimeElapsedQuery;
|
funcsTable.GL.EndTimeElapsedQuery = EndTimeElapsedQuery;
|
||||||
funcsTable.GL.QueryCounterTimestamp = QueryCounterTimestamp;
|
funcsTable.GL.QueryCounterTimestamp = QueryCounterTimestamp;
|
||||||
|
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.IsQueryResultAvailable = IsQueryResultAvailable;
|
||||||
funcsTable.GL.GetQueryResult64 = GetQueryResult64;
|
funcsTable.GL.GetQueryResult64 = GetQueryResult64;
|
||||||
funcsTable.GL.DeleteBackendQuery = DeleteBackendQuery;
|
funcsTable.GL.DeleteBackendQuery = DeleteBackendQuery;
|
||||||
funcsTable.GL.GetGpuTimestampNs = GetGpuTimestampNs;
|
|
||||||
}
|
|
||||||
funcsTableInitialized = true;
|
funcsTableInitialized = true;
|
||||||
}
|
}
|
||||||
return funcsTable;
|
return funcsTable;
|
||||||
|
|||||||
@@ -3632,12 +3632,16 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
|
|
||||||
static Bool IsLegacyNativeReadPixelsFormat(GLenum format) {
|
static Bool IsLegacyNativeReadPixelsFormat(GLenum format) {
|
||||||
return format == GL_RGBA || format == GL_RGBA_INTEGER || format == GL_RED || format == GL_RED_INTEGER ||
|
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) {
|
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 ||
|
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) {
|
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) {
|
if (format == GL_RGBA_INTEGER) {
|
||||||
return type == GL_INT || type == GL_UNSIGNED_INT || type == GL_UNSIGNED_INT_2_10_10_10_REV;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3850,7 +3857,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
MGLOG_D("GetTexImage: attaching level %d to the scratch FBO", level);
|
MGLOG_D("GetTexImage: attaching level %d to the scratch FBO", level);
|
||||||
const GLenum backendAttachTarget = TextureImpl::ConvertTextureUploadTargetToBackendGLEnum(
|
const GLenum backendAttachTarget = TextureImpl::ConvertTextureUploadTargetToBackendGLEnum(
|
||||||
MG_Util::ConvertGLEnumToTextureUploadTarget(target));
|
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
|
// 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).
|
// of deeper slices are served from the CPU shadow instead (see the shadow-first branch).
|
||||||
ScratchFBOImpl::EnsureColorAttachmentLayer(tempFB, GL_READ_FRAMEBUFFER, backendTexId, level, 0);
|
ScratchFBOImpl::EnsureColorAttachmentLayer(tempFB, GL_READ_FRAMEBUFFER, backendTexId, level, 0);
|
||||||
@@ -3859,8 +3873,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
tempFB, GL_READ_FRAMEBUFFER, backendTexId,
|
tempFB, GL_READ_FRAMEBUFFER, backendTexId,
|
||||||
backendAttachTarget == GL_UNKNOWN_MGL ? target : backendAttachTarget, level);
|
backendAttachTarget == GL_UNKNOWN_MGL ? target : backendAttachTarget, level);
|
||||||
}
|
}
|
||||||
|
if (format != GL_DEPTH_STENCIL) {
|
||||||
MGLOG_D("GetTexImage: glReadBuffer(GL_COLOR_ATTACHMENT0)");
|
MGLOG_D("GetTexImage: glReadBuffer(GL_COLOR_ATTACHMENT0)");
|
||||||
ScratchFBOImpl::EnsureReadBuffer(tempFB, GL_COLOR_ATTACHMENT0);
|
ScratchFBOImpl::EnsureReadBuffer(tempFB, GL_COLOR_ATTACHMENT0);
|
||||||
|
}
|
||||||
|
|
||||||
GLenum fbStatus = g_GLESFuncs.glCheckFramebufferStatus(GL_READ_FRAMEBUFFER);
|
GLenum fbStatus = g_GLESFuncs.glCheckFramebufferStatus(GL_READ_FRAMEBUFFER);
|
||||||
MGLOG_D("GetTexImage: GL_READ_FRAMEBUFFER status = %s", MG_Util::ConvertGLEnumToString(fbStatus).c_str());
|
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 {
|
struct GLESQueryObject {
|
||||||
GLuint queryId = 0;
|
GLuint queryId = 0;
|
||||||
Uint contextGeneration = 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};
|
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) {
|
Bool IsQueryResultAvailable(BackendQueryHandle handle) {
|
||||||
const auto* query = static_cast<GLESQueryObject*>(handle);
|
const auto* query = static_cast<GLESQueryObject*>(handle);
|
||||||
// Null/stale handles report available so the frontend proceeds to
|
// 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
|
// report it as produced and let the frontend cache it and release
|
||||||
// the handle.
|
// the handle.
|
||||||
if (query == nullptr || query->contextGeneration != g_syncContextGeneration ||
|
if (query == nullptr || query->contextGeneration != g_syncContextGeneration ||
|
||||||
!g_GLESFuncs.glGetQueryObjectuiv || !g_GLESFuncs.glGetQueryObjectui64vEXT) {
|
!g_GLESFuncs.glGetQueryObjectuiv ||
|
||||||
|
(!query->isOcclusion && !g_GLESFuncs.glGetQueryObjectui64vEXT)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// A thread that does not own the ES context cannot issue GL calls,
|
// 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
|
// context switch) the result may be garbage, which is tolerable for
|
||||||
// an F3 GPU% readout, and consuming the latched flag here could hide
|
// an F3 GPU% readout, and consuming the latched flag here could hide
|
||||||
// the event from another observer.
|
// 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;
|
GLuint64 result = 0;
|
||||||
g_GLESFuncs.glGetQueryObjectui64vEXT(query->queryId, GL_QUERY_RESULT, &result);
|
g_GLESFuncs.glGetQueryObjectui64vEXT(query->queryId, GL_QUERY_RESULT, &result);
|
||||||
*outNanoseconds = static_cast<Uint64>(result);
|
*outNanoseconds = static_cast<Uint64>(result);
|
||||||
|
|||||||
@@ -130,6 +130,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
BackendQueryHandle BeginTimeElapsedQuery();
|
BackendQueryHandle BeginTimeElapsedQuery();
|
||||||
void EndTimeElapsedQuery(BackendQueryHandle query);
|
void EndTimeElapsedQuery(BackendQueryHandle query);
|
||||||
BackendQueryHandle QueryCounterTimestamp();
|
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);
|
Bool IsQueryResultAvailable(BackendQueryHandle query);
|
||||||
// Returns true when a final value landed in *outNanoseconds (a zero for
|
// Returns true when a final value landed in *outNanoseconds (a zero for
|
||||||
// null or stale-generation handles IS final: the frontend may cache it
|
// 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_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_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_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_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_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) {
|
if (shaderSubgroupSupported && !MG_Config::Features.DisableSubgroup) {
|
||||||
extensions.push_back(E_GL_KHR_shader_subgroup);
|
extensions.push_back(E_GL_KHR_shader_subgroup);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2866,6 +2866,15 @@ void main() {
|
|||||||
|
|
||||||
#if defined(VK_USE_PLATFORM_XLIB_KHR)
|
#if defined(VK_USE_PLATFORM_XLIB_KHR)
|
||||||
if (m_platformDisplay != nullptr) {
|
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*);
|
using XCloseDisplayFn = int (*)(Display*);
|
||||||
auto* closeDisplay = reinterpret_cast<XCloseDisplayFn>(m_platformCloseDisplay);
|
auto* closeDisplay = reinterpret_cast<XCloseDisplayFn>(m_platformCloseDisplay);
|
||||||
if (closeDisplay) {
|
if (closeDisplay) {
|
||||||
@@ -6973,11 +6982,14 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Bool readIsDefaultFbo = readFbo->IsDefaultFramebuffer();
|
const Bool readIsDefaultFbo = readFbo->IsDefaultFramebuffer();
|
||||||
BlitImageBinding srcBinding{};
|
// Materialize any pending clear on the read-buffer attachment BEFORE resolving the
|
||||||
if (!ResolveColorBlitBinding(*readFbo, true, m_imageIndexAcquired, m_swapchainObject, *m_textureManager,
|
// blit binding below: for a renderbuffer/texture that has never been part of any
|
||||||
*m_renderPassManager, srcBinding)) {
|
// render pass yet (e.g. a GL_NONE draw buffer slot whose attachment is only ever
|
||||||
return;
|
// 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) {
|
if (!readIsDefaultFbo) {
|
||||||
const auto& sourceAttachment = readFbo->GetAttachment(readFbo->GetReadBuffer());
|
const auto& sourceAttachment = readFbo->GetAttachment(readFbo->GetReadBuffer());
|
||||||
auto sourceTexture = sourceAttachment.GetTexture();
|
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
|
const VkImageLayout srcOriginalLayout = readIsDefaultFbo
|
||||||
? m_swapchainObject.GetImageLayout(m_imageIndexAcquired)
|
? m_swapchainObject.GetImageLayout(m_imageIndexAcquired)
|
||||||
: *srcBinding.trackedLayout;
|
: *srcBinding.trackedLayout;
|
||||||
@@ -9284,6 +9302,18 @@ void main() {
|
|||||||
if (!m_window) {
|
if (!m_window) {
|
||||||
#ifdef VK_USE_PLATFORM_METAL_EXT
|
#ifdef VK_USE_PLATFORM_METAL_EXT
|
||||||
exts.push_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME);
|
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
|
#else
|
||||||
exts.push_back(VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME);
|
exts.push_back(VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME);
|
||||||
#endif
|
#endif
|
||||||
@@ -9903,7 +9933,17 @@ void main() {
|
|||||||
VkPhysicalDeviceProperties2 properties2{};
|
VkPhysicalDeviceProperties2 properties2{};
|
||||||
properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
|
properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
|
||||||
properties2.pNext = &xfbProperties;
|
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 &&
|
m_xfbQueriesSupported = xfbProperties.transformFeedbackQueries == VK_TRUE &&
|
||||||
s_vkCmdBeginQueryIndexedEXT != nullptr && s_vkCmdEndQueryIndexedEXT != nullptr;
|
s_vkCmdBeginQueryIndexedEXT != nullptr && s_vkCmdEndQueryIndexedEXT != nullptr;
|
||||||
}
|
}
|
||||||
@@ -9985,6 +10025,21 @@ void main() {
|
|||||||
m_window = reinterpret_cast<NativeWindowType>(
|
m_window = reinterpret_cast<NativeWindowType>(
|
||||||
CreateInternalMetalLayer(m_config.SurfaceWidth, m_config.SurfaceHeight, &m_platformDisplay));
|
CreateInternalMetalLayer(m_config.SurfaceWidth, m_config.SurfaceHeight, &m_platformDisplay));
|
||||||
m_platformLibrary = reinterpret_cast<void*>(m_window);
|
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
|
#else
|
||||||
auto* createHeadlessSurface =
|
auto* createHeadlessSurface =
|
||||||
reinterpret_cast<PFN_vkCreateHeadlessSurfaceEXT>(
|
reinterpret_cast<PFN_vkCreateHeadlessSurfaceEXT>(
|
||||||
@@ -10019,7 +10074,8 @@ void main() {
|
|||||||
sci.pLayer = reinterpret_cast<const void*>(m_window);
|
sci.pLayer = reinterpret_cast<const void*>(m_window);
|
||||||
VK_VERIFY(vkCreateMetalSurfaceEXT(m_instance, &sci, nullptr, &m_surface), "vkCreateMetalSurfaceEXT failed");
|
VK_VERIFY(vkCreateMetalSurfaceEXT(m_instance, &sci, nullptr, &m_surface), "vkCreateMetalSurfaceEXT failed");
|
||||||
#elif defined VK_USE_PLATFORM_XLIB_KHR
|
#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);
|
void* x11Lib = dlopen("libX11.so.6", RTLD_LOCAL | RTLD_NOW);
|
||||||
if (!x11Lib) {
|
if (!x11Lib) {
|
||||||
@@ -10039,6 +10095,38 @@ void main() {
|
|||||||
m_platformLibrary = x11Lib;
|
m_platformLibrary = x11Lib;
|
||||||
m_platformCloseDisplay = reinterpret_cast<void*>(xCloseDisplay);
|
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};
|
VkXlibSurfaceCreateInfoKHR sci{VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR};
|
||||||
sci.dpy = display;
|
sci.dpy = display;
|
||||||
sci.window = static_cast<Window>(m_window);
|
sci.window = static_cast<Window>(m_window);
|
||||||
|
|||||||
@@ -431,6 +431,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
void* m_platformDisplay = nullptr;
|
void* m_platformDisplay = nullptr;
|
||||||
void* m_platformLibrary = nullptr;
|
void* m_platformLibrary = nullptr;
|
||||||
void* m_platformCloseDisplay = 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;
|
VulkanRendererConfig m_config;
|
||||||
Bool m_swapchainResizeRequested = false;
|
Bool m_swapchainResizeRequested = false;
|
||||||
// Presentation is suspended while the window is zero-area (minimized): the
|
// 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_INTEGER_SAMPLES, &maxIntegerSamples);
|
||||||
glesFuncs.glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
|
glesFuncs.glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
|
||||||
glesFuncs.glGetIntegerv(GL_MAX_SAMPLE_MASK_WORDS, &maxSampleMaskWords);
|
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_TEXTURE_IMAGE_UNITS, &maxTextureImageUnits);
|
||||||
glesFuncs.glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxVertexTextureImageUnits);
|
glesFuncs.glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxVertexTextureImageUnits);
|
||||||
glesFuncs.glGetIntegerv(GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS, &maxComputeTextureImageUnits);
|
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 debuggableRelease = (findProperty("mobilegl.debuggableRelease") ?: "false").toString().toBoolean()
|
||||||
|
|
||||||
val mobileGlVersionMajor = 26
|
val mobileGlVersionMajor = 26
|
||||||
val mobileGlVersionMinor = 7
|
val mobileGlVersionMinor = 8
|
||||||
val mobileGlGitShortHash = runGit("rev-parse", "--short=7", "HEAD") ?: "nogit"
|
val mobileGlGitShortHash = runGit("rev-parse", "--short=7", "HEAD") ?: "nogit"
|
||||||
val mobileGlMonthlyRevision = runGit(
|
val mobileGlMonthlyRevision = runGit(
|
||||||
"rev-list",
|
"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)")
|
help="seconds before killing one glcts invocation (a wedged case never returns)")
|
||||||
ap.add_argument("--skip-file", default=None,
|
ap.add_argument("--skip-file", default=None,
|
||||||
help="file of case names to exclude, e.g. cases known to wedge the host")
|
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",
|
ap.add_argument("--env", action="append", default=[], metavar="K=V",
|
||||||
help="extra environment variable for glcts (repeatable)")
|
help="extra environment variable for glcts (repeatable)")
|
||||||
args = ap.parse_args()
|
args = ap.parse_args()
|
||||||
@@ -135,6 +137,8 @@ def main():
|
|||||||
"--deqp-log-shader-sources=disable",
|
"--deqp-log-shader-sources=disable",
|
||||||
f"--deqp-log-filename={qpa}",
|
f"--deqp-log-filename={qpa}",
|
||||||
]
|
]
|
||||||
|
if args.waiver_file:
|
||||||
|
cmd.append(f"--deqp-waiver-file={os.path.abspath(args.waiver_file)}")
|
||||||
timed_out = False
|
timed_out = False
|
||||||
try:
|
try:
|
||||||
# RLIMIT_CORE=0: MobileGL asserts abort with a core dump, and writing
|
# 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