mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 20:28:32 +09:00
[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:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user