Route shader creation through renderer GL

This commit is contained in:
2026-06-03 07:03:50 +02:00
parent acdaf3bb8e
commit 1ae79ab3c1
6 changed files with 1123 additions and 67 deletions

View File

@@ -228,8 +228,11 @@ Known local toolchain state:
names directly. It also owns the PanoPainter shader uniform catalog and legacy hash
mapping used by `Shader` active-uniform discovery and the uniform uniqueness
check. Legacy `Shader` program use/delete, uniform writes, and
attribute-location lookup now consume tested dispatch contracts here;
compile/link and active-uniform discovery are still retained legacy GL calls.
attribute-location lookup now consume tested dispatch contracts here.
Legacy shader source compilation, shader deletion, program attach/link,
attribute rebinding, active-uniform count/enumeration, and uniform-location
discovery now consume tested dispatch contracts as well, leaving the retained
shader utility with thin GL adapter functions.
App OpenGL initialization debug severity, debug output, GL info string,
renderer API viewport/scissor rect conversion, default depth/program-point/
line-smooth state, blend factor/equation, and UI render-target RGBA8 format

View File

@@ -1120,9 +1120,11 @@ Results:
names used by `Shader`, preserves the legacy hash ids, and rejects empty,
unnamed, null-name, mismatched-hash, and duplicate-name catalogs.
Legacy `Shader` program use/delete, uniform writes, and attribute-location
lookups now execute through tested `pp_renderer_gl` dispatch contracts;
shader compile/link and active-uniform discovery remain the next shader
boundary work.
lookups now execute through tested `pp_renderer_gl` dispatch contracts.
Legacy shader source compilation, shader deletion, program attach/link,
attribute rebinding, active-uniform count/enumeration, and uniform-location
discovery also execute through tested `pp_renderer_gl` dispatch contracts,
leaving only thin GL adapter functions in the retained `Shader` utility.
- `pp_renderer_gl_command_plan_tests` covers the headless OpenGL command
planner for recorded render-pass clear masks/values, viewport/scissor state,
blend/depth/sampler state, texture format mapping, mesh/draw primitive modes,

View File

