mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-16 06:23:23 +09:00
[Feat] (GL): implement GL_ARB_shader_objects over the core's shared shader / program name space.
This commit is contained in:
@@ -428,6 +428,7 @@ set(SOURCE_FILES
|
||||
MobileGL/MG_Impl/GLImpl/Framebuffer/Validators.cpp
|
||||
MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp
|
||||
MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp
|
||||
MobileGL/MG_Impl/GLImpl/Program/GL_ShaderObjectsARB.cpp
|
||||
MobileGL/MG_Impl/GLImpl/Program/ProgramInterface.cpp
|
||||
MobileGL/MG_Impl/GLImpl/Program/GL_ProgramPipeline.cpp
|
||||
MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp
|
||||
|
||||
@@ -1307,17 +1307,6 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, GetnMinmaxARB, GLenum target, GLboolean rese
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, FramebufferSampleLocationsfvARB, GLenum target, GLuint start, GLsizei count, const GLfloat* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, FramebufferSampleLocationsfvARB, target, start, count, v)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferSampleLocationsfvARB, GLuint framebuffer, GLuint start, GLsizei count, const GLfloat* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferSampleLocationsfvARB, framebuffer, start, count, v)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, EvaluateDepthValuesARB, void) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, EvaluateDepthValuesARB, )
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteObjectARB, GLhandleARB obj) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DeleteObjectARB, obj)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(GLhandleARB, GetHandleARB, GLenum pname) DECLARE_GL_FUNCTION_STUB_END(GLhandleARB, GetHandleARB, pname)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, DetachObjectARB, GLhandleARB containerObj, GLhandleARB attachedObj) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DetachObjectARB, containerObj, attachedObj)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(GLhandleARB, CreateShaderObjectARB, GLenum shaderType) DECLARE_GL_FUNCTION_STUB_END(GLhandleARB, CreateShaderObjectARB, shaderType)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(GLhandleARB, CreateProgramObjectARB, void) DECLARE_GL_FUNCTION_STUB_END(GLhandleARB, CreateProgramObjectARB, )
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, AttachObjectARB, GLhandleARB containerObj, GLhandleARB obj) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, AttachObjectARB, containerObj, obj)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, UseProgramObjectARB, GLhandleARB programObj) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, UseProgramObjectARB, programObj)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetObjectParameterfvARB, GLhandleARB obj, GLenum pname, GLfloat* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetObjectParameterfvARB, obj, pname, params)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetObjectParameterivARB, GLhandleARB obj, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetObjectParameterivARB, obj, pname, params)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetInfoLogARB, GLhandleARB obj, GLsizei maxLength, GLsizei* length, GLcharARB* infoLog) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetInfoLogARB, obj, maxLength, length, infoLog)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetAttachedObjectsARB, GLhandleARB containerObj, GLsizei maxCount, GLsizei* count, GLhandleARB* obj) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetAttachedObjectsARB, containerObj, maxCount, count, obj)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedStringARB, GLenum type, GLint namelen, const GLchar* name, GLint strinen, const GLchar* string) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedStringARB, type, namelen, name, strinen, string)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteNamedStringARB, GLint namelen, const GLchar* name) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DeleteNamedStringARB, namelen, name)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, CompileShaderIncludeARB, GLuint shader, GLsizei count, const GLchar* const* path, const GLint* length) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CompileShaderIncludeARB, shader, count, path, length)
|
||||
@@ -2996,6 +2985,165 @@ MOBILEGL_GL_API void glGetnUniformdvARB(GLuint program, GLint location, GLsizei
|
||||
glGetnUniformdv(program, location, bufSize, params);
|
||||
}
|
||||
|
||||
// ---- GL_ARB_shader_objects --------------------------------------------------------------------
|
||||
// A handle is a name of the core's one shared shader/program name space: CreateShaderObjectARB and
|
||||
// CreateProgramObjectARB hand out exactly what glCreateShader / glCreateProgram would, so an ARB
|
||||
// handle works with every core call and a core name with every ARB call. The calls whose meaning
|
||||
// is the core one under another name forward to the core entry point; the ones that ask which
|
||||
// kind of object a handle names (DeleteObjectARB, GetHandleARB, GetObjectParameter*ARB,
|
||||
// GetInfoLogARB) are GLImpl's (Program/GL_ShaderObjectsARB.cpp). GLhandleARB is GLuint everywhere
|
||||
// but Apple, where the header makes it a pointer - hence the two conversions.
|
||||
namespace {
|
||||
inline GLuint ArbName(GLhandleARB handle) {
|
||||
#if defined(__APPLE__)
|
||||
return static_cast<GLuint>(reinterpret_cast<uintptr_t>(handle));
|
||||
#else
|
||||
return handle;
|
||||
#endif
|
||||
}
|
||||
inline GLhandleARB ArbHandle(GLuint name) {
|
||||
#if defined(__APPLE__)
|
||||
return reinterpret_cast<GLhandleARB>(static_cast<uintptr_t>(name));
|
||||
#else
|
||||
return name;
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
||||
|
||||
MOBILEGL_GL_API GLhandleARB glCreateShaderObjectARB(GLenum shaderType) { return ArbHandle(glCreateShader(shaderType)); }
|
||||
|
||||
MOBILEGL_GL_API GLhandleARB glCreateProgramObjectARB(void) { return ArbHandle(glCreateProgram()); }
|
||||
|
||||
MOBILEGL_GL_API void glDeleteObjectARB(GLhandleARB obj) {
|
||||
const MobileGL::MG_Impl::EGLImpl::GLStreamScope mglStreamScope;
|
||||
MobileGL::MG_Impl::GLImpl::DeleteObjectARB(ArbName(obj));
|
||||
}
|
||||
|
||||
MOBILEGL_GL_API GLhandleARB glGetHandleARB(GLenum pname) {
|
||||
const MobileGL::MG_Impl::EGLImpl::GLStreamScope mglStreamScope;
|
||||
return ArbHandle(MobileGL::MG_Impl::GLImpl::GetHandleARB(pname));
|
||||
}
|
||||
|
||||
MOBILEGL_GL_API void glDetachObjectARB(GLhandleARB containerObj, GLhandleARB attachedObj) {
|
||||
glDetachShader(ArbName(containerObj), ArbName(attachedObj));
|
||||
}
|
||||
|
||||
MOBILEGL_GL_API void glAttachObjectARB(GLhandleARB containerObj, GLhandleARB obj) {
|
||||
glAttachShader(ArbName(containerObj), ArbName(obj));
|
||||
}
|
||||
|
||||
MOBILEGL_GL_API void glShaderSourceARB(GLhandleARB shaderObj, GLsizei count, const GLcharARB** string,
|
||||
const GLint* length) {
|
||||
glShaderSource(ArbName(shaderObj), count, string, length);
|
||||
}
|
||||
|
||||
MOBILEGL_GL_API void glCompileShaderARB(GLhandleARB shaderObj) { glCompileShader(ArbName(shaderObj)); }
|
||||
|
||||
MOBILEGL_GL_API void glLinkProgramARB(GLhandleARB programObj) { glLinkProgram(ArbName(programObj)); }
|
||||
|
||||
MOBILEGL_GL_API void glUseProgramObjectARB(GLhandleARB programObj) { glUseProgram(ArbName(programObj)); }
|
||||
|
||||
MOBILEGL_GL_API void glValidateProgramARB(GLhandleARB programObj) { glValidateProgram(ArbName(programObj)); }
|
||||
|
||||
MOBILEGL_GL_API void glGetObjectParameterfvARB(GLhandleARB obj, GLenum pname, GLfloat* params) {
|
||||
const MobileGL::MG_Impl::EGLImpl::GLStreamScope mglStreamScope;
|
||||
MobileGL::MG_Impl::GLImpl::GetObjectParameterfvARB(ArbName(obj), pname, params);
|
||||
}
|
||||
|
||||
MOBILEGL_GL_API void glGetObjectParameterivARB(GLhandleARB obj, GLenum pname, GLint* params) {
|
||||
const MobileGL::MG_Impl::EGLImpl::GLStreamScope mglStreamScope;
|
||||
MobileGL::MG_Impl::GLImpl::GetObjectParameterivARB(ArbName(obj), pname, params);
|
||||
}
|
||||
|
||||
MOBILEGL_GL_API void glGetInfoLogARB(GLhandleARB obj, GLsizei maxLength, GLsizei* length, GLcharARB* infoLog) {
|
||||
const MobileGL::MG_Impl::EGLImpl::GLStreamScope mglStreamScope;
|
||||
MobileGL::MG_Impl::GLImpl::GetInfoLogARB(ArbName(obj), maxLength, length, infoLog);
|
||||
}
|
||||
|
||||
MOBILEGL_GL_API void glGetAttachedObjectsARB(GLhandleARB containerObj, GLsizei maxCount, GLsizei* count,
|
||||
GLhandleARB* obj) {
|
||||
#if defined(__APPLE__)
|
||||
Vector<GLuint> names(maxCount > 0 ? static_cast<SizeT>(maxCount) : 0);
|
||||
GLsizei written = 0;
|
||||
glGetAttachedShaders(ArbName(containerObj), maxCount, &written, names.empty() ? nullptr : names.data());
|
||||
if (obj != nullptr) {
|
||||
for (GLsizei i = 0; i < written; ++i) obj[i] = ArbHandle(names[static_cast<SizeT>(i)]);
|
||||
}
|
||||
if (count != nullptr) *count = written;
|
||||
#else
|
||||
glGetAttachedShaders(ArbName(containerObj), maxCount, count, obj);
|
||||
#endif
|
||||
}
|
||||
|
||||
MOBILEGL_GL_API GLint glGetUniformLocationARB(GLhandleARB programObj, const GLcharARB* name) {
|
||||
return glGetUniformLocation(ArbName(programObj), name);
|
||||
}
|
||||
|
||||
MOBILEGL_GL_API void glGetActiveUniformARB(GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei* length,
|
||||
GLint* size, GLenum* type, GLcharARB* name) {
|
||||
glGetActiveUniform(ArbName(programObj), index, maxLength, length, size, type, name);
|
||||
}
|
||||
|
||||
MOBILEGL_GL_API void glGetUniformfvARB(GLhandleARB programObj, GLint location, GLfloat* params) {
|
||||
glGetUniformfv(ArbName(programObj), location, params);
|
||||
}
|
||||
|
||||
MOBILEGL_GL_API void glGetUniformivARB(GLhandleARB programObj, GLint location, GLint* params) {
|
||||
glGetUniformiv(ArbName(programObj), location, params);
|
||||
}
|
||||
|
||||
MOBILEGL_GL_API void glGetShaderSourceARB(GLhandleARB obj, GLsizei maxLength, GLsizei* length, GLcharARB* source) {
|
||||
glGetShaderSource(ArbName(obj), maxLength, length, source);
|
||||
}
|
||||
|
||||
MOBILEGL_GL_API void glUniform1fARB(GLint location, GLfloat v0) { glUniform1f(location, v0); }
|
||||
MOBILEGL_GL_API void glUniform2fARB(GLint location, GLfloat v0, GLfloat v1) { glUniform2f(location, v0, v1); }
|
||||
MOBILEGL_GL_API void glUniform3fARB(GLint location, GLfloat v0, GLfloat v1, GLfloat v2) {
|
||||
glUniform3f(location, v0, v1, v2);
|
||||
}
|
||||
MOBILEGL_GL_API void glUniform4fARB(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) {
|
||||
glUniform4f(location, v0, v1, v2, v3);
|
||||
}
|
||||
MOBILEGL_GL_API void glUniform1iARB(GLint location, GLint v0) { glUniform1i(location, v0); }
|
||||
MOBILEGL_GL_API void glUniform2iARB(GLint location, GLint v0, GLint v1) { glUniform2i(location, v0, v1); }
|
||||
MOBILEGL_GL_API void glUniform3iARB(GLint location, GLint v0, GLint v1, GLint v2) { glUniform3i(location, v0, v1, v2); }
|
||||
MOBILEGL_GL_API void glUniform4iARB(GLint location, GLint v0, GLint v1, GLint v2, GLint v3) {
|
||||
glUniform4i(location, v0, v1, v2, v3);
|
||||
}
|
||||
MOBILEGL_GL_API void glUniform1fvARB(GLint location, GLsizei count, const GLfloat* value) {
|
||||
glUniform1fv(location, count, value);
|
||||
}
|
||||
MOBILEGL_GL_API void glUniform2fvARB(GLint location, GLsizei count, const GLfloat* value) {
|
||||
glUniform2fv(location, count, value);
|
||||
}
|
||||
MOBILEGL_GL_API void glUniform3fvARB(GLint location, GLsizei count, const GLfloat* value) {
|
||||
glUniform3fv(location, count, value);
|
||||
}
|
||||
MOBILEGL_GL_API void glUniform4fvARB(GLint location, GLsizei count, const GLfloat* value) {
|
||||
glUniform4fv(location, count, value);
|
||||
}
|
||||
MOBILEGL_GL_API void glUniform1ivARB(GLint location, GLsizei count, const GLint* value) {
|
||||
glUniform1iv(location, count, value);
|
||||
}
|
||||
MOBILEGL_GL_API void glUniform2ivARB(GLint location, GLsizei count, const GLint* value) {
|
||||
glUniform2iv(location, count, value);
|
||||
}
|
||||
MOBILEGL_GL_API void glUniform3ivARB(GLint location, GLsizei count, const GLint* value) {
|
||||
glUniform3iv(location, count, value);
|
||||
}
|
||||
MOBILEGL_GL_API void glUniform4ivARB(GLint location, GLsizei count, const GLint* value) {
|
||||
glUniform4iv(location, count, value);
|
||||
}
|
||||
MOBILEGL_GL_API void glUniformMatrix2fvARB(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) {
|
||||
glUniformMatrix2fv(location, count, transpose, value);
|
||||
}
|
||||
MOBILEGL_GL_API void glUniformMatrix3fvARB(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) {
|
||||
glUniformMatrix3fv(location, count, transpose, value);
|
||||
}
|
||||
MOBILEGL_GL_API void glUniformMatrix4fvARB(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) {
|
||||
glUniformMatrix4fv(location, count, transpose, value);
|
||||
}
|
||||
|
||||
// KHR_robustness and EXT_robustness name the same entry points with their own suffixes (an ES
|
||||
// application, ANGLE's GL backend among them, looks them up by those names).
|
||||
MOBILEGL_GL_API GLenum glGetGraphicsResetStatusKHR(void) {
|
||||
|
||||
@@ -197,4 +197,12 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
void TransformFeedbackVaryings(GLuint program, GLsizei count, const GLchar* const* varyings, GLenum bufferMode);
|
||||
void GetTransformFeedbackVarying(GLuint program, GLuint index, GLsizei bufSize, GLsizei* length, GLsizei* size,
|
||||
GLenum* type, GLchar* name);
|
||||
|
||||
// GL_ARB_shader_objects (GL_ShaderObjectsARB.cpp): the entry points that ask which kind of object
|
||||
// a handle names. The rest of the extension is the core calls under ARB names (Definitions.cpp).
|
||||
void DeleteObjectARB(GLuint obj);
|
||||
GLuint GetHandleARB(GLenum pname);
|
||||
void GetObjectParameterivARB(GLuint obj, GLenum pname, GLint* params);
|
||||
void GetObjectParameterfvARB(GLuint obj, GLenum pname, GLfloat* params);
|
||||
void GetInfoLogARB(GLuint obj, GLsizei maxLength, GLsizei* length, GLchar* infoLog);
|
||||
} // namespace MobileGL::MG_Impl::GLImpl
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
// MobileGL - MobileGL/MG_Impl/GLImpl/Program/GL_ShaderObjectsARB.cpp
|
||||
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||
// Licensed under the GNU Lesser General Public License v3.0:
|
||||
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// End of Source File Header
|
||||
|
||||
// GL_ARB_shader_objects: the entry points whose meaning is not just "the core call under another
|
||||
// name". A handle IS a shader or program name of the core's one shared shader/program name space
|
||||
// (GL 4.6 §7.1 / §7.3): glCreateShaderObjectARB and glCreateProgramObjectARB hand out exactly what
|
||||
// glCreateShader / glCreateProgram would, so every core call takes an ARB handle and every ARB call
|
||||
// takes a core name. What this file adds is the part of the extension that asks "which kind of
|
||||
// object is this handle" - deleting, querying and reading the info log of either kind through one
|
||||
// entry point - and the one query of the current program by handle.
|
||||
|
||||
#include "GL_Program.h"
|
||||
#include "../Getter/GL_Getter.h"
|
||||
|
||||
#include <MG_State/GLState/Core.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace MobileGL::MG_Impl::GLImpl {
|
||||
|
||||
namespace {
|
||||
// The ARB tokens that have no core spelling. The rest of GetObjectParameter's pnames ARE the
|
||||
// core values (OBJECT_DELETE_STATUS_ARB == GL_DELETE_STATUS ... 0x8B80-0x8B88, and
|
||||
// ARB_vertex_shader's two attribute counts 0x8B89 / 0x8B8A).
|
||||
constexpr GLenum kProgramObjectArb = 0x8B40;
|
||||
constexpr GLenum kShaderObjectArb = 0x8B48;
|
||||
constexpr GLenum kObjectTypeArb = 0x8B4E;
|
||||
constexpr GLenum kObjectSubtypeArb = 0x8B4F;
|
||||
|
||||
enum class ObjectKind { None, Program, Shader };
|
||||
|
||||
ObjectKind KindOf(GLuint obj) {
|
||||
if (obj == 0) return ObjectKind::None;
|
||||
if (IsProgram(obj)) return ObjectKind::Program;
|
||||
if (IsShader(obj)) return ObjectKind::Shader;
|
||||
return ObjectKind::None;
|
||||
}
|
||||
|
||||
void Fail(ErrorCode code, const char* func, const std::string& what) {
|
||||
MG_State::pGLContext->RecordError(code, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", func, what));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// DeleteObjectARB deletes whichever kind the handle names, with the core's deferred-deletion
|
||||
// rules (a program in use, a shader still attached). Zero is ignored, as for glDeleteShader /
|
||||
// glDeleteProgram; any other name that is not a shader or program object is INVALID_VALUE.
|
||||
void DeleteObjectARB(GLuint obj) {
|
||||
if (obj == 0) return;
|
||||
switch (KindOf(obj)) {
|
||||
case ObjectKind::Program: DeleteProgram(obj); return;
|
||||
case ObjectKind::Shader: DeleteShader(obj); return;
|
||||
case ObjectKind::None: break;
|
||||
}
|
||||
Fail(ErrorCode::InvalidValue, __func__, std::to_string(obj) + " is not a shader or program object.");
|
||||
}
|
||||
|
||||
// GetHandleARB(PROGRAM_OBJECT_ARB) is the program object in use - GL_CURRENT_PROGRAM, 0 when
|
||||
// none. It is the only pname the extension defines.
|
||||
GLuint GetHandleARB(GLenum pname) {
|
||||
if (pname != kProgramObjectArb) {
|
||||
Fail(ErrorCode::InvalidEnum, __func__, "pname must be GL_PROGRAM_OBJECT_ARB.");
|
||||
return 0;
|
||||
}
|
||||
GLint current = 0;
|
||||
GetIntegerv(GL_CURRENT_PROGRAM, ¤t);
|
||||
return static_cast<GLuint>(current);
|
||||
}
|
||||
|
||||
namespace {
|
||||
// The ARB spec's table of pnames, per kind. An unknown pname is INVALID_ENUM; a known one the
|
||||
// object's kind does not have (COMPILE_STATUS of a program, LINK_STATUS of a shader, SUBTYPE
|
||||
// of a program) is INVALID_OPERATION; a handle that names no shader or program is
|
||||
// INVALID_VALUE. True when `*out` holds the answer (every pname here is a single value).
|
||||
bool QueryObjectParameter(GLuint obj, GLenum pname, GLint* out, const char* func) {
|
||||
bool programParam = false, shaderParam = false;
|
||||
switch (pname) {
|
||||
case kObjectTypeArb:
|
||||
case GL_DELETE_STATUS:
|
||||
case GL_INFO_LOG_LENGTH: programParam = shaderParam = true; break;
|
||||
case kObjectSubtypeArb:
|
||||
case GL_COMPILE_STATUS:
|
||||
case GL_SHADER_SOURCE_LENGTH: shaderParam = true; break;
|
||||
case GL_LINK_STATUS:
|
||||
case GL_VALIDATE_STATUS:
|
||||
case GL_ATTACHED_SHADERS:
|
||||
case GL_ACTIVE_UNIFORMS:
|
||||
case GL_ACTIVE_UNIFORM_MAX_LENGTH:
|
||||
case GL_ACTIVE_ATTRIBUTES:
|
||||
case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH: programParam = true; break;
|
||||
default: Fail(ErrorCode::InvalidEnum, func, "pname is not a GetObjectParameterARB parameter."); return false;
|
||||
}
|
||||
const ObjectKind kind = KindOf(obj);
|
||||
if (kind == ObjectKind::None) {
|
||||
Fail(ErrorCode::InvalidValue, func, std::to_string(obj) + " is not a shader or program object.");
|
||||
return false;
|
||||
}
|
||||
if ((kind == ObjectKind::Program && !programParam) || (kind == ObjectKind::Shader && !shaderParam)) {
|
||||
Fail(ErrorCode::InvalidOperation, func, "pname is not a parameter of this kind of object.");
|
||||
return false;
|
||||
}
|
||||
if (pname == kObjectTypeArb) {
|
||||
*out = static_cast<GLint>(kind == ObjectKind::Program ? kProgramObjectArb : kShaderObjectArb);
|
||||
} else if (pname == kObjectSubtypeArb) {
|
||||
GetShaderiv(obj, GL_SHADER_TYPE, out);
|
||||
} else if (kind == ObjectKind::Program) {
|
||||
GetProgramiv(obj, pname, out);
|
||||
} else {
|
||||
GetShaderiv(obj, pname, out);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void GetObjectParameterivARB(GLuint obj, GLenum pname, GLint* params) {
|
||||
GLint value = 0;
|
||||
if (QueryObjectParameter(obj, pname, &value, __func__) && params != nullptr) *params = value;
|
||||
}
|
||||
|
||||
void GetObjectParameterfvARB(GLuint obj, GLenum pname, GLfloat* params) {
|
||||
GLint value = 0;
|
||||
if (QueryObjectParameter(obj, pname, &value, __func__) && params != nullptr) *params = static_cast<GLfloat>(value);
|
||||
}
|
||||
|
||||
void GetInfoLogARB(GLuint obj, GLsizei maxLength, GLsizei* length, GLchar* infoLog) {
|
||||
switch (KindOf(obj)) {
|
||||
case ObjectKind::Program: GetProgramInfoLog(obj, maxLength, length, infoLog); return;
|
||||
case ObjectKind::Shader: GetShaderInfoLog(obj, maxLength, length, infoLog); return;
|
||||
case ObjectKind::None: break;
|
||||
}
|
||||
Fail(ErrorCode::InvalidValue, __func__, std::to_string(obj) + " is not a shader or program object.");
|
||||
}
|
||||
|
||||
} // namespace MobileGL::MG_Impl::GLImpl
|
||||
@@ -824,6 +824,46 @@ namespace MobileGL::MG_Impl {
|
||||
GETPROC(glMultiDrawArraysIndirectCount, name);
|
||||
GETPROC(glMultiDrawElementsIndirectCount, name);
|
||||
GETPROC(glPolygonOffsetClamp, name);
|
||||
// GL_ARB_shader_objects (Definitions.cpp): advertised, so every entry point resolves.
|
||||
GETPROC(glDeleteObjectARB, name);
|
||||
GETPROC(glGetHandleARB, name);
|
||||
GETPROC(glDetachObjectARB, name);
|
||||
GETPROC(glCreateShaderObjectARB, name);
|
||||
GETPROC(glShaderSourceARB, name);
|
||||
GETPROC(glCompileShaderARB, name);
|
||||
GETPROC(glCreateProgramObjectARB, name);
|
||||
GETPROC(glAttachObjectARB, name);
|
||||
GETPROC(glLinkProgramARB, name);
|
||||
GETPROC(glUseProgramObjectARB, name);
|
||||
GETPROC(glValidateProgramARB, name);
|
||||
GETPROC(glUniform1fARB, name);
|
||||
GETPROC(glUniform2fARB, name);
|
||||
GETPROC(glUniform3fARB, name);
|
||||
GETPROC(glUniform4fARB, name);
|
||||
GETPROC(glUniform1iARB, name);
|
||||
GETPROC(glUniform2iARB, name);
|
||||
GETPROC(glUniform3iARB, name);
|
||||
GETPROC(glUniform4iARB, name);
|
||||
GETPROC(glUniform1fvARB, name);
|
||||
GETPROC(glUniform2fvARB, name);
|
||||
GETPROC(glUniform3fvARB, name);
|
||||
GETPROC(glUniform4fvARB, name);
|
||||
GETPROC(glUniform1ivARB, name);
|
||||
GETPROC(glUniform2ivARB, name);
|
||||
GETPROC(glUniform3ivARB, name);
|
||||
GETPROC(glUniform4ivARB, name);
|
||||
GETPROC(glUniformMatrix2fvARB, name);
|
||||
GETPROC(glUniformMatrix3fvARB, name);
|
||||
GETPROC(glUniformMatrix4fvARB, name);
|
||||
GETPROC(glGetObjectParameterfvARB, name);
|
||||
GETPROC(glGetObjectParameterivARB, name);
|
||||
GETPROC(glGetInfoLogARB, name);
|
||||
GETPROC(glGetAttachedObjectsARB, name);
|
||||
GETPROC(glGetUniformLocationARB, name);
|
||||
GETPROC(glGetActiveUniformARB, name);
|
||||
GETPROC(glGetUniformfvARB, name);
|
||||
GETPROC(glGetUniformivARB, name);
|
||||
GETPROC(glGetShaderSourceARB, name);
|
||||
GETPROC(glPrimitiveBoundingBoxARB, name);
|
||||
GETPROC(glGetTextureHandleARB, name);
|
||||
GETPROC(glGetTextureSamplerHandleARB, name);
|
||||
|
||||
@@ -71,6 +71,7 @@ add_executable(MobileGLIntegrationTest
|
||||
Scenarios/SyncWireScenario.cpp
|
||||
Scenarios/DrawParametersScenario.cpp
|
||||
Scenarios/AsyncCompileScenario.cpp
|
||||
Scenarios/ArbShaderObjectsScenario.cpp
|
||||
Scenarios/XfbAfterClipDistanceScenario.cpp
|
||||
Scenarios/UnwrittenPositionOutputScenario.cpp
|
||||
Scenarios/SampleMaskScopeScenario.cpp
|
||||
@@ -2466,6 +2467,9 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
|
||||
endmacro()
|
||||
|
||||
mgl_itest_register_split_arms("ClearThenReadPixelsScenario.*" "")
|
||||
# GL_ARB_shader_objects (P13): ARB-created shader / program objects travel the record arm like
|
||||
# core-created ones, so the scenario runs on every split arm as well as both monoliths.
|
||||
mgl_itest_register_split_arms("ArbShaderObjectsScenario.*" "")
|
||||
mgl_itest_register_split_arms("TriangleScenario.*" "")
|
||||
mgl_itest_register_split_arms("PersistentCoherentMapScenario.*" "")
|
||||
# P11 A2 (MG_Remote/CONTRACT-P11.md §2): the >= 16 MiB arena on all three arms. Its tier case
|
||||
|
||||
@@ -0,0 +1,466 @@
|
||||
// MobileGL - MobileGL/MG_IntegrationTest/Scenarios/ArbShaderObjectsScenario.cpp
|
||||
// Copyright (c) 2026 MobileGL-Dev
|
||||
// Licensed under the GNU Lesser General Public License v3.0:
|
||||
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// End of Source File Header
|
||||
//
|
||||
// Scenario - GL_ARB_shader_objects, which MobileGL advertises (KWin asks for it) and whose entry
|
||||
// points were stubs until P13: glCreateShaderObjectARB / glCreateProgramObjectARB returned 1.
|
||||
//
|
||||
// WHAT IS PINNED:
|
||||
// * an ARB handle is a name of the core's ONE shared shader/program name space (GL 4.6 §7.1): it
|
||||
// never collides with a core-created name, and core and ARB calls take each other's names;
|
||||
// * a program built entirely through ARB calls - mixed with core calls on the same objects -
|
||||
// links, is made current, takes a uniform and draws the right pixels;
|
||||
// * the extension's own queries: GetHandleARB(PROGRAM_OBJECT_ARB) is the current program,
|
||||
// GetObjectParameter*ARB answers OBJECT_TYPE / OBJECT_SUBTYPE and the core status pnames per
|
||||
// kind, GetInfoLogARB / GetAttachedObjectsARB read either kind, and DeleteObjectARB deletes
|
||||
// either kind with the core's deferred-deletion rules;
|
||||
// * the errors the extension names (INVALID_ENUM / INVALID_OPERATION / INVALID_VALUE);
|
||||
// * every advertised entry point resolves through eglGetProcAddress (glvnd and libepoxy find
|
||||
// them that way, not by symbol);
|
||||
// * and, because shader/program names share one space while every glGen* family has its own,
|
||||
// the per-type glGen* name spaces: a generated name is reserved but is no object until its
|
||||
// first bind (where the spec says so), name 0 is never handed out, and a deleted name is
|
||||
// free again.
|
||||
//
|
||||
// ARMS: both backends, monolith and every split arm (the record arm carries shader and program
|
||||
// objects by {slot, gen} whichever entry point created them).
|
||||
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../Harness/HeadlessGL.h"
|
||||
#include "../Harness/ScenarioFixture.h"
|
||||
|
||||
#ifdef GLAPI
|
||||
#undef GLAPI
|
||||
#endif
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glcorearb.h>
|
||||
#undef GL_GLEXT_PROTOTYPES
|
||||
|
||||
#include <EGL/egl.h>
|
||||
|
||||
// The extension's own tokens, spelled out: glcorearb.h has none of them.
|
||||
#define MGITEST_PROGRAM_OBJECT_ARB 0x8B40
|
||||
#define MGITEST_SHADER_OBJECT_ARB 0x8B48
|
||||
#define MGITEST_OBJECT_TYPE_ARB 0x8B4E
|
||||
#define MGITEST_OBJECT_SUBTYPE_ARB 0x8B4F
|
||||
|
||||
// GLhandleARB is GLuint everywhere but Apple, which this module does not build for. Resolved by
|
||||
// the linker straight into MobileGL, like every other gl* call here.
|
||||
extern "C" {
|
||||
GLuint glCreateShaderObjectARB(GLenum shaderType);
|
||||
GLuint glCreateProgramObjectARB(void);
|
||||
void glDeleteObjectARB(GLuint obj);
|
||||
GLuint glGetHandleARB(GLenum pname);
|
||||
void glDetachObjectARB(GLuint containerObj, GLuint attachedObj);
|
||||
void glAttachObjectARB(GLuint containerObj, GLuint obj);
|
||||
void glShaderSourceARB(GLuint shaderObj, GLsizei count, const char** string, const GLint* length);
|
||||
void glCompileShaderARB(GLuint shaderObj);
|
||||
void glLinkProgramARB(GLuint programObj);
|
||||
void glUseProgramObjectARB(GLuint programObj);
|
||||
void glValidateProgramARB(GLuint programObj);
|
||||
void glUniform4fARB(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
|
||||
void glGetObjectParameterivARB(GLuint obj, GLenum pname, GLint* params);
|
||||
void glGetObjectParameterfvARB(GLuint obj, GLenum pname, GLfloat* params);
|
||||
void glGetInfoLogARB(GLuint obj, GLsizei maxLength, GLsizei* length, char* infoLog);
|
||||
void glGetAttachedObjectsARB(GLuint containerObj, GLsizei maxCount, GLsizei* count, GLuint* obj);
|
||||
GLint glGetUniformLocationARB(GLuint programObj, const char* name);
|
||||
void glGetUniformfvARB(GLuint programObj, GLint location, GLfloat* params);
|
||||
void glGetShaderSourceARB(GLuint obj, GLsizei maxLength, GLsizei* length, char* source);
|
||||
}
|
||||
|
||||
namespace MGITest {
|
||||
namespace {
|
||||
|
||||
// A full-viewport triangle from gl_VertexID: no attribute, so the draw depends on nothing
|
||||
// but the program and its uniform.
|
||||
constexpr const char* kVertexSource = R"(#version 330 core
|
||||
void main() {
|
||||
vec2 p = vec2(float((gl_VertexID & 1) << 2) - 1.0, float((gl_VertexID & 2) << 1) - 1.0);
|
||||
gl_Position = vec4(p, 0.0, 1.0);
|
||||
}
|
||||
)";
|
||||
constexpr const char* kFragmentSource = R"(#version 330 core
|
||||
uniform vec4 uColor;
|
||||
out vec4 oColor;
|
||||
void main() { oColor = uColor; }
|
||||
)";
|
||||
|
||||
constexpr int kEdge = 16;
|
||||
|
||||
class ArbShaderObjectsScenario : public ScenarioTest {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
ScenarioTest::SetUp();
|
||||
if (!Ready()) return;
|
||||
m_target = MakeColorFbo(kEdge, kEdge);
|
||||
ASSERT_NE(m_target.fbo, 0u) << "could not create the render target";
|
||||
glGenVertexArrays(1, &m_vao);
|
||||
FirstGLError();
|
||||
}
|
||||
void TearDown() override {
|
||||
if (Ready()) {
|
||||
glUseProgram(0);
|
||||
if (m_vao != 0) glDeleteVertexArrays(1, &m_vao);
|
||||
DestroyColorFbo(m_target);
|
||||
}
|
||||
ScenarioTest::TearDown();
|
||||
}
|
||||
|
||||
// A shader built entirely through the extension, its compile status read both ways.
|
||||
GLuint ArbShader(GLenum type, const char* source) {
|
||||
const GLuint shader = glCreateShaderObjectARB(type);
|
||||
EXPECT_NE(shader, 0u);
|
||||
glShaderSourceARB(shader, 1, &source, nullptr);
|
||||
glCompileShaderARB(shader);
|
||||
GLint arbStatus = 0, coreStatus = 0;
|
||||
glGetObjectParameterivARB(shader, GL_COMPILE_STATUS, &arbStatus);
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &coreStatus);
|
||||
if (arbStatus != GL_TRUE) {
|
||||
char log[1024] = {};
|
||||
glGetInfoLogARB(shader, sizeof(log), nullptr, log);
|
||||
ADD_FAILURE() << "ARB compile failed: " << log;
|
||||
}
|
||||
EXPECT_EQ(arbStatus, coreStatus) << "the ARB and core status queries disagree";
|
||||
return shader;
|
||||
}
|
||||
|
||||
void DrawInto() {
|
||||
BindFbo(m_target);
|
||||
ClearTo(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glBindVertexArray(m_vao);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
}
|
||||
|
||||
ColorFbo m_target;
|
||||
GLuint m_vao = 0;
|
||||
};
|
||||
|
||||
TEST_F(ArbShaderObjectsScenario, EveryEntryPointResolvesThroughGetProcAddress) {
|
||||
if (!Ready()) return;
|
||||
const char* names[] = {
|
||||
"glDeleteObjectARB", "glGetHandleARB", "glDetachObjectARB", "glCreateShaderObjectARB",
|
||||
"glShaderSourceARB", "glCompileShaderARB", "glCreateProgramObjectARB", "glAttachObjectARB",
|
||||
"glLinkProgramARB", "glUseProgramObjectARB", "glValidateProgramARB", "glUniform1fARB",
|
||||
"glUniform2fARB", "glUniform3fARB", "glUniform4fARB", "glUniform1iARB", "glUniform2iARB",
|
||||
"glUniform3iARB", "glUniform4iARB", "glUniform1fvARB", "glUniform2fvARB", "glUniform3fvARB",
|
||||
"glUniform4fvARB", "glUniform1ivARB", "glUniform2ivARB", "glUniform3ivARB", "glUniform4ivARB",
|
||||
"glUniformMatrix2fvARB", "glUniformMatrix3fvARB", "glUniformMatrix4fvARB",
|
||||
"glGetObjectParameterfvARB", "glGetObjectParameterivARB", "glGetInfoLogARB",
|
||||
"glGetAttachedObjectsARB", "glGetUniformLocationARB", "glGetActiveUniformARB",
|
||||
"glGetUniformfvARB", "glGetUniformivARB", "glGetShaderSourceARB"};
|
||||
for (const char* name : names) {
|
||||
EXPECT_NE(reinterpret_cast<void*>(eglGetProcAddress(name)), nullptr) << name << " does not resolve";
|
||||
}
|
||||
EXPECT_EQ(reinterpret_cast<void*>(eglGetProcAddress("glCreateShaderObjectARB")),
|
||||
reinterpret_cast<void*>(&glCreateShaderObjectARB))
|
||||
<< "eglGetProcAddress hands out a different glCreateShaderObjectARB than the library exports";
|
||||
}
|
||||
|
||||
// One name space for shaders and programs, whichever entry point created them; core and
|
||||
// ARB calls take each other's names, and OBJECT_TYPE / OBJECT_SUBTYPE say which kind.
|
||||
TEST_F(ArbShaderObjectsScenario, ArbAndCoreHandlesShareOneNameSpace) {
|
||||
if (!Ready()) return;
|
||||
const GLuint coreShader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
const GLuint arbShader = glCreateShaderObjectARB(GL_VERTEX_SHADER);
|
||||
const GLuint coreProgram = glCreateProgram();
|
||||
const GLuint arbProgram = glCreateProgramObjectARB();
|
||||
ASSERT_EQ(FirstGLError(), 0u);
|
||||
const std::set<GLuint> names{coreShader, arbShader, coreProgram, arbProgram};
|
||||
EXPECT_EQ(names.size(), 4u) << "an ARB handle collided with a core-created name";
|
||||
EXPECT_EQ(names.count(0u), 0u) << "a create handed out name 0";
|
||||
|
||||
EXPECT_EQ(glIsShader(arbShader), GL_TRUE);
|
||||
EXPECT_EQ(glIsProgram(arbProgram), GL_TRUE);
|
||||
EXPECT_EQ(glIsProgram(arbShader), GL_FALSE);
|
||||
EXPECT_EQ(glIsShader(arbProgram), GL_FALSE);
|
||||
GLint value = 0;
|
||||
glGetShaderiv(arbShader, GL_SHADER_TYPE, &value);
|
||||
EXPECT_EQ(value, GL_VERTEX_SHADER);
|
||||
glGetObjectParameterivARB(coreShader, MGITEST_OBJECT_SUBTYPE_ARB, &value);
|
||||
EXPECT_EQ(value, GL_FRAGMENT_SHADER);
|
||||
glGetObjectParameterivARB(coreShader, MGITEST_OBJECT_TYPE_ARB, &value);
|
||||
EXPECT_EQ(value, MGITEST_SHADER_OBJECT_ARB);
|
||||
glGetObjectParameterivARB(arbProgram, MGITEST_OBJECT_TYPE_ARB, &value);
|
||||
EXPECT_EQ(value, MGITEST_PROGRAM_OBJECT_ARB);
|
||||
GLfloat asFloat = 0.0f;
|
||||
glGetObjectParameterfvARB(arbShader, MGITEST_OBJECT_SUBTYPE_ARB, &asFloat);
|
||||
EXPECT_EQ(asFloat, static_cast<GLfloat>(GL_VERTEX_SHADER));
|
||||
EXPECT_EQ(FirstGLError(), 0u);
|
||||
|
||||
// Attachments cross the two spellings too.
|
||||
glAttachShader(arbProgram, coreShader);
|
||||
glAttachObjectARB(coreProgram, arbShader);
|
||||
GLuint attached[4] = {};
|
||||
GLsizei count = 0;
|
||||
glGetAttachedObjectsARB(arbProgram, 4, &count, attached);
|
||||
ASSERT_EQ(count, 1);
|
||||
EXPECT_EQ(attached[0], coreShader);
|
||||
glGetAttachedShaders(coreProgram, 4, &count, attached);
|
||||
ASSERT_EQ(count, 1);
|
||||
EXPECT_EQ(attached[0], arbShader);
|
||||
glGetObjectParameterivARB(coreProgram, GL_ATTACHED_SHADERS, &value);
|
||||
EXPECT_EQ(value, 1);
|
||||
glDetachObjectARB(arbProgram, coreShader);
|
||||
glGetProgramiv(arbProgram, GL_ATTACHED_SHADERS, &value);
|
||||
EXPECT_EQ(value, 0);
|
||||
EXPECT_EQ(FirstGLError(), 0u);
|
||||
|
||||
glDeleteObjectARB(coreShader);
|
||||
glDeleteShader(arbShader); // attached to coreProgram: flagged, not yet gone
|
||||
glDeleteObjectARB(arbProgram);
|
||||
glDeleteProgram(coreProgram); // takes arbShader with it
|
||||
EXPECT_EQ(glIsShader(coreShader), GL_FALSE);
|
||||
EXPECT_EQ(glIsProgram(arbProgram), GL_FALSE);
|
||||
EXPECT_EQ(glIsProgram(coreProgram), GL_FALSE);
|
||||
EXPECT_EQ(glIsShader(arbShader), GL_FALSE);
|
||||
EXPECT_EQ(FirstGLError(), 0u);
|
||||
}
|
||||
|
||||
// A program built through the extension, mixed with core calls on the same objects, draws.
|
||||
TEST_F(ArbShaderObjectsScenario, AnArbProgramMixedWithCoreCallsDraws) {
|
||||
if (!Ready()) return;
|
||||
const GLuint vs = ArbShader(GL_VERTEX_SHADER, kVertexSource);
|
||||
const GLuint fs = ArbShader(GL_FRAGMENT_SHADER, kFragmentSource);
|
||||
const GLuint program = glCreateProgramObjectARB();
|
||||
glAttachObjectARB(program, vs);
|
||||
glAttachShader(program, fs); // core call on an ARB handle
|
||||
glLinkProgramARB(program);
|
||||
GLint linked = 0;
|
||||
glGetProgramiv(program, GL_LINK_STATUS, &linked);
|
||||
if (linked != GL_TRUE) {
|
||||
char log[1024] = {};
|
||||
glGetInfoLogARB(program, sizeof(log), nullptr, log);
|
||||
FAIL() << "ARB link failed: " << log;
|
||||
}
|
||||
GLint arbLinked = 0;
|
||||
glGetObjectParameterivARB(program, GL_LINK_STATUS, &arbLinked);
|
||||
EXPECT_EQ(arbLinked, GL_TRUE);
|
||||
glValidateProgramARB(program);
|
||||
|
||||
glUseProgramObjectARB(program);
|
||||
EXPECT_EQ(glGetHandleARB(MGITEST_PROGRAM_OBJECT_ARB), program);
|
||||
GLint current = 0;
|
||||
glGetIntegerv(GL_CURRENT_PROGRAM, ¤t);
|
||||
EXPECT_EQ(static_cast<GLuint>(current), program);
|
||||
const GLint arbLocation = glGetUniformLocationARB(program, "uColor");
|
||||
EXPECT_EQ(arbLocation, glGetUniformLocation(program, "uColor"));
|
||||
ASSERT_GE(arbLocation, 0);
|
||||
glUniform4fARB(arbLocation, 0.0f, 1.0f, 0.0f, 1.0f);
|
||||
GLfloat readBack[4] = {};
|
||||
glGetUniformfvARB(program, arbLocation, readBack);
|
||||
EXPECT_EQ(readBack[1], 1.0f);
|
||||
ASSERT_EQ(FirstGLError(), 0u);
|
||||
DrawInto();
|
||||
const Image green = ReadPixels(kEdge, kEdge);
|
||||
EXPECT_TRUE(RegionIsMostly(green, 0, kEdge - 1, 0, kEdge - 1, "green", 0.0, "ARB program, ARB uniform"));
|
||||
|
||||
// The same program through the core spelling of the uniform, then released by the core
|
||||
// call and observed through the ARB query.
|
||||
glUniform4f(glGetUniformLocation(program, "uColor"), 0.0f, 0.0f, 1.0f, 1.0f);
|
||||
DrawInto();
|
||||
const Image blue = ReadPixels(kEdge, kEdge);
|
||||
EXPECT_TRUE(RegionIsMostly(blue, 0, kEdge - 1, 0, kEdge - 1, "blue", 0.0, "core uniform on an ARB program"));
|
||||
glUseProgram(0);
|
||||
EXPECT_EQ(glGetHandleARB(MGITEST_PROGRAM_OBJECT_ARB), 0u);
|
||||
|
||||
char source[512] = {};
|
||||
GLsizei sourceLength = 0;
|
||||
glGetShaderSourceARB(fs, sizeof(source), &sourceLength, source);
|
||||
EXPECT_EQ(std::string(source), std::string(kFragmentSource));
|
||||
glDeleteObjectARB(vs);
|
||||
glDeleteObjectARB(fs);
|
||||
glDeleteObjectARB(program);
|
||||
EXPECT_EQ(FirstGLError(), 0u);
|
||||
}
|
||||
|
||||
// DeleteObjectARB follows the core's deferred deletion: a program in use and a shader still
|
||||
// attached are flagged (DELETE_STATUS) and stay objects until released.
|
||||
TEST_F(ArbShaderObjectsScenario, DeleteObjectArbDefersLikeTheCore) {
|
||||
if (!Ready()) return;
|
||||
const GLuint vs = ArbShader(GL_VERTEX_SHADER, kVertexSource);
|
||||
const GLuint fs = ArbShader(GL_FRAGMENT_SHADER, kFragmentSource);
|
||||
const GLuint program = glCreateProgramObjectARB();
|
||||
glAttachObjectARB(program, vs);
|
||||
glAttachObjectARB(program, fs);
|
||||
glLinkProgramARB(program);
|
||||
glUseProgramObjectARB(program);
|
||||
glDeleteObjectARB(program);
|
||||
glDeleteObjectARB(vs);
|
||||
GLint status = 0;
|
||||
EXPECT_EQ(glIsProgram(program), GL_TRUE) << "a program in use was deleted at once";
|
||||
glGetObjectParameterivARB(program, GL_DELETE_STATUS, &status);
|
||||
EXPECT_EQ(status, GL_TRUE);
|
||||
EXPECT_EQ(glIsShader(vs), GL_TRUE) << "an attached shader was deleted at once";
|
||||
glGetObjectParameterivARB(vs, GL_DELETE_STATUS, &status);
|
||||
EXPECT_EQ(status, GL_TRUE);
|
||||
EXPECT_EQ(FirstGLError(), 0u);
|
||||
glUseProgramObjectARB(0);
|
||||
EXPECT_EQ(glIsProgram(program), GL_FALSE) << "releasing the flagged program did not delete it";
|
||||
EXPECT_EQ(glIsShader(vs), GL_FALSE) << "deleting the program did not take its flagged shader";
|
||||
EXPECT_EQ(glIsShader(fs), GL_TRUE);
|
||||
glDeleteObjectARB(fs);
|
||||
EXPECT_EQ(glIsShader(fs), GL_FALSE);
|
||||
EXPECT_EQ(FirstGLError(), 0u);
|
||||
}
|
||||
|
||||
TEST_F(ArbShaderObjectsScenario, TheExtensionsErrorsAreTheSpecs) {
|
||||
if (!Ready()) return;
|
||||
const GLuint shader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER);
|
||||
const GLuint program = glCreateProgramObjectARB();
|
||||
GLint value = -7;
|
||||
EXPECT_EQ(glGetHandleARB(GL_SHADER_TYPE), 0u);
|
||||
EXPECT_EQ(FirstGLError(), static_cast<unsigned>(GL_INVALID_ENUM)) << "GetHandleARB(bad pname)";
|
||||
glGetObjectParameterivARB(program, GL_COMPILE_STATUS, &value);
|
||||
EXPECT_EQ(FirstGLError(), static_cast<unsigned>(GL_INVALID_OPERATION)) << "COMPILE_STATUS of a program";
|
||||
glGetObjectParameterivARB(shader, GL_LINK_STATUS, &value);
|
||||
EXPECT_EQ(FirstGLError(), static_cast<unsigned>(GL_INVALID_OPERATION)) << "LINK_STATUS of a shader";
|
||||
glGetObjectParameterivARB(program, MGITEST_OBJECT_SUBTYPE_ARB, &value);
|
||||
EXPECT_EQ(FirstGLError(), static_cast<unsigned>(GL_INVALID_OPERATION)) << "OBJECT_SUBTYPE of a program";
|
||||
glGetObjectParameterivARB(program, GL_TEXTURE_2D, &value);
|
||||
EXPECT_EQ(FirstGLError(), static_cast<unsigned>(GL_INVALID_ENUM)) << "an unknown pname";
|
||||
EXPECT_EQ(value, -7) << "a refused query wrote its output";
|
||||
const GLuint stranger = shader + program + 1000u;
|
||||
glGetObjectParameterivARB(stranger, MGITEST_OBJECT_TYPE_ARB, &value);
|
||||
EXPECT_EQ(FirstGLError(), static_cast<unsigned>(GL_INVALID_VALUE)) << "a name that is no object";
|
||||
glDeleteObjectARB(stranger);
|
||||
EXPECT_EQ(FirstGLError(), static_cast<unsigned>(GL_INVALID_VALUE)) << "DeleteObjectARB(no object)";
|
||||
glDeleteObjectARB(0);
|
||||
EXPECT_EQ(FirstGLError(), 0u) << "DeleteObjectARB(0) is ignored";
|
||||
char log[8] = {};
|
||||
glGetInfoLogARB(stranger, sizeof(log), nullptr, log);
|
||||
EXPECT_EQ(FirstGLError(), static_cast<unsigned>(GL_INVALID_VALUE)) << "GetInfoLogARB(no object)";
|
||||
glDeleteObjectARB(shader);
|
||||
glDeleteObjectARB(program);
|
||||
EXPECT_EQ(FirstGLError(), 0u);
|
||||
}
|
||||
|
||||
// Names freed by DeleteObjectARB may come back from either create, and never while live.
|
||||
TEST_F(ArbShaderObjectsScenario, FreedNamesAreReusedWithoutCollision) {
|
||||
if (!Ready()) return;
|
||||
std::set<GLuint> live;
|
||||
std::vector<GLuint> freed;
|
||||
for (int round = 0; round < 4; ++round) {
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
const GLuint names[4] = {glCreateShaderObjectARB(GL_VERTEX_SHADER), glCreateShader(GL_FRAGMENT_SHADER),
|
||||
glCreateProgramObjectARB(), glCreateProgram()};
|
||||
for (GLuint name : names) {
|
||||
EXPECT_NE(name, 0u);
|
||||
EXPECT_TRUE(live.insert(name).second) << "name " << name << " handed out while live";
|
||||
}
|
||||
}
|
||||
// Free half of them, through both spellings.
|
||||
int k = 0;
|
||||
for (auto it = live.begin(); it != live.end();) {
|
||||
if ((k++ & 1) == 0) {
|
||||
if (k & 2) {
|
||||
glDeleteObjectARB(*it);
|
||||
} else if (glIsProgram(*it)) {
|
||||
glDeleteProgram(*it);
|
||||
} else {
|
||||
glDeleteShader(*it);
|
||||
}
|
||||
freed.push_back(*it);
|
||||
it = live.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (GLuint name : live) EXPECT_TRUE(glIsShader(name) || glIsProgram(name)) << name;
|
||||
for (GLuint name : live) glDeleteObjectARB(name);
|
||||
EXPECT_EQ(FirstGLError(), 0u);
|
||||
}
|
||||
|
||||
// THE OTHER NAME SPACES, which the extension does not touch: each glGen* family has its own,
|
||||
// a generated name is reserved but is not an object until its first bind (samplers are the
|
||||
// exception - glGenSamplers creates them), name 0 is never generated, and a deleted name is
|
||||
// no object any more.
|
||||
TEST_F(ArbShaderObjectsScenario, GenNameSpacesAreSeparateAndBindCreates) {
|
||||
if (!Ready()) return;
|
||||
const GLuint shader = glCreateShader(GL_VERTEX_SHADER);
|
||||
GLuint buffer = 0, texture = 0, vao = 0, fbo = 0, rbo = 0, sampler = 0, query = 0, pipeline = 0, xfb = 0;
|
||||
glGenBuffers(1, &buffer);
|
||||
glGenTextures(1, &texture);
|
||||
glGenVertexArrays(1, &vao);
|
||||
glGenFramebuffers(1, &fbo);
|
||||
glGenRenderbuffers(1, &rbo);
|
||||
glGenSamplers(1, &sampler);
|
||||
glGenQueries(1, &query);
|
||||
glGenProgramPipelines(1, &pipeline);
|
||||
glGenTransformFeedbacks(1, &xfb);
|
||||
ASSERT_EQ(FirstGLError(), 0u);
|
||||
for (GLuint name : {buffer, texture, vao, fbo, rbo, sampler, query, pipeline, xfb}) EXPECT_NE(name, 0u);
|
||||
// Reserved, not yet objects.
|
||||
EXPECT_EQ(glIsBuffer(buffer), GL_FALSE);
|
||||
EXPECT_EQ(glIsTexture(texture), GL_FALSE);
|
||||
EXPECT_EQ(glIsVertexArray(vao), GL_FALSE);
|
||||
EXPECT_EQ(glIsFramebuffer(fbo), GL_FALSE);
|
||||
EXPECT_EQ(glIsRenderbuffer(rbo), GL_FALSE);
|
||||
EXPECT_EQ(glIsSampler(sampler), GL_TRUE) << "glGenSamplers creates its objects";
|
||||
EXPECT_EQ(glIsQuery(query), GL_FALSE);
|
||||
EXPECT_EQ(glIsProgramPipeline(pipeline), GL_FALSE);
|
||||
EXPECT_EQ(glIsTransformFeedback(xfb), GL_FALSE);
|
||||
// The first bind creates.
|
||||
glBindBuffer(GL_ARRAY_BUFFER, buffer);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glBindVertexArray(vao);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
|
||||
glBindProgramPipeline(pipeline);
|
||||
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, xfb);
|
||||
glBeginQuery(GL_SAMPLES_PASSED, query);
|
||||
glEndQuery(GL_SAMPLES_PASSED);
|
||||
ASSERT_EQ(FirstGLError(), 0u);
|
||||
EXPECT_EQ(glIsBuffer(buffer), GL_TRUE);
|
||||
EXPECT_EQ(glIsTexture(texture), GL_TRUE);
|
||||
EXPECT_EQ(glIsVertexArray(vao), GL_TRUE);
|
||||
EXPECT_EQ(glIsFramebuffer(fbo), GL_TRUE);
|
||||
EXPECT_EQ(glIsRenderbuffer(rbo), GL_TRUE);
|
||||
EXPECT_EQ(glIsQuery(query), GL_TRUE);
|
||||
EXPECT_EQ(glIsProgramPipeline(pipeline), GL_TRUE);
|
||||
EXPECT_EQ(glIsTransformFeedback(xfb), GL_TRUE);
|
||||
// Separate spaces: being a buffer says nothing about being a shader, and the reverse.
|
||||
EXPECT_EQ(glIsShader(buffer), buffer == shader ? GL_TRUE : GL_FALSE);
|
||||
EXPECT_EQ(glIsBuffer(shader), shader == buffer ? GL_TRUE : GL_FALSE);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glBindVertexArray(0);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
glBindProgramPipeline(0);
|
||||
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
|
||||
glDeleteBuffers(1, &buffer);
|
||||
glDeleteTextures(1, &texture);
|
||||
glDeleteVertexArrays(1, &vao);
|
||||
glDeleteFramebuffers(1, &fbo);
|
||||
glDeleteRenderbuffers(1, &rbo);
|
||||
glDeleteSamplers(1, &sampler);
|
||||
glDeleteQueries(1, &query);
|
||||
glDeleteProgramPipelines(1, &pipeline);
|
||||
glDeleteTransformFeedbacks(1, &xfb);
|
||||
ASSERT_EQ(FirstGLError(), 0u);
|
||||
EXPECT_EQ(glIsBuffer(buffer), GL_FALSE);
|
||||
EXPECT_EQ(glIsTexture(texture), GL_FALSE);
|
||||
EXPECT_EQ(glIsVertexArray(vao), GL_FALSE);
|
||||
EXPECT_EQ(glIsFramebuffer(fbo), GL_FALSE);
|
||||
EXPECT_EQ(glIsRenderbuffer(rbo), GL_FALSE);
|
||||
EXPECT_EQ(glIsSampler(sampler), GL_FALSE);
|
||||
EXPECT_EQ(glIsQuery(query), GL_FALSE);
|
||||
EXPECT_EQ(glIsProgramPipeline(pipeline), GL_FALSE);
|
||||
EXPECT_EQ(glIsTransformFeedback(xfb), GL_FALSE);
|
||||
EXPECT_EQ(glIsShader(shader), GL_TRUE) << "deleting other kinds' names touched a shader";
|
||||
glDeleteShader(shader);
|
||||
EXPECT_EQ(FirstGLError(), 0u);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace MGITest
|
||||
Reference in New Issue
Block a user