Route shader runtime calls through renderer GL

This commit is contained in:
2026-06-03 06:53:51 +02:00
parent f20595aff6
commit acdaf3bb8e
6 changed files with 717 additions and 17 deletions

View File

@@ -227,7 +227,10 @@ Known local toolchain state:
`Shader` no longer spells GL enum
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. App OpenGL initialization debug severity, debug output, GL info string,
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.
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
tokens are cataloged and tested here too, including the legacy convert command

View File

@@ -1119,6 +1119,10 @@ Results:
uniform catalog validation covers the 43 legacy uniform
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.
- `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

@@ -883,6 +883,153 @@ pp::foundation::Status bind_opengl_sampler_object(
return pp::foundation::Status::success();
}
pp::foundation::Status use_opengl_program(
std::uint32_t program_id,
OpenGlProgramUseDispatch dispatch) noexcept
{
if (dispatch.use_program == nullptr) {
return pp::foundation::Status::invalid_argument("OpenGL program use dispatch callback must not be null");
}
dispatch.use_program(program_id);
return pp::foundation::Status::success();
}
pp::foundation::Status delete_opengl_program(
std::uint32_t program_id,
OpenGlProgramDeleteDispatch dispatch) noexcept
{
if (dispatch.use_program == nullptr || dispatch.delete_program == nullptr) {
return pp::foundation::Status::invalid_argument("OpenGL program delete dispatch callbacks must not be null");
}
if (program_id == 0U) {
return pp::foundation::Status::success();
}
dispatch.use_program(default_framebuffer_id());
dispatch.delete_program(program_id);
return pp::foundation::Status::success();
}
pp::foundation::Status set_opengl_uniform_vec4(
std::int32_t location,
const float* values,
OpenGlUniformVec4Dispatch dispatch) noexcept
{
if (dispatch.uniform_4fv == nullptr) {
return pp::foundation::Status::invalid_argument("OpenGL vec4 uniform dispatch callback must not be null");
}
if (values == nullptr) {
return pp::foundation::Status::invalid_argument("OpenGL vec4 uniform values must not be null");
}
dispatch.uniform_4fv(location, 1, values);
return pp::foundation::Status::success();
}
pp::foundation::Status set_opengl_uniform_vec3(
std::int32_t location,
const float* values,
OpenGlUniformVec3Dispatch dispatch) noexcept
{
if (dispatch.uniform_3fv == nullptr) {
return pp::foundation::Status::invalid_argument("OpenGL vec3 uniform dispatch callback must not be null");
}
if (values == nullptr) {
return pp::foundation::Status::invalid_argument("OpenGL vec3 uniform values must not be null");
}
dispatch.uniform_3fv(location, 1, values);
return pp::foundation::Status::success();
}
pp::foundation::Status set_opengl_uniform_vec2(
std::int32_t location,
const float* values,
OpenGlUniformVec2Dispatch dispatch) noexcept
{
if (dispatch.uniform_2fv == nullptr) {
return pp::foundation::Status::invalid_argument("OpenGL vec2 uniform dispatch callback must not be null");
}
if (values == nullptr) {
return pp::foundation::Status::invalid_argument("OpenGL vec2 uniform values must not be null");
}
dispatch.uniform_2fv(location, 1, values);
return pp::foundation::Status::success();
}
pp::foundation::Status set_opengl_uniform_mat4(
std::int32_t location,
const float* values,
OpenGlUniformMat4Dispatch dispatch) noexcept
{
if (dispatch.uniform_matrix_4fv == nullptr) {
return pp::foundation::Status::invalid_argument("OpenGL mat4 uniform dispatch callback must not be null");
}
if (values == nullptr) {
return pp::foundation::Status::invalid_argument("OpenGL mat4 uniform values must not be null");
}
dispatch.uniform_matrix_4fv(
location,
1,
static_cast<std::uint8_t>(matrix_uniform_not_transposed()),
values);
return pp::foundation::Status::success();
}
pp::foundation::Status set_opengl_uniform_int(
std::int32_t location,
std::int32_t value,
OpenGlUniformIntDispatch dispatch) noexcept
{
if (dispatch.uniform_1i == nullptr) {
return pp::foundation::Status::invalid_argument("OpenGL int uniform dispatch callback must not be null");
}
dispatch.uniform_1i(location, value);
return pp::foundation::Status::success();
}
pp::foundation::Status set_opengl_uniform_float(
std::int32_t location,
float value,
OpenGlUniformFloatDispatch dispatch) noexcept
{
if (dispatch.uniform_1f == nullptr) {
return pp::foundation::Status::invalid_argument("OpenGL float uniform dispatch callback must not be null");
}
dispatch.uniform_1f(location, value);
return pp::foundation::Status::success();
}
pp::foundation::Result<std::int32_t> get_opengl_attribute_location(
std::uint32_t program_id,
const char* attribute_name,
OpenGlAttributeLocationDispatch dispatch) noexcept
{
if (dispatch.get_attrib_location == nullptr) {
return pp::foundation::Result<std::int32_t>::failure(
pp::foundation::Status::invalid_argument(
"OpenGL attribute-location dispatch callback must not be null"));
}
if (program_id == 0U || attribute_name == nullptr || attribute_name[0] == '\0') {
return pp::foundation::Result<std::int32_t>::failure(
pp::foundation::Status::invalid_argument("OpenGL attribute-location parameters are invalid"));
}
return pp::foundation::Result<std::int32_t>::success(
dispatch.get_attrib_location(program_id, attribute_name));
}
std::uint32_t extension_count_query() noexcept
{
return gl_num_extensions;

View File

@@ -220,6 +220,18 @@ using OpenGlScissorFn = void (*)(std::int32_t x, std::int32_t y, std::int32_t wi
using OpenGlBlendFuncFn = void (*)(std::uint32_t source_factor, std::uint32_t destination_factor) noexcept;
using OpenGlBlendEquationSeparateFn = void (*)(std::uint32_t color_equation, std::uint32_t alpha_equation) noexcept;
using OpenGlUseProgramFn = void (*)(std::uint32_t program) noexcept;
using OpenGlDeleteProgramFn = void (*)(std::uint32_t program) noexcept;
using OpenGlUniform4fvFn = void (*)(std::int32_t location, std::int32_t count, const float* values) noexcept;
using OpenGlUniform3fvFn = void (*)(std::int32_t location, std::int32_t count, const float* values) noexcept;
using OpenGlUniform2fvFn = void (*)(std::int32_t location, std::int32_t count, const float* values) noexcept;
using OpenGlUniformMatrix4fvFn = void (*)(
std::int32_t location,
std::int32_t count,
std::uint8_t transpose,
const float* values) noexcept;
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 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;
@@ -433,6 +445,43 @@ struct OpenGlSamplerBindDispatch {
OpenGlBindSamplerFn bind_sampler = nullptr;
};
struct OpenGlProgramUseDispatch {
OpenGlUseProgramFn use_program = nullptr;
};
struct OpenGlProgramDeleteDispatch {
OpenGlUseProgramFn use_program = nullptr;
OpenGlDeleteProgramFn delete_program = nullptr;
};
struct OpenGlUniformVec4Dispatch {
OpenGlUniform4fvFn uniform_4fv = nullptr;
};
struct OpenGlUniformVec3Dispatch {
OpenGlUniform3fvFn uniform_3fv = nullptr;
};
struct OpenGlUniformVec2Dispatch {
OpenGlUniform2fvFn uniform_2fv = nullptr;
};
struct OpenGlUniformMat4Dispatch {
OpenGlUniformMatrix4fvFn uniform_matrix_4fv = nullptr;
};
struct OpenGlUniformIntDispatch {
OpenGlUniform1iFn uniform_1i = nullptr;
};
struct OpenGlUniformFloatDispatch {
OpenGlUniform1fFn uniform_1f = nullptr;
};
struct OpenGlAttributeLocationDispatch {
OpenGlGetAttribLocationFn get_attrib_location = nullptr;
};
[[nodiscard]] OpenGlCapabilities detect_opengl_capabilities(
std::span<const std::string_view> extensions,
OpenGlRuntime runtime) noexcept;
@@ -520,6 +569,40 @@ struct OpenGlSamplerBindDispatch {
std::uint32_t unit,
std::uint32_t sampler_id,
OpenGlSamplerBindDispatch dispatch) noexcept;
[[nodiscard]] pp::foundation::Status use_opengl_program(
std::uint32_t program_id,
OpenGlProgramUseDispatch dispatch) noexcept;
[[nodiscard]] pp::foundation::Status delete_opengl_program(
std::uint32_t program_id,
OpenGlProgramDeleteDispatch dispatch) noexcept;
[[nodiscard]] pp::foundation::Status set_opengl_uniform_vec4(
std::int32_t location,
const float* values,
OpenGlUniformVec4Dispatch dispatch) noexcept;
[[nodiscard]] pp::foundation::Status set_opengl_uniform_vec3(
std::int32_t location,
const float* values,
OpenGlUniformVec3Dispatch dispatch) noexcept;
[[nodiscard]] pp::foundation::Status set_opengl_uniform_vec2(
std::int32_t location,
const float* values,
OpenGlUniformVec2Dispatch dispatch) noexcept;
[[nodiscard]] pp::foundation::Status set_opengl_uniform_mat4(
std::int32_t location,
const float* values,
OpenGlUniformMat4Dispatch dispatch) noexcept;
[[nodiscard]] pp::foundation::Status set_opengl_uniform_int(
std::int32_t location,
std::int32_t value,
OpenGlUniformIntDispatch dispatch) noexcept;
[[nodiscard]] pp::foundation::Status set_opengl_uniform_float(
std::int32_t location,
float value,
OpenGlUniformFloatDispatch dispatch) noexcept;
[[nodiscard]] pp::foundation::Result<std::int32_t> get_opengl_attribute_location(
std::uint32_t program_id,
const char* attribute_name,
OpenGlAttributeLocationDispatch dispatch) noexcept;
[[nodiscard]] std::uint32_t extension_count_query() noexcept;
[[nodiscard]] std::uint32_t extension_string_name() noexcept;

View File

@@ -35,9 +35,57 @@ namespace {
return static_cast<GLenum>(pp::renderer::gl::active_uniform_count_query());
}
[[nodiscard]] GLboolean matrix_uniform_not_transposed() noexcept
void use_opengl_program(std::uint32_t program) noexcept
{
return static_cast<GLboolean>(pp::renderer::gl::matrix_uniform_not_transposed());
glUseProgram(static_cast<GLuint>(program));
}
void delete_opengl_program(std::uint32_t program) noexcept
{
glDeleteProgram(static_cast<GLuint>(program));
}
void set_opengl_uniform_4fv(std::int32_t location, std::int32_t count, const float* values) noexcept
{
glUniform4fv(static_cast<GLint>(location), static_cast<GLsizei>(count), values);
}
void set_opengl_uniform_3fv(std::int32_t location, std::int32_t count, const float* values) noexcept
{
glUniform3fv(static_cast<GLint>(location), static_cast<GLsizei>(count), values);
}
void set_opengl_uniform_2fv(std::int32_t location, std::int32_t count, const float* values) noexcept
{
glUniform2fv(static_cast<GLint>(location), static_cast<GLsizei>(count), values);
}
void set_opengl_uniform_matrix_4fv(
std::int32_t location,
std::int32_t count,
std::uint8_t transpose,
const float* values) noexcept
{
glUniformMatrix4fv(
static_cast<GLint>(location),
static_cast<GLsizei>(count),
static_cast<GLboolean>(transpose),
values);
}
void set_opengl_uniform_1i(std::int32_t location, std::int32_t value) noexcept
{
glUniform1i(static_cast<GLint>(location), static_cast<GLint>(value));
}
void set_opengl_uniform_1f(std::int32_t location, float value) noexcept
{
glUniform1f(static_cast<GLint>(location), value);
}
std::int32_t get_opengl_attrib_location(std::uint32_t program, const char* name) noexcept
{
return static_cast<std::int32_t>(glGetAttribLocation(static_cast<GLuint>(program), name));
}
}
@@ -65,14 +113,12 @@ std::string Shader::read(const std::string& path)
std::string data((char*)a.read_all(), a.m_len);
// split path
std::string name, base, ext;
std::string base;
std::regex reg_path(R"((.*)[\\/]([^\\/]+)\.(\w+)$)");
std::smatch m;
if (std::regex_search(path, m, reg_path))
{
base = m[1].str();
name = m[2].str();
ext = m[3].str();
}
for (const auto& l : split(data, '\n'))
@@ -319,8 +365,14 @@ void Shader::destroy()
{
App::I->render_task_async([prog=prog]
{
glUseProgram(0);
glDeleteProgram(prog);
const auto status = pp::renderer::gl::delete_opengl_program(
static_cast<std::uint32_t>(prog),
pp::renderer::gl::OpenGlProgramDeleteDispatch {
.use_program = use_opengl_program,
.delete_program = delete_opengl_program,
});
if (!status.ok())
LOG("Shader::destroy() failed because: %s", status.message);
});
prog = 0;
}
@@ -329,54 +381,148 @@ void Shader::destroy()
void Shader::use()
{
glUseProgram(prog);
const auto status = pp::renderer::gl::use_opengl_program(
static_cast<std::uint32_t>(prog),
pp::renderer::gl::OpenGlProgramUseDispatch {
.use_program = use_opengl_program,
});
if (!status.ok())
LOG("Shader::use() failed because: %s", status.message);
}
void Shader::u_vec4(kShaderUniform id, const glm::vec4& v)
{
if (m_umap.count(id) == 0)
LOG("UNIFORM vec4 %d NOT FOUND in shader %d", (int)id, (int)name)
else glUniform4fv(m_umap[id], 1, glm::value_ptr(v));
else
{
const auto status = pp::renderer::gl::set_opengl_uniform_vec4(
static_cast<std::int32_t>(m_umap[id]),
glm::value_ptr(v),
pp::renderer::gl::OpenGlUniformVec4Dispatch {
.uniform_4fv = set_opengl_uniform_4fv,
});
if (!status.ok())
LOG("Shader::u_vec4() failed because: %s", status.message);
}
}
void Shader::u_vec3(kShaderUniform id, const glm::vec3& v)
{
if (m_umap.count(id) == 0)
LOG("UNIFORM vec3 %d NOT FOUND in shader %d", (int)id, (int)name)
else glUniform3fv(m_umap[id], 1, glm::value_ptr(v));
else
{
const auto status = pp::renderer::gl::set_opengl_uniform_vec3(
static_cast<std::int32_t>(m_umap[id]),
glm::value_ptr(v),
pp::renderer::gl::OpenGlUniformVec3Dispatch {
.uniform_3fv = set_opengl_uniform_3fv,
});
if (!status.ok())
LOG("Shader::u_vec3() failed because: %s", status.message);
}
}
void Shader::u_vec2(kShaderUniform id, const glm::vec2& v)
{
if (m_umap.count(id) == 0)
LOG("UNIFORM vec2 %d NOT FOUND in shader %d", (int)id, (int)name)
else glUniform2fv(m_umap[id], 1, glm::value_ptr(v));
else
{
const auto status = pp::renderer::gl::set_opengl_uniform_vec2(
static_cast<std::int32_t>(m_umap[id]),
glm::value_ptr(v),
pp::renderer::gl::OpenGlUniformVec2Dispatch {
.uniform_2fv = set_opengl_uniform_2fv,
});
if (!status.ok())
LOG("Shader::u_vec2() failed because: %s", status.message);
}
}
void Shader::u_mat4(kShaderUniform id, const glm::mat4& m)
{
if (m_umap.count(id) == 0)
LOG("UNIFORM mat4 %d NOT FOUND in shader %d", (int)id, (int)name)
else glUniformMatrix4fv(m_umap[id], 1, matrix_uniform_not_transposed(), glm::value_ptr(m));
else
{
const auto status = pp::renderer::gl::set_opengl_uniform_mat4(
static_cast<std::int32_t>(m_umap[id]),
glm::value_ptr(m),
pp::renderer::gl::OpenGlUniformMat4Dispatch {
.uniform_matrix_4fv = set_opengl_uniform_matrix_4fv,
});
if (!status.ok())
LOG("Shader::u_mat4() failed because: %s", status.message);
}
}
void Shader::u_int(kShaderUniform id, int i)
{
if (m_umap.count(id) == 0)
LOG("UNIFORM int %d NOT FOUND in shader %d", (int)id, (int)name)
else
glUniform1i(m_umap[id], i);
{
const auto status = pp::renderer::gl::set_opengl_uniform_int(
static_cast<std::int32_t>(m_umap[id]),
static_cast<std::int32_t>(i),
pp::renderer::gl::OpenGlUniformIntDispatch {
.uniform_1i = set_opengl_uniform_1i,
});
if (!status.ok())
LOG("Shader::u_int() failed because: %s", status.message);
}
}
void Shader::u_int(const char* uniform_name, int i)
{
glUniform1i(glGetAttribLocation(prog, uniform_name), i);
const auto location = pp::renderer::gl::get_opengl_attribute_location(
static_cast<std::uint32_t>(prog),
uniform_name,
pp::renderer::gl::OpenGlAttributeLocationDispatch {
.get_attrib_location = get_opengl_attrib_location,
});
if (!location.ok())
{
LOG("Shader::u_int(name) lookup failed because: %s", location.status().message);
return;
}
const auto status = pp::renderer::gl::set_opengl_uniform_int(
location.value(),
static_cast<std::int32_t>(i),
pp::renderer::gl::OpenGlUniformIntDispatch {
.uniform_1i = set_opengl_uniform_1i,
});
if (!status.ok())
LOG("Shader::u_int(name) failed because: %s", status.message);
}
void Shader::u_float(kShaderUniform id, float f)
{
if (m_umap.count(id) == 0)
LOG("UNIFORM float %d NOT FOUND in shader %d", (int)id, (int)name)
else glUniform1f(m_umap[id], f);
else
{
const auto status = pp::renderer::gl::set_opengl_uniform_float(
static_cast<std::int32_t>(m_umap[id]),
f,
pp::renderer::gl::OpenGlUniformFloatDispatch {
.uniform_1f = set_opengl_uniform_1f,
});
if (!status.ok())
LOG("Shader::u_float() failed because: %s", status.message);
}
}
GLint Shader::GetAttribLocation(const char* attribute_name)
{
return glGetAttribLocation(prog, attribute_name);
const auto location = pp::renderer::gl::get_opengl_attribute_location(
static_cast<std::uint32_t>(prog),
attribute_name,
pp::renderer::gl::OpenGlAttributeLocationDispatch {
.get_attrib_location = get_opengl_attrib_location,
});
if (!location.ok())
{
LOG("Shader::GetAttribLocation() failed because: %s", location.status().message);
return -1;
}
return static_cast<GLint>(location.value());
}
bool ShaderManager::load(kShader id, const std::string& path)

View File

@@ -96,6 +96,21 @@ struct RecordedOpenGlSamplerBorderCall {
const float* values = nullptr;
};
struct RecordedOpenGlUniformFloatVectorCall {
std::int32_t location = 0;
std::int32_t count = 0;
const float* values = nullptr;
std::uint8_t transpose = 0;
std::uint32_t component_count = 0;
};
struct RecordedOpenGlUniformScalarCall {
std::int32_t location = 0;
std::int32_t int_value = 0;
float float_value = 0.0F;
bool is_float = false;
};
std::vector<RecordedOpenGlStateCall> recorded_state_calls;
std::vector<std::uint32_t> recorded_string_queries;
std::vector<pp::renderer::gl::OpenGlDefaultClear> recorded_clear_calls;
@@ -118,6 +133,11 @@ std::vector<RecordedOpenGlBlitFramebufferCall> recorded_blit_framebuffer_calls;
std::vector<std::uint32_t> recorded_generated_sampler_counts;
std::vector<RecordedOpenGlSamplerParameterCall> recorded_sampler_parameter_calls;
std::vector<RecordedOpenGlSamplerBorderCall> recorded_sampler_border_calls;
std::vector<std::uint32_t> recorded_deleted_programs;
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::uint32_t next_texture_id = 91U;
std::uint32_t next_framebuffer_id = 44U;
std::uint32_t next_sampler_id = 71U;
@@ -495,6 +515,83 @@ void record_sampler_parameter_fv(
});
}
void record_delete_program(std::uint32_t program) noexcept
{
recorded_deleted_programs.push_back(program);
}
void record_uniform_4fv(std::int32_t location, std::int32_t count, const float* values) noexcept
{
recorded_uniform_vector_calls.push_back(RecordedOpenGlUniformFloatVectorCall {
.location = location,
.count = count,
.values = values,
.component_count = 4U,
});
}
void record_uniform_3fv(std::int32_t location, std::int32_t count, const float* values) noexcept
{
recorded_uniform_vector_calls.push_back(RecordedOpenGlUniformFloatVectorCall {
.location = location,
.count = count,
.values = values,
.component_count = 3U,
});
}
void record_uniform_2fv(std::int32_t location, std::int32_t count, const float* values) noexcept
{
recorded_uniform_vector_calls.push_back(RecordedOpenGlUniformFloatVectorCall {
.location = location,
.count = count,
.values = values,
.component_count = 2U,
});
}
void record_uniform_matrix_4fv(
std::int32_t location,
std::int32_t count,
std::uint8_t transpose,
const float* values) noexcept
{
recorded_uniform_vector_calls.push_back(RecordedOpenGlUniformFloatVectorCall {
.location = location,
.count = count,
.values = values,
.transpose = transpose,
.component_count = 16U,
});
}
void record_uniform_1i(std::int32_t location, std::int32_t value) noexcept
{
recorded_uniform_scalar_calls.push_back(RecordedOpenGlUniformScalarCall {
.location = location,
.int_value = value,
});
}
void record_uniform_1f(std::int32_t location, float value) noexcept
{
recorded_uniform_scalar_calls.push_back(RecordedOpenGlUniformScalarCall {
.location = location,
.float_value = value,
.is_float = true,
});
}
std::int32_t record_get_attrib_location(std::uint32_t program, const char* name) noexcept
{
recorded_attrib_location_programs.push_back(program);
recorded_attrib_location_names.push_back(name == nullptr ? std::string_view {} : std::string_view { name });
if (name != nullptr && std::string_view { name } == std::string_view { "uvs" }) {
return 1;
}
return -1;
}
void detects_common_extension_capabilities(pp::tests::Harness& h)
{
constexpr std::array<std::string_view, 2> extensions {
@@ -1944,6 +2041,221 @@ void rejects_invalid_sampler_dispatch(pp::tests::Harness& h)
PP_EXPECT(h, missing_bind_dispatch.code == pp::foundation::StatusCode::invalid_argument);
}
void uses_and_deletes_programs_through_dispatch(pp::tests::Harness& h)
{
recorded_binding_calls.clear();
recorded_deleted_programs.clear();
const auto use_status = pp::renderer::gl::use_opengl_program(
91U,
pp::renderer::gl::OpenGlProgramUseDispatch {
.use_program = record_use_program,
});
const auto delete_status = pp::renderer::gl::delete_opengl_program(
91U,
pp::renderer::gl::OpenGlProgramDeleteDispatch {
.use_program = record_use_program,
.delete_program = record_delete_program,
});
const auto delete_zero_status = pp::renderer::gl::delete_opengl_program(
0U,
pp::renderer::gl::OpenGlProgramDeleteDispatch {
.use_program = record_use_program,
.delete_program = record_delete_program,
});
PP_EXPECT(h, use_status.ok());
PP_EXPECT(h, delete_status.ok());
PP_EXPECT(h, delete_zero_status.ok());
PP_EXPECT(h, recorded_binding_calls.size() == 2U);
PP_EXPECT(h, recorded_binding_calls[0].kind == RecordedOpenGlBindingCall::Kind::use_program);
PP_EXPECT(h, recorded_binding_calls[0].first == 91U);
PP_EXPECT(h, recorded_binding_calls[1].kind == RecordedOpenGlBindingCall::Kind::use_program);
PP_EXPECT(h, recorded_binding_calls[1].first == 0U);
PP_EXPECT(h, recorded_deleted_programs.size() == 1U);
PP_EXPECT(h, recorded_deleted_programs[0] == 91U);
}
void sets_vector_uniforms_through_dispatch(pp::tests::Harness& h)
{
recorded_uniform_vector_calls.clear();
constexpr std::array<float, 16> values {
1.0F, 2.0F, 3.0F, 4.0F,
5.0F, 6.0F, 7.0F, 8.0F,
9.0F, 10.0F, 11.0F, 12.0F,
13.0F, 14.0F, 15.0F, 16.0F,
};
const auto vec4_status = pp::renderer::gl::set_opengl_uniform_vec4(
4,
values.data(),
pp::renderer::gl::OpenGlUniformVec4Dispatch {
.uniform_4fv = record_uniform_4fv,
});
const auto vec3_status = pp::renderer::gl::set_opengl_uniform_vec3(
3,
values.data(),
pp::renderer::gl::OpenGlUniformVec3Dispatch {
.uniform_3fv = record_uniform_3fv,
});
const auto vec2_status = pp::renderer::gl::set_opengl_uniform_vec2(
2,
values.data(),
pp::renderer::gl::OpenGlUniformVec2Dispatch {
.uniform_2fv = record_uniform_2fv,
});
const auto mat4_status = pp::renderer::gl::set_opengl_uniform_mat4(
-1,
values.data(),
pp::renderer::gl::OpenGlUniformMat4Dispatch {
.uniform_matrix_4fv = record_uniform_matrix_4fv,
});
PP_EXPECT(h, vec4_status.ok());
PP_EXPECT(h, vec3_status.ok());
PP_EXPECT(h, vec2_status.ok());
PP_EXPECT(h, mat4_status.ok());
PP_EXPECT(h, recorded_uniform_vector_calls.size() == 4U);
PP_EXPECT(h, recorded_uniform_vector_calls[0].location == 4);
PP_EXPECT(h, recorded_uniform_vector_calls[0].count == 1);
PP_EXPECT(h, recorded_uniform_vector_calls[0].values == values.data());
PP_EXPECT(h, recorded_uniform_vector_calls[0].component_count == 4U);
PP_EXPECT(h, recorded_uniform_vector_calls[1].component_count == 3U);
PP_EXPECT(h, recorded_uniform_vector_calls[2].component_count == 2U);
PP_EXPECT(h, recorded_uniform_vector_calls[3].location == -1);
PP_EXPECT(h, recorded_uniform_vector_calls[3].transpose == 0U);
PP_EXPECT(h, recorded_uniform_vector_calls[3].component_count == 16U);
}
void sets_scalar_uniforms_through_dispatch(pp::tests::Harness& h)
{
recorded_uniform_scalar_calls.clear();
const auto int_status = pp::renderer::gl::set_opengl_uniform_int(
7,
42,
pp::renderer::gl::OpenGlUniformIntDispatch {
.uniform_1i = record_uniform_1i,
});
const auto float_status = pp::renderer::gl::set_opengl_uniform_float(
-1,
0.25F,
pp::renderer::gl::OpenGlUniformFloatDispatch {
.uniform_1f = record_uniform_1f,
});
PP_EXPECT(h, int_status.ok());
PP_EXPECT(h, float_status.ok());
PP_EXPECT(h, recorded_uniform_scalar_calls.size() == 2U);
PP_EXPECT(h, recorded_uniform_scalar_calls[0].location == 7);
PP_EXPECT(h, recorded_uniform_scalar_calls[0].int_value == 42);
PP_EXPECT(h, !recorded_uniform_scalar_calls[0].is_float);
PP_EXPECT(h, recorded_uniform_scalar_calls[1].location == -1);
PP_EXPECT(h, recorded_uniform_scalar_calls[1].float_value == 0.25F);
PP_EXPECT(h, recorded_uniform_scalar_calls[1].is_float);
}
void queries_attribute_location_through_dispatch(pp::tests::Harness& h)
{
recorded_attrib_location_programs.clear();
recorded_attrib_location_names.clear();
const auto location = pp::renderer::gl::get_opengl_attribute_location(
11U,
"uvs",
pp::renderer::gl::OpenGlAttributeLocationDispatch {
.get_attrib_location = record_get_attrib_location,
});
const auto missing = pp::renderer::gl::get_opengl_attribute_location(
11U,
"missing",
pp::renderer::gl::OpenGlAttributeLocationDispatch {
.get_attrib_location = record_get_attrib_location,
});
PP_EXPECT(h, location.ok());
PP_EXPECT(h, location.value() == 1);
PP_EXPECT(h, missing.ok());
PP_EXPECT(h, missing.value() == -1);
PP_EXPECT(h, recorded_attrib_location_programs.size() == 2U);
PP_EXPECT(h, recorded_attrib_location_programs[0] == 11U);
PP_EXPECT(h, recorded_attrib_location_names[0] == std::string_view("uvs"));
PP_EXPECT(h, recorded_attrib_location_names[1] == std::string_view("missing"));
}
void rejects_invalid_program_uniform_dispatch(pp::tests::Harness& h)
{
constexpr std::array<float, 4> values { 1.0F, 2.0F, 3.0F, 4.0F };
const auto missing_use = pp::renderer::gl::use_opengl_program(
1U,
pp::renderer::gl::OpenGlProgramUseDispatch {});
const auto missing_delete = pp::renderer::gl::delete_opengl_program(
1U,
pp::renderer::gl::OpenGlProgramDeleteDispatch {
.use_program = record_use_program,
});
const auto missing_vec4 = pp::renderer::gl::set_opengl_uniform_vec4(
1,
values.data(),
pp::renderer::gl::OpenGlUniformVec4Dispatch {});
const auto null_vec3 = pp::renderer::gl::set_opengl_uniform_vec3(
1,
nullptr,
pp::renderer::gl::OpenGlUniformVec3Dispatch {
.uniform_3fv = record_uniform_3fv,
});
const auto missing_vec2 = pp::renderer::gl::set_opengl_uniform_vec2(
1,
values.data(),
pp::renderer::gl::OpenGlUniformVec2Dispatch {});
const auto null_mat4 = pp::renderer::gl::set_opengl_uniform_mat4(
1,
nullptr,
pp::renderer::gl::OpenGlUniformMat4Dispatch {
.uniform_matrix_4fv = record_uniform_matrix_4fv,
});
const auto missing_int = pp::renderer::gl::set_opengl_uniform_int(
1,
2,
pp::renderer::gl::OpenGlUniformIntDispatch {});
const auto missing_float = pp::renderer::gl::set_opengl_uniform_float(
1,
2.0F,
pp::renderer::gl::OpenGlUniformFloatDispatch {});
const auto missing_attrib_dispatch = pp::renderer::gl::get_opengl_attribute_location(
1U,
"pos",
pp::renderer::gl::OpenGlAttributeLocationDispatch {});
const auto invalid_attrib_name = pp::renderer::gl::get_opengl_attribute_location(
1U,
"",
pp::renderer::gl::OpenGlAttributeLocationDispatch {
.get_attrib_location = record_get_attrib_location,
});
PP_EXPECT(h, !missing_use.ok());
PP_EXPECT(h, missing_use.code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(h, !missing_delete.ok());
PP_EXPECT(h, missing_delete.code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(h, !missing_vec4.ok());
PP_EXPECT(h, missing_vec4.code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(h, !null_vec3.ok());
PP_EXPECT(h, null_vec3.code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(h, !missing_vec2.ok());
PP_EXPECT(h, missing_vec2.code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(h, !null_mat4.ok());
PP_EXPECT(h, null_mat4.code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(h, !missing_int.ok());
PP_EXPECT(h, missing_int.code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(h, !missing_float.ok());
PP_EXPECT(h, missing_float.code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(h, !missing_attrib_dispatch.ok());
PP_EXPECT(h, missing_attrib_dispatch.status().code == pp::foundation::StatusCode::invalid_argument);
PP_EXPECT(h, !invalid_attrib_name.ok());
PP_EXPECT(h, invalid_attrib_name.status().code == pp::foundation::StatusCode::invalid_argument);
}
void updates_texture_2d_through_dispatch(pp::tests::Harness& h)
{
recorded_binding_calls.clear();
@@ -2726,6 +3038,11 @@ int main()
harness.run("sets_sampler_border_color_through_dispatch", sets_sampler_border_color_through_dispatch);
harness.run("binds_sampler_through_dispatch", binds_sampler_through_dispatch);
harness.run("rejects_invalid_sampler_dispatch", rejects_invalid_sampler_dispatch);
harness.run("uses_and_deletes_programs_through_dispatch", uses_and_deletes_programs_through_dispatch);
harness.run("sets_vector_uniforms_through_dispatch", sets_vector_uniforms_through_dispatch);
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("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);