[Fix]: fix glmark2 crash

- deal with legacy GLSL syntax (attribute/varying/gl_FragColor/texture2D/etc.)
- implement glGet GL_SHADER_SOURCE_LENGTH, and make sure returns
  original shader source
- expose proper extensions (GL_ARB_depth_texture)
- support env var MOBILEGL_LOG_FILE_PATH
- unit tests to test against those changes
This commit is contained in:
2026-06-07 11:08:38 +08:00
parent ff41e59282
commit 19ada4b8f9
13 changed files with 439 additions and 48 deletions
@@ -126,7 +126,7 @@ 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_EXT_framebuffer_object, E_GL_ARB_depth_texture},
.IsCompatibilityProfile = false // Is Compatibility Profile
},
.StaticBackendCapability = {.AllowVSOnlyPrograms = false} // Backend Capability
+16 -5
View File
@@ -214,21 +214,22 @@ namespace MobileGL::MG_Backend::DirectGLES {
g_GLESFuncs.glBindVertexArray(m_backendVAOId);
}
inline void BindAttributeBuffer(const MG_State::GLState::VertexAttribute& attrib) {
inline Bool BindAttributeBuffer(const MG_State::GLState::VertexAttribute& attrib) {
const auto& bufferObject = attrib.Buffer;
if (!bufferObject) {
MGLOG_W("Attribute has no bound buffer, skipping.");
return;
return false;
}
const auto& backendBufferIt = BufferImpl::g_backendBufferObjects.find(bufferObject.get());
if (backendBufferIt == BufferImpl::g_backendBufferObjects.end()) {
MGLOG_E("No backend buffer found for attribute's buffer, cannot bind attribute.");
return;
return false;
}
const auto& backendBufferObject = backendBufferIt->second;
backendBufferObject->Bind(GL_ARRAY_BUFFER);
return true;
}
void BackendVertexArrayObject::SyncToBackend(
@@ -266,7 +267,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
m_syncedAttributeVersions[attribIndex].BufferVersion;
if (!needsSyncFormat && !needsSyncBuffer) continue;
BindAttributeBuffer(attrib);
if (!BindAttributeBuffer(attrib)) {
continue;
}
if (!attrib.IsInteger) {
g_GLESFuncs.glVertexAttribPointer(
@@ -286,16 +289,24 @@ namespace MobileGL::MG_Backend::DirectGLES {
Uint16 currentIndexBufferVersion = stateVAOObject->GetIndexBufferBindingSlot().GetVersion();
if (currentIndexBufferVersion != m_syncedIndexBufferVersion) {
const auto& indexBufferBinding = stateVAOObject->GetIndexBufferBindingSlot().GetBoundObject();
Bool indexBufferSynced = false;
if (indexBufferBinding) {
const auto& backendBufferIt = BufferImpl::g_backendBufferObjects.find(indexBufferBinding.get());
if (backendBufferIt != BufferImpl::g_backendBufferObjects.end()) {
const auto& backendBufferObject = backendBufferIt->second;
backendBufferObject->Bind(GL_ELEMENT_ARRAY_BUFFER);
indexBufferSynced = true;
} else {
MGLOG_W("No backend buffer found for index buffer binding, cannot bind index buffer.");
}
} else {
g_GLESFuncs.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
indexBufferSynced = true;
}
if (indexBufferSynced) {
m_syncedIndexBufferVersion = currentIndexBufferVersion;
}
m_syncedIndexBufferVersion = currentIndexBufferVersion;
}
m_syncedAttributeVersions = allAttributeVersions;
+27 -15
View File
@@ -15,6 +15,15 @@
#include <MG_Util/Converters/MGToGL/BufferEnumConverter.h>
namespace MobileGL::MG_Impl::GLImpl {
namespace {
auto& GetBufferBindingSlot(BufferTarget target) {
if (target == BufferTarget::Index) {
return MG_State::pGLContext->GetBoundVertexArray()->GetIndexBufferBindingSlot();
}
return MG_State::pGLContext->GetBufferBindingSlot(target);
}
} // namespace
void GetBufferParameteriv_State(GLenum target, GLenum pname, GLint* params) {
if (!params) {
MG_State::pGLContext->RecordError(
@@ -26,7 +35,7 @@ namespace MobileGL::MG_Impl::GLImpl {
BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target);
if (!BufferImpl::ValidateBufferTarget(bufferTarget)) return;
auto& bindingSlot = MG_State::pGLContext->GetBufferBindingSlot(bufferTarget);
auto& bindingSlot = GetBufferBindingSlot(bufferTarget);
auto& bufferObject = bindingSlot.GetBoundObject();
if (!bufferObject) {
@@ -105,7 +114,7 @@ namespace MobileGL::MG_Impl::GLImpl {
BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target);
if (!BufferImpl::ValidateBufferTarget(bufferTarget)) return;
auto& bindingSlot = MG_State::pGLContext->GetBufferBindingSlot(bufferTarget);
auto& bindingSlot = GetBufferBindingSlot(bufferTarget);
auto& bufferObject = bindingSlot.GetBoundObject();
if (!bufferObject) {
@@ -148,7 +157,7 @@ namespace MobileGL::MG_Impl::GLImpl {
BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target);
if (!BufferImpl::ValidateBufferTarget(bufferTarget)) return GL_FALSE;
auto& bindingSlot = MG_State::pGLContext->GetBufferBindingSlot(bufferTarget);
auto& bindingSlot = GetBufferBindingSlot(bufferTarget);
auto& bufferObject = bindingSlot.GetBoundObject();
if (!bufferObject) {
@@ -189,7 +198,7 @@ namespace MobileGL::MG_Impl::GLImpl {
BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target);
if (!BufferImpl::ValidateBufferTarget(bufferTarget)) return nullptr;
auto& bindingSlot = MG_State::pGLContext->GetBufferBindingSlot(bufferTarget);
auto& bindingSlot = GetBufferBindingSlot(bufferTarget);
auto& bufferObject = bindingSlot.GetBoundObject();
if (!bufferObject) {
MG_State::pGLContext->RecordError(
@@ -291,7 +300,7 @@ namespace MobileGL::MG_Impl::GLImpl {
BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target);
if (!BufferImpl::ValidateBufferTarget(bufferTarget)) return nullptr;
auto& bindingSlot = MG_State::pGLContext->GetBufferBindingSlot(bufferTarget);
auto& bindingSlot = GetBufferBindingSlot(bufferTarget);
auto& bufferObject = bindingSlot.GetBoundObject();
if (!bufferObject) {
@@ -335,8 +344,8 @@ namespace MobileGL::MG_Impl::GLImpl {
if (!BufferImpl::ValidateBufferTarget(readBufferTarget) || !BufferImpl::ValidateBufferTarget(writeBufferTarget))
return;
auto& readBindingSlot = MG_State::pGLContext->GetBufferBindingSlot(readBufferTarget);
auto& writeBindingSlot = MG_State::pGLContext->GetBufferBindingSlot(writeBufferTarget);
auto& readBindingSlot = GetBufferBindingSlot(readBufferTarget);
auto& writeBindingSlot = GetBufferBindingSlot(writeBufferTarget);
auto& readBufferObject = readBindingSlot.GetBoundObject();
auto& writeBufferObject = writeBindingSlot.GetBoundObject();
@@ -401,7 +410,7 @@ namespace MobileGL::MG_Impl::GLImpl {
BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target);
if (!BufferImpl::ValidateBufferTarget(bufferTarget)) return;
auto& bindingSlot = MG_State::pGLContext->GetBufferBindingSlot(bufferTarget);
auto& bindingSlot = GetBufferBindingSlot(bufferTarget);
auto& bufferObject = bindingSlot.GetBoundObject();
if (!bufferObject) {
@@ -454,7 +463,7 @@ namespace MobileGL::MG_Impl::GLImpl {
BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target);
if (!BufferImpl::ValidateBufferTarget(bufferTarget)) return;
auto& bindingSlot = MG_State::pGLContext->GetBufferBindingSlot(bufferTarget);
auto& bindingSlot = GetBufferBindingSlot(bufferTarget);
auto& bufferObject = bindingSlot.GetBoundObject();
if (!bufferObject) {
@@ -477,15 +486,18 @@ namespace MobileGL::MG_Impl::GLImpl {
BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target);
if (!BufferImpl::ValidateBufferTarget(bufferTarget)) return;
Bool doesBufferObjectCreated = MG_State::pGLContext->ValidateBufferObject(buffer);
if (!doesBufferObjectCreated) {
MG_State::pGLContext->CreateBufferObject(buffer);
SharedPtr<MG_State::GLState::BufferObject> bufferObject;
if (buffer != 0) {
Bool doesBufferObjectCreated = MG_State::pGLContext->ValidateBufferObject(buffer);
if (!doesBufferObjectCreated) {
MG_State::pGLContext->CreateBufferObject(buffer);
}
bufferObject = MG_State::pGLContext->GetBufferObject(buffer);
}
auto& bufferObject = MG_State::pGLContext->GetBufferObject(buffer);
auto& bindingSlot = MG_State::pGLContext->GetBufferBindingSlot(bufferTarget);
auto& bindingSlot = GetBufferBindingSlot(bufferTarget);
bindingSlot.Bind(bufferObject);
MGLOG_D("%s: bind buffer object %d -> %s", __func__, bufferObject->GetExternalIndex(),
MGLOG_D("%s: bind buffer object %d -> %s", __func__, bufferObject ? bufferObject->GetExternalIndex() : 0,
MG_Util::ConvertGLEnumToString(target).c_str());
}
+18 -13
View File
@@ -69,13 +69,15 @@ namespace MobileGL::MG_Impl::GLImpl {
}
void CopyStr(GLsizei bufSize, GLsizei* length, GLchar* dst, const char* src, GLsizei srcLength) {
if (bufSize <= 0) {
if (length) *length = 0;
return;
}
auto sz = std::min(bufSize - 1, srcLength);
if (length) *length = sz;
if (bufSize == 0) return;
Memcpy(dst, src, sz);
dst[sz] = '\0';
if (length) *length = sz;
}
void AttachShader_State(GLuint program, GLuint shader) {
@@ -317,8 +319,8 @@ namespace MobileGL::MG_Impl::GLImpl {
return;
}
getProgramiv(program, pname, params);
MGLOG_D("%s: %s = (%d, %d, %d)", __func__,
MG_Util::ConvertGLEnumToString(pname).c_str(), params[0], params[1], params[2]);
MGLOG_D("%s: %s = (%d, %d, %d)", __func__, MG_Util::ConvertGLEnumToString(pname).c_str(), params[0],
params[1], params[2]);
break;
}
@@ -363,10 +365,10 @@ namespace MobileGL::MG_Impl::GLImpl {
*params = shaderObject->GetCompileStatus();
break;
case GL_INFO_LOG_LENGTH:
*params = (GLint)shaderObject->GetInfoLog().length();
*params = shaderObject->GetInfoLog().empty() ? 0 : (GLint)shaderObject->GetInfoLog().length() + 1;
break;
case GL_SHADER_SOURCE_LENGTH:
*params = (GLint)shaderObject->GetShaderSource().length();
*params = shaderObject->GetShaderSource().empty() ? 0 : (GLint)shaderObject->GetShaderSource().length() + 1;
break;
default:
MG_State::pGLContext->RecordError(
@@ -525,7 +527,10 @@ namespace MobileGL::MG_Impl::GLImpl {
std::string src;
for (GLsizei i = 0; i < count; i++) {
src += (length == nullptr || length[i] <= 0) ? string[i] : std::string(string[i], length[i]);
if (!string[i]) {
continue;
}
src += (length && length[i] >= 0) ? std::string(string[i], length[i]) : std::string(string[i]);
}
shaderObject->SetShaderSource(Move(src));
}
@@ -1475,8 +1480,8 @@ namespace MobileGL::MG_Impl::GLImpl {
return getProgramResourceIndex(program, programInterface, name);
}
void GetProgramResourceName(GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize,
GLsizei* length, GLchar* name) {
void GetProgramResourceName(GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei* length,
GLchar* name) {
auto& programObject = TryToGetProgramObject(program);
if (!programObject || !programObject->GetLinkStatus()) return;
if (bufSize < 0) {
@@ -1502,8 +1507,8 @@ namespace MobileGL::MG_Impl::GLImpl {
if (!programObject || !programObject->GetLinkStatus()) return;
if (propCount < 0 || bufSize < 0) {
MG_State::pGLContext->RecordError(
ErrorCode::InvalidValue,
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "propCount and bufSize must be non-negative."));
ErrorCode::InvalidValue, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
"propCount and bufSize must be non-negative."));
return;
}
auto getProgramResourceiv = MG_Backend::gBackendFunctionsTable.GL.GetProgramResourceiv;
@@ -10,6 +10,7 @@
#include <MG_Util/Converters/GLToStr/GLEnumConverter.h>
#include <MG_Util/ShaderTranspiler/Types.h>
#include <MG_Util/ShaderTranspiler/ShaderCompiler.h>
#include <MG_Util/ShaderTranspiler/ShaderSourceProcessor.h>
#include <MG_Util/Converters/MGToGL/ProgramEnumConverter.h>
#include <MG_Util/Converters/SPIRVCrossToGL/SpvcTypeConverter.h>
@@ -389,10 +390,13 @@ namespace MobileGL::MG_State::GLState {
// 1. Compile shaders
for (SizeT i = 0; i < m_shaders.size(); i++) {
auto shaderType = MG_Util::ConvertShaderStageToGLEnum(m_shaders[i]->GetShaderStage());
auto shaderStage = m_shaders[i]->GetShaderStage();
auto shaderType = MG_Util::ConvertShaderStageToGLEnum(shaderStage);
String compileSource = m_shaders[i]->GetShaderSource();
PreprocessShaderSource(shaderStage, compileSource);
shaderTypes[i] = shaderType;
ShaderAttrib attrib{.shaderType = shaderType,
.sourceStr = m_shaders[i]->GetShaderSource(),
.sourceStr = compileSource,
.flags = 0}; // Will need patched glslang to work
MGLOG_D("ProgramObject %u: GenerateBinary - compiling shader[%zu] type %u", m_externalIndex, i, shaderType);
auto res = ShaderCompiler::CompileShader(attrib);
@@ -403,7 +407,7 @@ namespace MobileGL::MG_State::GLState {
MGLOG_E("ProgramObject %u: GenerateBinary - CompileShader return code %d, log:\n%s", m_externalIndex,
res.error().errc, res.error().log.c_str());
MGLOG_E("ProgramObject %u: GenerateBinary - last compiled shader src: \n%s", m_externalIndex,
m_shaders[i]->GetShaderSource().c_str());
compileSource.c_str());
}
MOBILEGL_ASSERT(res, "CompileShader failed during binary generation");
shaders[i] = res.value();
@@ -24,13 +24,14 @@ namespace MobileGL::MG_State::GLState {
void ShaderObject::Compile() {
using namespace MG_Util::ShaderTranspiler;
MG_Util::ShaderTranspiler::PreprocessShaderSource(m_stage, m_source);
String compileSource = m_source;
MG_Util::ShaderTranspiler::PreprocessShaderSource(m_stage, compileSource);
// Compile for OpenGL here, so that we can do validation and link
// like a real OpenGL driver at linking stage
// Will compile for other backends later.
ShaderAttrib attrib{.shaderType = MG_Util::ConvertShaderStageToGLEnum(m_stage),
.sourceStr = m_source,
.sourceStr = compileSource,
.flags = ShaderCompileBits::CompileForOpenGL};
auto result = ShaderCompiler::CompileShader(attrib);
@@ -42,7 +43,7 @@ namespace MobileGL::MG_State::GLState {
m_infoLog = result.error().log;
MGLOG_D("ShaderObject::Compile: Shader %d compilation failed.\nSource:\n%s\nInfoLog:\n%s\nSetting "
"m_compileStatus = false as a result.",
m_externalIndex, m_source.c_str(), m_infoLog.c_str());
m_externalIndex, compileSource.c_str(), m_infoLog.c_str());
}
}
+7 -1
View File
@@ -35,6 +35,12 @@ add_executable(
target_link_libraries(
SanityTest
GTest::gtest_main
MobileGL_s
)
target_include_directories(SanityTest PRIVATE
${MGL_ROOT}/include
${MGL_ROOT}/MobileGL
)
# message(STATUS "glslang_LIBRARIES: ${glslang_LIBRARIES}")
@@ -63,4 +69,4 @@ add_subdirectory(VertexArray)
add_subdirectory(Program)
if (ENABLE_INTEGRATION_TESTS)
add_subdirectory(Backend/DirectVulkan)
endif()
endif()
+90
View File
@@ -7,6 +7,9 @@
// End of Source File Header
#include <gtest/gtest.h>
#include <cstring>
#include <vector>
#include "Includes.h"
#include "Init.h"
#include "MG_Impl/GLImpl/Program/GL_Program.h"
@@ -113,6 +116,93 @@ TEST_F(ProgramTest, CompileFragment) {
CompileShader(fs);
}
TEST_F(ProgramTest, ShaderSourceKeepsOriginalTextAfterCompile) {
const char* part0 = R"(#define HIGHP_OR_DEFAULT highp
attribute vec4 Position;
varying vec2 uv;
)";
const char* ignored = "this segment should be ignored";
const char* part2 = R"(void main() {
uv = Position.xy;
gl_Position = Position;
}
)";
const GLchar* parts[] = {part0, ignored, part2};
const GLint lengths[] = {static_cast<GLint>(std::strlen(part0)), 0, static_cast<GLint>(std::strlen(part2))};
const String expectedSource = String(part0) + part2;
GLuint vs = CreateShader(GL_VERTEX_SHADER);
ShaderSource(vs, 3, parts, lengths);
GLint sourceLength = 0;
GetShaderiv(vs, GL_SHADER_SOURCE_LENGTH, &sourceLength);
ASSERT_EQ(sourceLength, static_cast<GLint>(expectedSource.size() + 1));
std::vector<GLchar> sourceBuffer(static_cast<size_t>(sourceLength));
GLsizei written = 0;
GetShaderSource(vs, sourceLength, &written, sourceBuffer.data());
EXPECT_EQ(written, static_cast<GLsizei>(expectedSource.size()));
EXPECT_EQ(String(sourceBuffer.data(), static_cast<size_t>(written)), expectedSource);
CompileShader(vs);
GLint compileStatus = GL_FALSE;
GetShaderiv(vs, GL_COMPILE_STATUS, &compileStatus);
ASSERT_EQ(compileStatus, GL_TRUE);
std::fill(sourceBuffer.begin(), sourceBuffer.end(), '\0');
written = 0;
GetShaderSource(vs, sourceLength, &written, sourceBuffer.data());
EXPECT_EQ(written, static_cast<GLsizei>(expectedSource.size()));
EXPECT_EQ(String(sourceBuffer.data(), static_cast<size_t>(written)), expectedSource);
}
TEST_F(ProgramTest, LinkProgramWithLegacyGlmarkStyleShaders) {
char infoLog[1024] = "";
const char* legacyVs = R"(attribute vec4 Position;
attribute vec2 TexCoord;
varying vec2 vTexCoord;
void main() {
vTexCoord = TexCoord;
gl_Position = Position;
}
)";
const char* legacyFs = R"(varying vec2 vTexCoord;
uniform sampler2D Texture;
void main() {
gl_FragColor = texture2D(Texture, vTexCoord);
}
)";
GLuint vs = CreateShader(GL_VERTEX_SHADER);
ShaderSource(vs, 1, &legacyVs, NULL);
CompileShader(vs);
GLint vsStatus = GL_FALSE;
GetShaderiv(vs, GL_COMPILE_STATUS, &vsStatus);
GetShaderInfoLog(vs, sizeof(infoLog), nullptr, infoLog);
ASSERT_EQ(vsStatus, GL_TRUE) << infoLog;
GLuint fs = CreateShader(GL_FRAGMENT_SHADER);
ShaderSource(fs, 1, &legacyFs, NULL);
CompileShader(fs);
GLint fsStatus = GL_FALSE;
GetShaderiv(fs, GL_COMPILE_STATUS, &fsStatus);
GetShaderInfoLog(fs, sizeof(infoLog), nullptr, infoLog);
ASSERT_EQ(fsStatus, GL_TRUE) << infoLog;
GLuint program = CreateProgram();
AttachShader(program, vs);
AttachShader(program, fs);
LinkProgram(program);
GLint linkStatus = GL_FALSE;
GetProgramiv(program, GL_LINK_STATUS, &linkStatus);
GetProgramInfoLog(program, sizeof(infoLog), nullptr, infoLog);
ASSERT_EQ(linkStatus, GL_TRUE) << infoLog;
}
TEST_F(ProgramTest, CompileAndLink) {
char infoLog[1024] = "";
@@ -8,6 +8,8 @@
#include <gtest/gtest.h>
#include <string>
#include "Includes.h"
#include "Init.h"
#include <MG_Util/Converters/GLToStr/GLEnumConverter.h>
@@ -29,6 +31,56 @@ TEST_F(ProgramUtilTest, Sanity) {
ASSERT_TRUE(true);
}
TEST_F(ProgramUtilTest, PreprocessLegacyVertexShaderModernizesGlmarkStyleSource) {
using namespace MG_Util::ShaderTranspiler;
String source = R"(#define HIGHP_OR_DEFAULT highp
attribute vec3 position;
varying vec2 uv;
uniform HIGHP_OR_DEFAULT mat4 modelViewProjection;
void main() {
uv = position.xy;
gl_Position = modelViewProjection * vec4(position, 1.0);
})";
PreprocessShaderSource(ShaderStage::Vertex, source);
EXPECT_EQ(source.find("#version 460 core\n"), 0);
EXPECT_NE(source.find("in vec3 position;"), String::npos);
EXPECT_NE(source.find("out vec2 uv;"), String::npos);
EXPECT_EQ(source.find("attribute"), String::npos);
EXPECT_EQ(source.find("varying"), String::npos);
EXPECT_EQ(source.find("HIGHP_OR_DEFAULT"), String::npos);
EXPECT_EQ(source.find("#define"), String::npos);
}
TEST_F(ProgramUtilTest, PreprocessLegacyFragmentShaderModernizesGlmarkStyleSource) {
using namespace MG_Util::ShaderTranspiler;
String source = R"(#define MEDIUMP_OR_DEFAULT mediump
varying vec2 uv;
uniform sampler2D texture0;
void main() {
MEDIUMP_OR_DEFAULT vec4 color = texture2D(texture0, uv);
gl_FragColor = color;
})";
PreprocessShaderSource(ShaderStage::Fragment, source);
EXPECT_EQ(source.find("#version 460 core\n"), 0);
EXPECT_NE(source.find("out vec4 mg_FragColor;\n"), String::npos);
EXPECT_NE(source.find("in vec2 uv;"), String::npos);
EXPECT_NE(source.find("texture(texture0, uv)"), String::npos);
EXPECT_NE(source.find("mg_FragColor = color;"), String::npos);
EXPECT_EQ(source.find("gl_FragColor"), String::npos);
EXPECT_EQ(source.find("texture2D"), String::npos);
EXPECT_EQ(source.find("MEDIUMP_OR_DEFAULT"), String::npos);
EXPECT_EQ(source.find("mediump"), String::npos);
EXPECT_EQ(source.find("#define"), String::npos);
}
const char* vs = R"(#version 150
in vec4 Position;
+57 -1
View File
@@ -7,10 +7,66 @@
// End of Source File Header
#include <gtest/gtest.h>
#include <algorithm>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <string>
#include <MG_Backend/DirectGLES/BackendObject_DirectGLES.h>
#include <MG_Util/Debug/Log.h>
namespace {
void SetEnvVar(const char* name, const char* value) {
#if defined(_WIN32)
_putenv_s(name, value);
#else
setenv(name, value, 1);
#endif
}
void UnsetEnvVar(const char* name) {
#if defined(_WIN32)
_putenv_s(name, "");
#else
unsetenv(name);
#endif
}
} // namespace
TEST(Sanity, BasicAssertions) {
// Expect two strings not to be equal.
EXPECT_STRNE("hello", "world");
// Expect equality.
EXPECT_EQ(7 * 6, 42);
}
}
TEST(DirectGLESSanity, AdvertisesDepthTextureForGlmarkShadowScenes) {
MobileGL::MG_Backend::DirectGLES::BackendObject_DirectGLES backend;
const auto& extensions = backend.GetRendererInfo().RendererGLInfo.Extensions;
EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_depth_texture), extensions.end());
}
TEST(LogSanity, UsesEnvOverrideForFilePath) {
namespace fs = std::filesystem;
MobileGL::MG_Util::Debug::Close();
const fs::path logPath = fs::temp_directory_path() / "mobilegl-log-env-override-test.log";
const std::string message = "mobilegl-log-env-override-regression";
fs::remove(logPath);
SetEnvVar("MOBILEGL_LOG_FILE_PATH", logPath.string().c_str());
MobileGL::MG_Util::Debug::Log("INFO", ANDROID_LOG_INFO, "%s", message.c_str());
MobileGL::MG_Util::Debug::Close();
UnsetEnvVar("MOBILEGL_LOG_FILE_PATH");
std::ifstream logFile(logPath);
ASSERT_TRUE(logFile.good());
const std::string contents((std::istreambuf_iterator<char>(logFile)), std::istreambuf_iterator<char>());
EXPECT_NE(contents.find(message), std::string::npos);
fs::remove(logPath);
}
@@ -318,6 +318,51 @@ TEST_F(GeneralVertexArrayTest, General_IndexBufferBinding) {
EXPECT_EQ(GetError(), GL_NO_ERROR);
}
TEST_F(GeneralVertexArrayTest, General_ElementArrayBufferBindingIsVaoLocalAndZeroUnbinds) {
GLuint vao1 = CreateVAO();
GLuint ebo1;
GenBuffers(1, &ebo1);
BindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo1);
GLint binding = -1;
GetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &binding);
EXPECT_EQ(binding, static_cast<GLint>(ebo1));
auto vaoObj1 = MG_State::pGLContext->GetVertexArrayObject(vao1);
ASSERT_NE(vaoObj1, nullptr);
EXPECT_EQ(vaoObj1->GetIndexBufferBindingSlot().GetBoundObject(), MG_State::pGLContext->GetBufferObject(ebo1));
GLuint vao2 = CreateVAO();
auto vaoObj2 = MG_State::pGLContext->GetVertexArrayObject(vao2);
ASSERT_NE(vaoObj2, nullptr);
GetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &binding);
EXPECT_EQ(binding, 0);
EXPECT_EQ(vaoObj2->GetIndexBufferBindingSlot().GetBoundObject(), nullptr);
BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
EXPECT_EQ(GetError(), GL_NO_ERROR);
EXPECT_EQ(MG_State::pGLContext->GetBufferObject(0), nullptr);
EXPECT_EQ(IsBuffer(0), GL_FALSE);
BindVertexArray(vao1);
GetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &binding);
EXPECT_EQ(binding, static_cast<GLint>(ebo1));
BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
GetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &binding);
EXPECT_EQ(binding, 0);
EXPECT_EQ(vaoObj1->GetIndexBufferBindingSlot().GetBoundObject(), nullptr);
EXPECT_EQ(MG_State::pGLContext->GetBufferObject(0), nullptr);
EXPECT_EQ(IsBuffer(0), GL_FALSE);
DeleteVertexArrays(1, &vao1);
DeleteVertexArrays(1, &vao2);
DeleteBuffers(1, &ebo1);
EXPECT_EQ(GetError(), GL_NO_ERROR);
}
TEST_F(GeneralVertexArrayTest, General_IntegerAttributes) {
GLuint vao = CreateVAO();
GLuint vbo = CreateVBO(GL_ARRAY_BUFFER, 128);
+9 -3
View File
@@ -58,8 +58,14 @@ namespace MobileGL {
void InitFile() {
#if MOBILEGL_LOG_ENABLE_FILE
if (!s_logFile && MOBILEGL_LOG_FILE_PATH && *MOBILEGL_LOG_FILE_PATH) {
s_logFile = std::fopen(MOBILEGL_LOG_FILE_PATH, "w");
if (!s_logFile) {
const char* logPath = std::getenv("MOBILEGL_LOG_FILE_PATH");
if (!logPath || !*logPath) {
logPath = MOBILEGL_LOG_FILE_PATH;
}
if (logPath && *logPath) {
s_logFile = std::fopen(logPath, "w");
}
}
#endif
}
@@ -118,4 +124,4 @@ namespace MobileGL {
#endif
}
} // namespace MG_Util::Debug
} // namespace MobileGL
} // namespace MobileGL
@@ -29,8 +29,7 @@ namespace {
while (functionPos != MobileGL::String::npos && functionPos < lineEnd) {
const bool hasLeftBoundary = functionPos == 0 || !IsIdentifierChar(source[functionPos - 1]);
const SizeT functionEnd = functionPos + functionName.size();
const bool hasRightBoundary =
functionEnd >= source.size() || !IsIdentifierChar(source[functionEnd]);
const bool hasRightBoundary = functionEnd >= source.size() || !IsIdentifierChar(source[functionEnd]);
if (hasLeftBoundary && hasRightBoundary) {
SizeT probe = functionEnd;
while (probe < lineEnd && std::isspace(static_cast<unsigned char>(source[probe]))) {
@@ -89,6 +88,108 @@ namespace {
RenameFunctionInvocations(source, fromName, to);
}
void ReplaceIdentifier(MobileGL::String& source, const MobileGL::String& from, const MobileGL::String& to) {
SizeT pos = 0;
while ((pos = source.find(from, pos)) != MobileGL::String::npos) {
const bool hasLeftBoundary = pos == 0 || !IsIdentifierChar(source[pos - 1]);
const SizeT end = pos + from.size();
const bool hasRightBoundary = end >= source.size() || !IsIdentifierChar(source[end]);
if (hasLeftBoundary && hasRightBoundary) {
source.replace(pos, from.size(), to);
pos += to.size();
} else {
pos = end;
}
}
}
void RemoveDefineForIdentifier(MobileGL::String& source, const MobileGL::String& identifier) {
SizeT lineStart = 0;
while (lineStart < source.size()) {
SizeT lineEnd = source.find('\n', lineStart);
const bool hasLineBreak = lineEnd != MobileGL::String::npos;
if (!hasLineBreak) {
lineEnd = source.size();
}
SizeT probe = lineStart;
while (probe < lineEnd && std::isspace(static_cast<unsigned char>(source[probe]))) {
probe++;
}
if (probe < lineEnd && source[probe] == '#') {
probe++;
while (probe < lineEnd && std::isspace(static_cast<unsigned char>(source[probe]))) {
probe++;
}
constexpr const char* defineToken = "define";
constexpr SizeT defineLen = 6;
const bool hasDefine = probe + defineLen <= lineEnd &&
source.compare(probe, defineLen, defineToken) == 0 &&
(probe + defineLen == lineEnd ||
!IsIdentifierChar(source[probe + defineLen]));
if (hasDefine) {
probe += defineLen;
while (probe < lineEnd && std::isspace(static_cast<unsigned char>(source[probe]))) {
probe++;
}
const bool hasIdentifier = probe + identifier.size() <= lineEnd &&
source.compare(probe, identifier.size(), identifier) == 0 &&
(probe + identifier.size() == lineEnd ||
!IsIdentifierChar(source[probe + identifier.size()]));
if (hasIdentifier) {
source.erase(lineStart, lineEnd - lineStart + (hasLineBreak ? 1 : 0));
continue;
}
}
}
lineStart = lineEnd + (hasLineBreak ? 1 : 0);
}
}
SizeT FindAfterVersionDirective(const MobileGL::String& source) {
const SizeT versionPos = source.find("#version");
if (versionPos == MobileGL::String::npos) {
return 0;
}
const SizeT lineEnd = source.find('\n', versionPos);
return lineEnd == MobileGL::String::npos ? source.size() : lineEnd + 1;
}
void ModernizeLegacyGLSL(MobileGL::ShaderStage stage, MobileGL::String& source) {
RemoveDefineForIdentifier(source, "HIGHP_OR_DEFAULT");
RemoveDefineForIdentifier(source, "MEDIUMP_OR_DEFAULT");
RemoveDefineForIdentifier(source, "LOWP_OR_DEFAULT");
ReplaceIdentifier(source, "HIGHP_OR_DEFAULT", "");
ReplaceIdentifier(source, "MEDIUMP_OR_DEFAULT", "");
ReplaceIdentifier(source, "LOWP_OR_DEFAULT", "");
ReplaceIdentifier(source, "highp", "");
ReplaceIdentifier(source, "mediump", "");
ReplaceIdentifier(source, "lowp", "");
ReplaceIdentifier(source, "texture2D", "texture");
ReplaceIdentifier(source, "texture2DProj", "textureProj");
ReplaceIdentifier(source, "textureCube", "texture");
ReplaceIdentifier(source, "texture3D", "texture");
if (stage == MobileGL::ShaderStage::Vertex) {
ReplaceIdentifier(source, "attribute", "in");
ReplaceIdentifier(source, "varying", "out");
return;
}
if (stage == MobileGL::ShaderStage::Fragment) {
ReplaceIdentifier(source, "varying", "in");
const bool usesFragColor = source.find("gl_FragColor") != MobileGL::String::npos;
if (usesFragColor) {
ReplaceIdentifier(source, "gl_FragColor", "mg_FragColor");
source.insert(FindAfterVersionDirective(source), "out vec4 mg_FragColor;\n");
}
}
}
} // namespace
namespace MobileGL {
@@ -145,7 +246,8 @@ namespace MobileGL {
} else {
profile = ShaderProfile::Core;
source.insert(0, "#version 460 core\n");
return;
versionPos = 0;
lineEnd = source.find('\n', versionPos);
}
SizeT firstLineEnd = lineEnd;
@@ -169,6 +271,7 @@ namespace MobileGL {
RenameBuiltinShadowingFunction(source, "round", "mg_round");
RenameBuiltinShadowingFunction(source, "tanh", "mg_tanh");
RenameBuiltinShadowingFunction(source, "fma", "mg_fma");
ModernizeLegacyGLSL(stage, source);
}
} // namespace ShaderTranspiler