@@ -1,4 +1,5 @@
#include "renderer_gl/opengl_capabilities.h"
#include "renderer_gl/shader_bindings.h"
#include <array>
#include <limits>
@@ -1030,6 +1031,190 @@ pp::foundation::Result<std::int32_t> get_opengl_attribute_location(
dispatch.get_attrib_location(program_id, attribute_name));
}
pp::foundation::Result<OpenGlShaderCompileInfo> compile_opengl_shader_source(
std::uint32_t stage,
const char* source,
char* info_log,
std::int32_t info_log_capacity,
OpenGlShaderCompileDispatch dispatch) noexcept
{
if (dispatch.create_shader == nullptr
|| dispatch.shader_source == nullptr
|| dispatch.compile_shader == nullptr
|| dispatch.get_shader_integer == nullptr
|| dispatch.get_shader_info_log == nullptr) {
return pp::foundation::Result<OpenGlShaderCompileInfo>::failure(
pp::foundation::Status::invalid_argument("OpenGL shader compile dispatch callbacks must not be null"));
}
if (stage == 0U || source == nullptr || source[0] == '\0' || info_log == nullptr || info_log_capacity <= 0) {
return pp::foundation::Result<OpenGlShaderCompileInfo>::failure(
pp::foundation::Status::invalid_argument("OpenGL shader compile parameters are invalid"));
}
const auto shader_id = dispatch.create_shader(stage);
if (shader_id == 0U) {
return pp::foundation::Result<OpenGlShaderCompileInfo>::failure(
pp::foundation::Status::out_of_range("OpenGL shader allocation returned id 0"));
}
const char* const sources[] { source };
dispatch.shader_source(shader_id, 1, sources);
dispatch.compile_shader(shader_id);
OpenGlShaderCompileInfo info {
.shader_id = shader_id,
};
dispatch.get_shader_integer(shader_id, shader_compile_status_query(), &info.compile_status);
dispatch.get_shader_info_log(shader_id, info_log_capacity, &info.info_log_length, info_log);
return pp::foundation::Result<OpenGlShaderCompileInfo>::success(info);
}
pp::foundation::Status delete_opengl_shader(
std::uint32_t shader_id,
OpenGlShaderDeleteDispatch dispatch) noexcept
{
if (dispatch.delete_shader == nullptr) {
return pp::foundation::Status::invalid_argument("OpenGL shader delete dispatch callback must not be null");
}
if (shader_id == 0U) {
return pp::foundation::Status::success();
}
dispatch.delete_shader(shader_id);
return pp::foundation::Status::success();
}
pp::foundation::Result<OpenGlProgramLinkInfo> link_opengl_shader_program(
std::uint32_t vertex_shader_id,
std::uint32_t fragment_shader_id,
std::span<const OpenGlAttributeBinding> attribute_bindings,
char* info_log,
std::int32_t info_log_capacity,
OpenGlProgramLinkDispatch dispatch) noexcept
{
if (dispatch.create_program == nullptr
|| dispatch.attach_shader == nullptr
|| dispatch.delete_shader == nullptr
|| dispatch.link_program == nullptr
|| dispatch.get_attrib_location == nullptr
|| dispatch.bind_attrib_location == nullptr
|| dispatch.get_program_integer == nullptr
|| dispatch.get_program_info_log == nullptr) {
return pp::foundation::Result<OpenGlProgramLinkInfo>::failure(
pp::foundation::Status::invalid_argument("OpenGL program link dispatch callbacks must not be null"));
}
if (vertex_shader_id == 0U
|| fragment_shader_id == 0U
|| attribute_bindings.empty()
|| info_log == nullptr
|| info_log_capacity <= 0) {
return pp::foundation::Result<OpenGlProgramLinkInfo>::failure(
pp::foundation::Status::invalid_argument("OpenGL program link parameters are invalid"));
}
const auto program_id = dispatch.create_program();
if (program_id == 0U) {
return pp::foundation::Result<OpenGlProgramLinkInfo>::failure(
pp::foundation::Status::out_of_range("OpenGL program allocation returned id 0"));
}
dispatch.attach_shader(program_id, vertex_shader_id);
dispatch.attach_shader(program_id, fragment_shader_id);
dispatch.delete_shader(vertex_shader_id);
dispatch.delete_shader(fragment_shader_id);
dispatch.link_program(program_id);
for (const auto& binding : attribute_bindings) {
if (binding.name != nullptr && dispatch.get_attrib_location(program_id, binding.name) != -1) {
dispatch.bind_attrib_location(program_id, binding.location, binding.name);
}
}
OpenGlProgramLinkInfo info {
.program_id = program_id,
};
dispatch.link_program(program_id);
dispatch.get_program_integer(program_id, program_link_status_query(), &info.link_status);
dispatch.get_program_info_log(program_id, info_log_capacity, &info.info_log_length, info_log);
return pp::foundation::Result<OpenGlProgramLinkInfo>::success(info);
}
pp::foundation::Result<std::int32_t> query_opengl_program_integer(
std::uint32_t program_id,
std::uint32_t query,
OpenGlProgramIntegerDispatch dispatch) noexcept
{
if (dispatch.get_program_integer == nullptr) {
return pp::foundation::Result<std::int32_t>::failure(
pp::foundation::Status::invalid_argument("OpenGL program integer dispatch callback must not be null"));
}
if (program_id == 0U || query == 0U) {
return pp::foundation::Result<std::int32_t>::failure(
pp::foundation::Status::invalid_argument("OpenGL program integer parameters are invalid"));
}
std::int32_t value = 0;
dispatch.get_program_integer(program_id, query, &value);
return pp::foundation::Result<std::int32_t>::success(value);
}
pp::foundation::Result<OpenGlActiveUniformInfo> get_opengl_active_uniform(
std::uint32_t program_id,
std::uint32_t index,
char* name,
std::int32_t name_capacity,
OpenGlActiveUniformDispatch dispatch) noexcept
{
if (dispatch.get_active_uniform == nullptr) {
return pp::foundation::Result<OpenGlActiveUniformInfo>::failure(
pp::foundation::Status::invalid_argument("OpenGL active uniform dispatch callback must not be null"));
}
if (program_id == 0U || name == nullptr || name_capacity <= 0) {
return pp::foundation::Result<OpenGlActiveUniformInfo>::failure(
pp::foundation::Status::invalid_argument("OpenGL active uniform parameters are invalid"));
}
OpenGlActiveUniformInfo info {};
dispatch.get_active_uniform(
program_id,
index,
name_capacity,
&info.length,
&info.size,
&info.type,
name);
if (info.length >= 0 && info.length < name_capacity) {
name[info.length] = '\0';
} else {
name[name_capacity - 1] = '\0';
}
return pp::foundation::Result<OpenGlActiveUniformInfo>::success(info);
}
pp::foundation::Result<std::int32_t> get_opengl_uniform_location(
std::uint32_t program_id,
const char* uniform_name,
OpenGlUniformLocationDispatch dispatch) noexcept
{
if (dispatch.get_uniform_location == nullptr) {
return pp::foundation::Result<std::int32_t>::failure(
pp::foundation::Status::invalid_argument("OpenGL uniform-location dispatch callback must not be null"));
}
if (program_id == 0U || uniform_name == nullptr || uniform_name[0] == '\0') {
return pp::foundation::Result<std::int32_t>::failure(
pp::foundation::Status::invalid_argument("OpenGL uniform-location parameters are invalid"));
}
return pp::foundation::Result<std::int32_t>::success(
dispatch.get_uniform_location(program_id, uniform_name));
}
std::uint32_t extension_count_query() noexcept
{
return gl_num_extensions;

View File

@@ -9,6 +9,8 @@
namespace pp::renderer::gl {
struct OpenGlAttributeBinding;
struct OpenGlRuntime {
bool desktop_gl = false;
bool gles = false;
@@ -148,6 +150,24 @@ struct OpenGlTexture2DReadbackResult {
bool pixels_read = false;
};
struct OpenGlShaderCompileInfo {
std::uint32_t shader_id = 0;
std::int32_t compile_status = 0;
std::int32_t info_log_length = 0;
};
struct OpenGlProgramLinkInfo {
std::uint32_t program_id = 0;
std::int32_t link_status = 0;
std::int32_t info_log_length = 0;
};
struct OpenGlActiveUniformInfo {
std::int32_t length = 0;
std::int32_t size = 0;
std::uint32_t type = 0;
};
struct OpenGlFramebufferRect {
std::int32_t x0 = 0;
std::int32_t y0 = 0;
@@ -232,6 +252,47 @@ using OpenGlUniformMatrix4fvFn = void (*)(
using OpenGlUniform1iFn = void (*)(std::int32_t location, std::int32_t value) noexcept;
using OpenGlUniform1fFn = void (*)(std::int32_t location, float value) noexcept;
using OpenGlGetAttribLocationFn = std::int32_t (*)(std::uint32_t program, const char* name) noexcept;
using OpenGlCreateShaderFn = std::uint32_t (*)(std::uint32_t stage) noexcept;
using OpenGlShaderSourceFn = void (*)(
std::uint32_t shader,
std::int32_t count,
const char* const* sources) noexcept;
using OpenGlCompileShaderFn = void (*)(std::uint32_t shader) noexcept;
using OpenGlGetShaderIntegerFn = void (*)(
std::uint32_t shader,
std::uint32_t query,
std::int32_t* value) noexcept;
using OpenGlGetShaderInfoLogFn = void (*)(
std::uint32_t shader,
std::int32_t capacity,
std::int32_t* length,
char* info_log) noexcept;
using OpenGlDeleteShaderFn = void (*)(std::uint32_t shader) noexcept;
using OpenGlCreateProgramFn = std::uint32_t (*)() noexcept;
using OpenGlAttachShaderFn = void (*)(std::uint32_t program, std::uint32_t shader) noexcept;
using OpenGlLinkProgramFn = void (*)(std::uint32_t program) noexcept;
using OpenGlBindAttribLocationFn = void (*)(
std::uint32_t program,
std::uint32_t location,
const char* name) noexcept;
using OpenGlGetProgramIntegerFn = void (*)(
std::uint32_t program,
std::uint32_t query,
std::int32_t* value) noexcept;
using OpenGlGetProgramInfoLogFn = void (*)(
std::uint32_t program,
std::int32_t capacity,
std::int32_t* length,
char* info_log) noexcept;
using OpenGlGetActiveUniformFn = void (*)(
std::uint32_t program,
std::uint32_t index,
std::int32_t capacity,
std::int32_t* length,
std::int32_t* size,
std::uint32_t* type,
char* name) noexcept;
using OpenGlGetUniformLocationFn = std::int32_t (*)(std::uint32_t program, const char* name) noexcept;
using OpenGlBindFramebufferFn = void (*)(std::uint32_t target, std::uint32_t framebuffer) noexcept;
using OpenGlBindTextureFn = void (*)(std::uint32_t target, std::uint32_t texture) noexcept;
using OpenGlBindSamplerFn = void (*)(std::uint32_t unit, std::uint32_t sampler) noexcept;
@@ -482,6 +543,41 @@ struct OpenGlAttributeLocationDispatch {
OpenGlGetAttribLocationFn get_attrib_location = nullptr;
};
struct OpenGlShaderCompileDispatch {
OpenGlCreateShaderFn create_shader = nullptr;
OpenGlShaderSourceFn shader_source = nullptr;
OpenGlCompileShaderFn compile_shader = nullptr;
OpenGlGetShaderIntegerFn get_shader_integer = nullptr;
OpenGlGetShaderInfoLogFn get_shader_info_log = nullptr;
};
struct OpenGlShaderDeleteDispatch {
OpenGlDeleteShaderFn delete_shader = nullptr;
};
struct OpenGlProgramLinkDispatch {
OpenGlCreateProgramFn create_program = nullptr;
OpenGlAttachShaderFn attach_shader = nullptr;
OpenGlDeleteShaderFn delete_shader = nullptr;
OpenGlLinkProgramFn link_program = nullptr;
OpenGlGetAttribLocationFn get_attrib_location = nullptr;
OpenGlBindAttribLocationFn bind_attrib_location = nullptr;
OpenGlGetProgramIntegerFn get_program_integer = nullptr;
OpenGlGetProgramInfoLogFn get_program_info_log = nullptr;
};
struct OpenGlProgramIntegerDispatch {
OpenGlGetProgramIntegerFn get_program_integer = nullptr;
};
struct OpenGlActiveUniformDispatch {
OpenGlGetActiveUniformFn get_active_uniform = nullptr;
};
struct OpenGlUniformLocationDispatch {
OpenGlGetUniformLocationFn get_uniform_location = nullptr;
};
[[nodiscard]] OpenGlCapabilities detect_opengl_capabilities(
std::span<const std::string_view> extensions,
OpenGlRuntime runtime) noexcept;
@@ -603,6 +699,36 @@ struct OpenGlAttributeLocationDispatch {
std::uint32_t program_id,
const char* attribute_name,
OpenGlAttributeLocationDispatch dispatch) noexcept;
[[nodiscard]] pp::foundation::Result<OpenGlShaderCompileInfo> compile_opengl_shader_source(
std::uint32_t stage,
const char* source,
char* info_log,
std::int32_t info_log_capacity,
OpenGlShaderCompileDispatch dispatch) noexcept;
[[nodiscard]] pp::foundation::Status delete_opengl_shader(
std::uint32_t shader_id,
OpenGlShaderDeleteDispatch dispatch) noexcept;
[[nodiscard]] pp::foundation::Result<OpenGlProgramLinkInfo> link_opengl_shader_program(
std::uint32_t vertex_shader_id,
std::uint32_t fragment_shader_id,
std::span<const OpenGlAttributeBinding> attribute_bindings,
char* info_log,
std::int32_t info_log_capacity,
OpenGlProgramLinkDispatch dispatch) noexcept;
[[nodiscard]] pp::foundation::Result<std::int32_t> query_opengl_program_integer(
std::uint32_t program_id,
std::uint32_t query,
OpenGlProgramIntegerDispatch dispatch) noexcept;
[[nodiscard]] pp::foundation::Result<OpenGlActiveUniformInfo> get_opengl_active_uniform(
std::uint32_t program_id,
std::uint32_t index,
char* name,
std::int32_t name_capacity,
OpenGlActiveUniformDispatch dispatch) noexcept;
[[nodiscard]] pp::foundation::Result<std::int32_t> get_opengl_uniform_location(
std::uint32_t program_id,
const char* uniform_name,
OpenGlUniformLocationDispatch dispatch) noexcept;
[[nodiscard]] std::uint32_t extension_count_query() noexcept;
[[nodiscard]] std::uint32_t extension_string_name() noexcept;

View File

@@ -88,6 +88,125 @@ std::int32_t get_opengl_attrib_location(std::uint32_t program, const char* name)
return static_cast<std::int32_t>(glGetAttribLocation(static_cast<GLuint>(program), name));
}
std::uint32_t create_opengl_shader(std::uint32_t stage) noexcept
{
return static_cast<std::uint32_t>(glCreateShader(static_cast<GLenum>(stage)));
}
void set_opengl_shader_source(
std::uint32_t shader,
std::int32_t count,
const char* const* sources) noexcept
{
glShaderSource(
static_cast<GLuint>(shader),
static_cast<GLsizei>(count),
reinterpret_cast<const GLchar* const*>(sources),
nullptr);
}
void compile_opengl_shader(std::uint32_t shader) noexcept
{
glCompileShader(static_cast<GLuint>(shader));
}
void query_opengl_shader_integer(
std::uint32_t shader,
std::uint32_t query,
std::int32_t* value) noexcept
{
glGetShaderiv(
static_cast<GLuint>(shader),
static_cast<GLenum>(query),
reinterpret_cast<GLint*>(value));
}
void get_opengl_shader_info_log(
std::uint32_t shader,
std::int32_t capacity,
std::int32_t* length,
char* info_log) noexcept
{
glGetShaderInfoLog(
static_cast<GLuint>(shader),
static_cast<GLsizei>(capacity),
reinterpret_cast<GLsizei*>(length),
reinterpret_cast<GLchar*>(info_log));
}
void delete_opengl_shader(std::uint32_t shader) noexcept
{
glDeleteShader(static_cast<GLuint>(shader));
}
std::uint32_t create_opengl_program() noexcept
{
return static_cast<std::uint32_t>(glCreateProgram());
}
void attach_opengl_shader(std::uint32_t program, std::uint32_t shader) noexcept
{
glAttachShader(static_cast<GLuint>(program), static_cast<GLuint>(shader));
}
void link_opengl_program(std::uint32_t program) noexcept
{
glLinkProgram(static_cast<GLuint>(program));
}
void bind_opengl_attrib_location(std::uint32_t program, std::uint32_t location, const char* name) noexcept
{
glBindAttribLocation(static_cast<GLuint>(program), static_cast<GLuint>(location), name);
}
void query_opengl_program_integer(
std::uint32_t program,
std::uint32_t query,
std::int32_t* value) noexcept
{
glGetProgramiv(
static_cast<GLuint>(program),
static_cast<GLenum>(query),
reinterpret_cast<GLint*>(value));
}
void get_opengl_program_info_log(
std::uint32_t program,
std::int32_t capacity,
std::int32_t* length,
char* info_log) noexcept
{
glGetProgramInfoLog(
static_cast<GLuint>(program),
static_cast<GLsizei>(capacity),
reinterpret_cast<GLsizei*>(length),
reinterpret_cast<GLchar*>(info_log));
}
void get_opengl_active_uniform(
std::uint32_t program,
std::uint32_t index,
std::int32_t capacity,
std::int32_t* length,
std::int32_t* size,
std::uint32_t* type,
char* name) noexcept
{
glGetActiveUniform(
static_cast<GLuint>(program),
static_cast<GLuint>(index),
static_cast<GLsizei>(capacity),
reinterpret_cast<GLsizei*>(length),
reinterpret_cast<GLint*>(size),
reinterpret_cast<GLenum*>(type),
reinterpret_cast<GLchar*>(name));
}
std::int32_t get_opengl_uniform_location(std::uint32_t program, const char* name) noexcept
{
return static_cast<std::int32_t>(glGetUniformLocation(static_cast<GLuint>(program), name));
}
}
std::map<kShader, Shader> ShaderManager::m_shaders;
@@ -247,113 +366,212 @@ bool Shader::create(const std::string& vertex, const std::string& fragment)
bool ret = true;
App::I->render_task([this, &ret, vertex, fragment]
{
GLint status;
static char infolog[4096];
int infolen;
const GLchar* source;
auto vs = glCreateShader(vertex_shader_stage());
if (!vs)
const auto vertex_shader = pp::renderer::gl::compile_opengl_shader_source(
pp::renderer::gl::vertex_shader_stage(),
vertex.c_str(),
infolog,
static_cast<std::int32_t>(sizeof(infolog)),
pp::renderer::gl::OpenGlShaderCompileDispatch {
.create_shader = create_opengl_shader,
.shader_source = set_opengl_shader_source,
.compile_shader = compile_opengl_shader,
.get_shader_integer = query_opengl_shader_integer,
.get_shader_info_log = get_opengl_shader_info_log,
});
if (!vertex_shader.ok())
{
ret = false;
LOG("Shader::create() vertex compile failed because: %s", vertex_shader.status().message);
return;
}
source = vertex.c_str();
glShaderSource(vs, 1, &source, nullptr);
glCompileShader(vs);
glGetShaderiv(vs, shader_compile_status_query(), &status);
glGetShaderInfoLog(vs, sizeof(infolog), &infolen, infolog);
if (infolen > 0)
if (vertex_shader.value().info_log_length > 0)
{
LOG("\nVERTEX SHADER: %s", m_path.c_str());
parse_error(infolog, vertex);
}
if (status == 0)
if (vertex_shader.value().compile_status == 0)
{
glDeleteShader(vs);
const auto status = pp::renderer::gl::delete_opengl_shader(
vertex_shader.value().shader_id,
pp::renderer::gl::OpenGlShaderDeleteDispatch {
.delete_shader = delete_opengl_shader,
});
if (!status.ok())
LOG("Shader::create() vertex shader cleanup failed because: %s", status.message);
ret = false;
return;
}
auto fs = glCreateShader(fragment_shader_stage());
if (!fs)
const auto fragment_shader = pp::renderer::gl::compile_opengl_shader_source(
pp::renderer::gl::fragment_shader_stage(),
fragment.c_str(),
infolog,
static_cast<std::int32_t>(sizeof(infolog)),
pp::renderer::gl::OpenGlShaderCompileDispatch {
.create_shader = create_opengl_shader,
.shader_source = set_opengl_shader_source,
.compile_shader = compile_opengl_shader,
.get_shader_integer = query_opengl_shader_integer,
.get_shader_info_log = get_opengl_shader_info_log,
});
if (!fragment_shader.ok())
{
glDeleteShader(vs);
const auto status = pp::renderer::gl::delete_opengl_shader(
vertex_shader.value().shader_id,
pp::renderer::gl::OpenGlShaderDeleteDispatch {
.delete_shader = delete_opengl_shader,
});
if (!status.ok())
LOG("Shader::create() vertex shader cleanup failed because: %s", status.message);
ret = false;
LOG("Shader::create() fragment compile failed because: %s", fragment_shader.status().message);
return;
}
source = fragment.c_str();
glShaderSource(fs, 1, &source, nullptr);
glCompileShader(fs);
glGetShaderiv(fs, shader_compile_status_query(), &status);
glGetShaderInfoLog(fs, sizeof(infolog), &infolen, infolog);
if (infolen > 0)
if (fragment_shader.value().info_log_length > 0)
{
LOG("\nFRAGMENT SHADER: %s", m_path.c_str());
parse_error(infolog, fragment);
}
if (status == 0)
if (fragment_shader.value().compile_status == 0)
{
glDeleteShader(vs);
glDeleteShader(fs);
const auto vertex_cleanup = pp::renderer::gl::delete_opengl_shader(
vertex_shader.value().shader_id,
pp::renderer::gl::OpenGlShaderDeleteDispatch {
.delete_shader = delete_opengl_shader,
});
const auto fragment_cleanup = pp::renderer::gl::delete_opengl_shader(
fragment_shader.value().shader_id,
pp::renderer::gl::OpenGlShaderDeleteDispatch {
.delete_shader = delete_opengl_shader,
});
if (!vertex_cleanup.ok())
LOG("Shader::create() vertex shader cleanup failed because: %s", vertex_cleanup.message);
if (!fragment_cleanup.ok())
LOG("Shader::create() fragment shader cleanup failed because: %s", fragment_cleanup.message);
ret = false;
return;
}
auto ps = glCreateProgram();
if (!ps)
const auto program = pp::renderer::gl::link_opengl_shader_program(
vertex_shader.value().shader_id,
fragment_shader.value().shader_id,
pp::renderer::gl::panopainter_shader_attribute_bindings(),
infolog,
static_cast<std::int32_t>(sizeof(infolog)),
pp::renderer::gl::OpenGlProgramLinkDispatch {
.create_program = create_opengl_program,
.attach_shader = attach_opengl_shader,
.delete_shader = delete_opengl_shader,
.link_program = link_opengl_program,
.get_attrib_location = get_opengl_attrib_location,
.bind_attrib_location = bind_opengl_attrib_location,
.get_program_integer = query_opengl_program_integer,
.get_program_info_log = get_opengl_program_info_log,
});
if (!program.ok())
{
glDeleteShader(vs);
glDeleteShader(fs);
const auto vertex_cleanup = pp::renderer::gl::delete_opengl_shader(
vertex_shader.value().shader_id,
pp::renderer::gl::OpenGlShaderDeleteDispatch {
.delete_shader = delete_opengl_shader,
});
const auto fragment_cleanup = pp::renderer::gl::delete_opengl_shader(
fragment_shader.value().shader_id,
pp::renderer::gl::OpenGlShaderDeleteDispatch {
.delete_shader = delete_opengl_shader,
});
if (!vertex_cleanup.ok())
LOG("Shader::create() vertex shader cleanup failed because: %s", vertex_cleanup.message);
if (!fragment_cleanup.ok())
LOG("Shader::create() fragment shader cleanup failed because: %s", fragment_cleanup.message);
ret = false;
LOG("Shader::create() program link failed because: %s", program.status().message);
return;
}
glAttachShader(ps, vs);
glAttachShader(ps, fs);
glDeleteShader(vs);
glDeleteShader(fs);
glLinkProgram(ps);
for (const auto& binding : pp::renderer::gl::panopainter_shader_attribute_bindings())
{
if (glGetAttribLocation(ps, binding.name) != -1)
glBindAttribLocation(ps, static_cast<GLuint>(binding.location), binding.name);
}
glLinkProgram(ps);
glGetProgramiv(ps, program_link_status_query(), &status);
glGetProgramInfoLog(ps, sizeof(infolog), &infolen, infolog);
if (infolen > 0)
if (program.value().info_log_length > 0)
LOG("LINK SHADER: %s\n%s", m_path.c_str(), infolog);
if (status == 0)
if (program.value().link_status == 0)
{
glDeleteProgram(ps);
const auto status = pp::renderer::gl::delete_opengl_program(
program.value().program_id,
pp::renderer::gl::OpenGlProgramDeleteDispatch {
.use_program = use_opengl_program,
.delete_program = delete_opengl_program,
});
if (!status.ok())
LOG("Shader::create() program cleanup failed because: %s", status.message);
ret = false;
return;
}
const auto cleanup_program = [](std::uint32_t program_id) noexcept {
const auto status = pp::renderer::gl::delete_opengl_program(
program_id,
pp::renderer::gl::OpenGlProgramDeleteDispatch {
.use_program = use_opengl_program,
.delete_program = delete_opengl_program,
});
if (!status.ok())
LOG("Shader::create() program cleanup failed because: %s", status.message);
};
// Parse shader uniforms
{
GLint count;
GLint size; // size of the variable
GLenum type; // type of the variable (float, vec3 or mat4, etc)
const GLsizei bufSize = 64; // maximum name length
GLchar name[bufSize]; // variable name in GLSL
GLsizei length; // name length
glGetProgramiv(ps, active_uniform_count_query(), &count);
const auto uniform_count = pp::renderer::gl::query_opengl_program_integer(
program.value().program_id,
pp::renderer::gl::active_uniform_count_query(),
pp::renderer::gl::OpenGlProgramIntegerDispatch {
.get_program_integer = query_opengl_program_integer,
});
if (!uniform_count.ok())
{
LOG("Shader::create() uniform discovery failed because: %s", uniform_count.status().message);
cleanup_program(program.value().program_id);
ret = false;
return;
}
constexpr std::int32_t bufSize = 64; // maximum name length
char uniform_name[bufSize]; // variable name in GLSL
const auto count = uniform_count.value();
for (int i = 0; i < count; i++)
{
glGetActiveUniform(ps, (GLuint)i, bufSize, &length, &size, &type, name);
name[length] = 0;
kShaderUniform id = static_cast<kShaderUniform>(pp::renderer::gl::shader_uniform_id(name));
const auto uniform = pp::renderer::gl::get_opengl_active_uniform(
program.value().program_id,
static_cast<std::uint32_t>(i),
uniform_name,
bufSize,
pp::renderer::gl::OpenGlActiveUniformDispatch {
.get_active_uniform = get_opengl_active_uniform,
});
if (!uniform.ok())
{
LOG("Shader::create() active uniform discovery failed because: %s", uniform.status().message);
cleanup_program(program.value().program_id);
ret = false;
return;
}
kShaderUniform id = static_cast<kShaderUniform>(pp::renderer::gl::shader_uniform_id(uniform_name));
if (m_umap.find(id) != m_umap.end())
LOG("UNIFORM ALREADY DEFINED: %s", name);
m_umap[id] = glGetUniformLocation(ps, name);
//printf("Uniform #%d Type: %u Name: %s Loc: %d\n", i, type, name, glGetUniformLocation(ps, name));
LOG("UNIFORM ALREADY DEFINED: %s", uniform_name);
const auto location = pp::renderer::gl::get_opengl_uniform_location(
program.value().program_id,
uniform_name,
pp::renderer::gl::OpenGlUniformLocationDispatch {
.get_uniform_location = get_opengl_uniform_location,
});
if (!location.ok())
{
LOG("Shader::create() uniform location failed because: %s", location.status().message);
cleanup_program(program.value().program_id);
ret = false;
return;
}
m_umap[id] = location.value();
}
}
prog = ps;
prog = program.value().program_id;
});
return ret;

View File

@@ -2,6 +2,7 @@
#include "renderer_gl/shader_bindings.h"
#include "test_harness.h"
#include <algorithm>
#include <array>
#include <cstdint>
#include <cstring>
@@ -111,6 +112,29 @@ struct RecordedOpenGlUniformScalarCall {
bool is_float = false;
};
struct RecordedOpenGlShaderSourceCall {
std::uint32_t shader = 0;
std::int32_t count = 0;
std::string_view source;
};
struct RecordedOpenGlProgramAttachCall {
std::uint32_t program = 0;
std::uint32_t shader = 0;
};
struct RecordedOpenGlAttribBindCall {
std::uint32_t program = 0;
std::uint32_t location = 0;
std::string_view name;
};
struct RecordedOpenGlActiveUniformCall {
std::uint32_t program = 0;
std::uint32_t index = 0;
std::int32_t capacity = 0;
};
std::vector<RecordedOpenGlStateCall> recorded_state_calls;
std::vector<std::uint32_t> recorded_string_queries;
std::vector<pp::renderer::gl::OpenGlDefaultClear> recorded_clear_calls;
@@ -138,9 +162,27 @@ std::vector<RecordedOpenGlUniformFloatVectorCall> recorded_uniform_vector_calls;
std::vector<RecordedOpenGlUniformScalarCall> recorded_uniform_scalar_calls;
std::vector<std::uint32_t> recorded_attrib_location_programs;
std::vector<std::string_view> recorded_attrib_location_names;
std::vector<std::uint32_t> recorded_created_shader_stages;
std::vector<RecordedOpenGlShaderSourceCall> recorded_shader_source_calls;
std::vector<std::uint32_t> recorded_compiled_shaders;
std::vector<std::uint32_t> recorded_deleted_shaders;
std::vector<std::uint32_t> recorded_shader_integer_queries;
std::vector<std::uint32_t> recorded_created_programs;
std::vector<RecordedOpenGlProgramAttachCall> recorded_program_attach_calls;
std::vector<std::uint32_t> recorded_linked_programs;
std::vector<RecordedOpenGlAttribBindCall> recorded_attrib_bind_calls;
std::vector<std::uint32_t> recorded_program_integer_queries;
std::vector<RecordedOpenGlActiveUniformCall> recorded_active_uniform_calls;
std::vector<std::uint32_t> recorded_uniform_location_programs;
std::vector<std::string_view> recorded_uniform_location_names;
std::uint32_t next_texture_id = 91U;
std::uint32_t next_framebuffer_id = 44U;
std::uint32_t next_sampler_id = 71U;
std::uint32_t next_shader_id = 301U;
std::uint32_t next_program_id = 401U;
std::int32_t configured_shader_compile_status = 1;
std::int32_t configured_program_link_status = 1;
std::int32_t configured_active_uniform_count = 2;
std::uint32_t configured_framebuffer_status = 0x8CD5U;
void record_enable(std::uint32_t state) noexcept
@@ -592,6 +634,164 @@ std::int32_t record_get_attrib_location(std::uint32_t program, const char* name)
return -1;
}
std::uint32_t record_create_shader(std::uint32_t stage) noexcept
{
recorded_created_shader_stages.push_back(stage);
return next_shader_id++;
}
void record_shader_source(std::uint32_t shader, std::int32_t count, const char* const* sources) noexcept
{
recorded_shader_source_calls.push_back(RecordedOpenGlShaderSourceCall {
.shader = shader,
.count = count,
.source = sources != nullptr && sources[0] != nullptr ? std::string_view { sources[0] } : std::string_view {},
});
}
void record_compile_shader(std::uint32_t shader) noexcept
{
recorded_compiled_shaders.push_back(shader);
}
void record_get_shader_integer(std::uint32_t shader, std::uint32_t query, std::int32_t* value) noexcept
{
recorded_shader_integer_queries.push_back(query);
if (value != nullptr) {
*value = configured_shader_compile_status;
}
(void)shader;
}
void record_get_shader_info_log(
std::uint32_t shader,
std::int32_t capacity,
std::int32_t* length,
char* info_log) noexcept
{
constexpr std::string_view log_text { "shader note" };
if (length != nullptr) {
*length = static_cast<std::int32_t>(log_text.size());
}
if (info_log != nullptr && capacity > 0) {
const auto copied = std::min<std::int32_t>(capacity - 1, static_cast<std::int32_t>(log_text.size()));
std::memcpy(info_log, log_text.data(), static_cast<std::size_t>(copied));
info_log[copied] = '\0';
}
(void)shader;
}
void record_delete_shader(std::uint32_t shader) noexcept
{
recorded_deleted_shaders.push_back(shader);
}
std::uint32_t record_create_program() noexcept
{
recorded_created_programs.push_back(next_program_id);
return next_program_id++;
}
void record_attach_shader(std::uint32_t program, std::uint32_t shader) noexcept
{
recorded_program_attach_calls.push_back(RecordedOpenGlProgramAttachCall {
.program = program,
.shader = shader,
});
}
void record_link_program(std::uint32_t program) noexcept
{
recorded_linked_programs.push_back(program);
}
void record_bind_attrib_location(std::uint32_t program, std::uint32_t location, const char* name) noexcept
{
recorded_attrib_bind_calls.push_back(RecordedOpenGlAttribBindCall {
.program = program,
.location = location,
.name = name == nullptr ? std::string_view {} : std::string_view { name },
});
}
void record_get_program_integer(std::uint32_t program, std::uint32_t query, std::int32_t* value) noexcept
{
recorded_program_integer_queries.push_back(query);
if (value == nullptr) {
return;
}
if (query == 0x8B82U) {
*value = configured_program_link_status;
} else if (query == 0x8B86U) {
*value = configured_active_uniform_count;
} else {
*value = -1;
}
(void)program;
}
void record_get_program_info_log(
std::uint32_t program,
std::int32_t capacity,
std::int32_t* length,
char* info_log) noexcept
{
constexpr std::string_view log_text { "program note" };
if (length != nullptr) {
*length = static_cast<std::int32_t>(log_text.size());
}
if (info_log != nullptr && capacity > 0) {
const auto copied = std::min<std::int32_t>(capacity - 1, static_cast<std::int32_t>(log_text.size()));
std::memcpy(info_log, log_text.data(), static_cast<std::size_t>(copied));
info_log[copied] = '\0';
}
(void)program;
}
void record_get_active_uniform(
std::uint32_t program,
std::uint32_t index,
std::int32_t capacity,
std::int32_t* length,
std::int32_t* size,
std::uint32_t* type,
char* name) noexcept
{
recorded_active_uniform_calls.push_back(RecordedOpenGlActiveUniformCall {
.program = program,
.index = index,
.capacity = capacity,
});
const std::string_view uniform_name = index == 0U ? std::string_view { "mvp" } : std::string_view { "tex" };
if (length != nullptr) {
*length = static_cast<std::int32_t>(uniform_name.size());
}
if (size != nullptr) {
*size = 1;
}
if (type != nullptr) {
*type = 0x8B5CU;
}
if (name != nullptr && capacity > 0) {
const auto copied = std::min<std::int32_t>(capacity - 1, static_cast<std::int32_t>(uniform_name.size()));
std::memcpy(name, uniform_name.data(), static_cast<std::size_t>(copied));
name[copied] = '\0';
}
}
std::int32_t record_get_uniform_location(std::uint32_t program, const char* name) noexcept
{
recorded_uniform_location_programs.push_back(program);
recorded_uniform_location_names.push_back(name == nullptr ? std::string_view {} : std::string_view { name });
if (name != nullptr && std::string_view { name } == std::string_view { "mvp" }) {
return 4;
}
if (name != nullptr && std::string_view { name } == std::string_view { "tex" }) {
return 7;
}
return -1;
}
void detects_common_extension_capabilities(pp::tests::Harness& h)
{
constexpr std::array<std::string_view, 2> extensions {
@@ -2256,6 +2456,321 @@ void rejects_invalid_program_uniform_dispatch(pp::tests::Harness& h)
PP_EXPECT(h, invalid_attrib_name.status().code == pp::foundation::StatusCode::invalid_argument);
}
void compiles_shader_source_through_dispatch(pp::tests::Harness& h)
{
recorded_created_shader_stages.clear();
recorded_shader_source_calls.clear();
recorded_compiled_shaders.clear();
recorded_shader_integer_queries.clear();
next_shader_id = 301U;
configured_shader_compile_status = 1;
std::array<char, 64> info_log {};
const auto shader = pp::renderer::gl::compile_opengl_shader_source(
0x8B31U,
"void main(){}",
info_log.data(),
static_cast<std::int32_t>(info_log.size()),
pp::renderer::gl::OpenGlShaderCompileDispatch {
.create_shader = record_create_shader,
.shader_source = record_shader_source,
.compile_shader = record_compile_shader,
.get_shader_integer = record_get_shader_integer,
.get_shader_info_log = record_get_shader_info_log,
});
PP_EXPECT(h, shader.ok());
PP_EXPECT(h, shader.value().shader_id == 301U);
PP_EXPECT(h, shader.value().compile_status == 1);
PP_EXPECT(h, shader.value().info_log_length == 11);
PP_EXPECT(h, std::string_view { info_log.data() } == std::string_view("shader note"));
PP_EXPECT(h, recorded_created_shader_stages.size() == 1U);
PP_EXPECT(h, recorded_created_shader_stages[0] == 0x8B31U);
PP_EXPECT(h, recorded_shader_source_calls.size() == 1U);
PP_EXPECT(h, recorded_shader_source_calls[0].shader == 301U);
PP_EXPECT(h, recorded_shader_source_calls[0].count == 1);
PP_EXPECT(h, recorded_shader_source_calls[0].source == std::string_view("void main(){}"));
PP_EXPECT(h, recorded_compiled_shaders.size() == 1U);
PP_EXPECT(h, recorded_compiled_shaders[0] == 301U);
PP_EXPECT(h, recorded_shader_integer_queries.size() == 1U);
PP_EXPECT(h, recorded_shader_integer_queries[0] == 0x8B81U);
}
void rejects_invalid_shader_compile_dispatch(pp::tests::Harness& h)
{
std::array<char, 16> info_log {};
const auto missing_dispatch = pp::renderer::gl::compile_opengl_shader_source(
0x8B31U,
"void main(){}",
info_log.data(),
static_cast<std::int32_t>(info_log.size()),
pp::renderer::gl::OpenGlShaderCompileDispatch {
.create_shader = record_create_shader,
.shader_source = record_shader_source,
});
const auto empty_source = pp::renderer::gl::compile_opengl_shader_source(
0x8B31U,
"",
info_log.data(),
static_cast<std::int32_t>(info_log.size()),
pp::renderer::gl::OpenGlShaderCompileDispatch {
.create_shader = record_create_shader,
.shader_source = record_shader_source,
.compile_shader = record_compile_shader,
.get_shader_integer = record_get_shader_integer,
.get_shader_info_log = record_get_shader_info_log,
});
const auto missing_log = pp::renderer::gl::compile_opengl_shader_source(
0x8B31U,
"void main(){}",
nullptr,
static_cast<std::int32_t>(info_log.size()),
pp::renderer::gl::OpenGlShaderCompileDispatch {
.create_shader = record_create_shader,
.shader_source = record_shader_source,
.compile_shader = record_compile_shader,
.get_shader_integer = record_get_shader_integer,
.get_shader_info_log = record_get_shader_info_log,
});
PP_EXPECT(h, !missing_dispatch.ok());
PP_EXPECT(h, missing_dispatch.status().code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(h, !empty_source.ok());
PP_EXPECT(h, empty_source.status().code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(h, !missing_log.ok());
PP_EXPECT(h, missing_log.status().code == pp::foundation::StatusCode::invalid_argument);
}
void deletes_shader_through_dispatch(pp::tests::Harness& h)
{
recorded_deleted_shaders.clear();
const auto delete_status = pp::renderer::gl::delete_opengl_shader(
31U,
pp::renderer::gl::OpenGlShaderDeleteDispatch {
.delete_shader = record_delete_shader,
});
const auto delete_zero_status = pp::renderer::gl::delete_opengl_shader(
0U,
pp::renderer::gl::OpenGlShaderDeleteDispatch {
.delete_shader = record_delete_shader,
});
PP_EXPECT(h, delete_status.ok());
PP_EXPECT(h, delete_zero_status.ok());
PP_EXPECT(h, recorded_deleted_shaders.size() == 1U);
PP_EXPECT(h, recorded_deleted_shaders[0] == 31U);
}
void links_shader_program_through_dispatch(pp::tests::Harness& h)
{
recorded_created_programs.clear();
recorded_program_attach_calls.clear();
recorded_deleted_shaders.clear();
recorded_linked_programs.clear();
recorded_attrib_location_programs.clear();
recorded_attrib_location_names.clear();
recorded_attrib_bind_calls.clear();
recorded_program_integer_queries.clear();
next_program_id = 401U;
configured_program_link_status = 1;
std::array<char, 64> info_log {};
const auto program = pp::renderer::gl::link_opengl_shader_program(
101U,
103U,
pp::renderer::gl::panopainter_shader_attribute_bindings(),
info_log.data(),
static_cast<std::int32_t>(info_log.size()),
pp::renderer::gl::OpenGlProgramLinkDispatch {
.create_program = record_create_program,
.attach_shader = record_attach_shader,
.delete_shader = record_delete_shader,
.link_program = record_link_program,
.get_attrib_location = record_get_attrib_location,
.bind_attrib_location = record_bind_attrib_location,
.get_program_integer = record_get_program_integer,
.get_program_info_log = record_get_program_info_log,
});
PP_EXPECT(h, program.ok());
PP_EXPECT(h, program.value().program_id == 401U);
PP_EXPECT(h, program.value().link_status == 1);
PP_EXPECT(h, program.value().info_log_length == 12);
PP_EXPECT(h, std::string_view { info_log.data() } == std::string_view("program note"));
PP_EXPECT(h, recorded_created_programs.size() == 1U);
PP_EXPECT(h, recorded_created_programs[0] == 401U);
PP_EXPECT(h, recorded_program_attach_calls.size() == 2U);
PP_EXPECT(h, recorded_program_attach_calls[0].shader == 101U);
PP_EXPECT(h, recorded_program_attach_calls[1].shader == 103U);
PP_EXPECT(h, recorded_deleted_shaders.size() == 2U);
PP_EXPECT(h, recorded_deleted_shaders[0] == 101U);
PP_EXPECT(h, recorded_deleted_shaders[1] == 103U);
PP_EXPECT(h, recorded_linked_programs.size() == 2U);
PP_EXPECT(h, recorded_linked_programs[0] == 401U);
PP_EXPECT(h, recorded_linked_programs[1] == 401U);
PP_EXPECT(h, recorded_attrib_bind_calls.size() == 1U);
PP_EXPECT(h, recorded_attrib_bind_calls[0].program == 401U);
PP_EXPECT(h, recorded_attrib_bind_calls[0].location == 1U);
PP_EXPECT(h, recorded_attrib_bind_calls[0].name == std::string_view("uvs"));
PP_EXPECT(h, recorded_program_integer_queries.size() == 1U);
PP_EXPECT(h, recorded_program_integer_queries[0] == 0x8B82U);
}
void rejects_invalid_shader_program_link_dispatch(pp::tests::Harness& h)
{
std::array<char, 16> info_log {};
const auto missing_dispatch = pp::renderer::gl::link_opengl_shader_program(
1U,
2U,
pp::renderer::gl::panopainter_shader_attribute_bindings(),
info_log.data(),
static_cast<std::int32_t>(info_log.size()),
pp::renderer::gl::OpenGlProgramLinkDispatch {
.create_program = record_create_program,
.attach_shader = record_attach_shader,
});
const auto zero_shader = pp::renderer::gl::link_opengl_shader_program(
0U,
2U,
pp::renderer::gl::panopainter_shader_attribute_bindings(),
info_log.data(),
static_cast<std::int32_t>(info_log.size()),
pp::renderer::gl::OpenGlProgramLinkDispatch {
.create_program = record_create_program,
.attach_shader = record_attach_shader,
.delete_shader = record_delete_shader,
.link_program = record_link_program,
.get_attrib_location = record_get_attrib_location,
.bind_attrib_location = record_bind_attrib_location,
.get_program_integer = record_get_program_integer,
.get_program_info_log = record_get_program_info_log,
});
const auto missing_log = pp::renderer::gl::link_opengl_shader_program(
1U,
2U,
pp::renderer::gl::panopainter_shader_attribute_bindings(),
nullptr,
static_cast<std::int32_t>(info_log.size()),
pp::renderer::gl::OpenGlProgramLinkDispatch {
.create_program = record_create_program,
.attach_shader = record_attach_shader,
.delete_shader = record_delete_shader,
.link_program = record_link_program,
.get_attrib_location = record_get_attrib_location,
.bind_attrib_location = record_bind_attrib_location,
.get_program_integer = record_get_program_integer,
.get_program_info_log = record_get_program_info_log,
});
PP_EXPECT(h, !missing_dispatch.ok());
PP_EXPECT(h, missing_dispatch.status().code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(h, !zero_shader.ok());
PP_EXPECT(h, zero_shader.status().code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(h, !missing_log.ok());
PP_EXPECT(h, missing_log.status().code == pp::foundation::StatusCode::invalid_argument);
}
void discovers_program_uniforms_through_dispatch(pp::tests::Harness& h)
{
recorded_program_integer_queries.clear();
recorded_active_uniform_calls.clear();
recorded_uniform_location_programs.clear();
recorded_uniform_location_names.clear();
configured_active_uniform_count = 2;
std::array<char, 64> name {};
const auto count = pp::renderer::gl::query_opengl_program_integer(
401U,
pp::renderer::gl::active_uniform_count_query(),
pp::renderer::gl::OpenGlProgramIntegerDispatch {
.get_program_integer = record_get_program_integer,
});
const auto first = pp::renderer::gl::get_opengl_active_uniform(
401U,
0U,
name.data(),
static_cast<std::int32_t>(name.size()),
pp::renderer::gl::OpenGlActiveUniformDispatch {
.get_active_uniform = record_get_active_uniform,
});
const auto location = pp::renderer::gl::get_opengl_uniform_location(
401U,
name.data(),
pp::renderer::gl::OpenGlUniformLocationDispatch {
.get_uniform_location = record_get_uniform_location,
});
PP_EXPECT(h, count.ok());
PP_EXPECT(h, count.value() == 2);
PP_EXPECT(h, first.ok());
PP_EXPECT(h, first.value().length == 3);
PP_EXPECT(h, first.value().size == 1);
PP_EXPECT(h, first.value().type == 0x8B5CU);
PP_EXPECT(h, std::string_view { name.data() } == std::string_view("mvp"));
PP_EXPECT(h, location.ok());
PP_EXPECT(h, location.value() == 4);
PP_EXPECT(h, recorded_program_integer_queries.size() == 1U);
PP_EXPECT(h, recorded_program_integer_queries[0] == 0x8B86U);
PP_EXPECT(h, recorded_active_uniform_calls.size() == 1U);
PP_EXPECT(h, recorded_active_uniform_calls[0].program == 401U);
PP_EXPECT(h, recorded_active_uniform_calls[0].index == 0U);
PP_EXPECT(h, recorded_uniform_location_names.size() == 1U);
PP_EXPECT(h, recorded_uniform_location_names[0] == std::string_view("mvp"));
}
void rejects_invalid_uniform_discovery_dispatch(pp::tests::Harness& h)
{
std::array<char, 16> name {};
const auto missing_program_integer = pp::renderer::gl::query_opengl_program_integer(
1U,
pp::renderer::gl::active_uniform_count_query(),
pp::renderer::gl::OpenGlProgramIntegerDispatch {});
const auto invalid_program_integer = pp::renderer::gl::query_opengl_program_integer(
0U,
pp::renderer::gl::active_uniform_count_query(),
pp::renderer::gl::OpenGlProgramIntegerDispatch {
.get_program_integer = record_get_program_integer,
});
const auto missing_active = pp::renderer::gl::get_opengl_active_uniform(
1U,
0U,
name.data(),
static_cast<std::int32_t>(name.size()),
pp::renderer::gl::OpenGlActiveUniformDispatch {});
const auto invalid_active_buffer = pp::renderer::gl::get_opengl_active_uniform(
1U,
0U,
nullptr,
static_cast<std::int32_t>(name.size()),
pp::renderer::gl::OpenGlActiveUniformDispatch {
.get_active_uniform = record_get_active_uniform,
});
const auto missing_uniform_location = pp::renderer::gl::get_opengl_uniform_location(
1U,
"mvp",
pp::renderer::gl::OpenGlUniformLocationDispatch {});
const auto invalid_uniform_name = pp::renderer::gl::get_opengl_uniform_location(
1U,
"",
pp::renderer::gl::OpenGlUniformLocationDispatch {
.get_uniform_location = record_get_uniform_location,
});
PP_EXPECT(h, !missing_program_integer.ok());
PP_EXPECT(h, missing_program_integer.status().code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(h, !invalid_program_integer.ok());
PP_EXPECT(h, invalid_program_integer.status().code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(h, !missing_active.ok());
PP_EXPECT(h, missing_active.status().code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(h, !invalid_active_buffer.ok());
PP_EXPECT(h, invalid_active_buffer.status().code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(h, !missing_uniform_location.ok());
PP_EXPECT(h, missing_uniform_location.status().code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(h, !invalid_uniform_name.ok());
PP_EXPECT(h, invalid_uniform_name.status().code == pp::foundation::StatusCode::invalid_argument);
}
void updates_texture_2d_through_dispatch(pp::tests::Harness& h)
{
recorded_binding_calls.clear();
@@ -3043,6 +3558,13 @@ int main()
harness.run("sets_scalar_uniforms_through_dispatch", sets_scalar_uniforms_through_dispatch);
harness.run("queries_attribute_location_through_dispatch", queries_attribute_location_through_dispatch);
harness.run("rejects_invalid_program_uniform_dispatch", rejects_invalid_program_uniform_dispatch);
harness.run("compiles_shader_source_through_dispatch", compiles_shader_source_through_dispatch);
harness.run("rejects_invalid_shader_compile_dispatch", rejects_invalid_shader_compile_dispatch);
harness.run("deletes_shader_through_dispatch", deletes_shader_through_dispatch);
harness.run("links_shader_program_through_dispatch", links_shader_program_through_dispatch);
harness.run("rejects_invalid_shader_program_link_dispatch", rejects_invalid_shader_program_link_dispatch);
harness.run("discovers_program_uniforms_through_dispatch", discovers_program_uniforms_through_dispatch);
harness.run("rejects_invalid_uniform_discovery_dispatch", rejects_invalid_uniform_discovery_dispatch);
harness.run("updates_texture_2d_through_dispatch", updates_texture_2d_through_dispatch);
harness.run("generates_texture_2d_mipmaps_through_dispatch", generates_texture_2d_mipmaps_through_dispatch);
harness.run("reads_back_texture_2d_through_framebuffer_dispatch", reads_back_texture_2d_through_framebuffer_dispatch);