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 `Shader` no longer spells GL enum
names directly. It also owns the PanoPainter shader uniform catalog and legacy hash names directly. It also owns the PanoPainter shader uniform catalog and legacy hash
mapping used by `Shader` active-uniform discovery and the uniform uniqueness 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/ renderer API viewport/scissor rect conversion, default depth/program-point/
line-smooth state, blend factor/equation, and UI render-target RGBA8 format line-smooth state, blend factor/equation, and UI render-target RGBA8 format
tokens are cataloged and tested here too, including the legacy convert command 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 uniform catalog validation covers the 43 legacy uniform
names used by `Shader`, preserves the legacy hash ids, and rejects empty, names used by `Shader`, preserves the legacy hash ids, and rejects empty,
unnamed, null-name, mismatched-hash, and duplicate-name catalogs. 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 - `pp_renderer_gl_command_plan_tests` covers the headless OpenGL command
planner for recorded render-pass clear masks/values, viewport/scissor state, planner for recorded render-pass clear masks/values, viewport/scissor state,
blend/depth/sampler state, texture format mapping, mesh/draw primitive modes, 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(); 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 std::uint32_t extension_count_query() noexcept
{ {
return gl_num_extensions; 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 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 OpenGlBlendEquationSeparateFn = void (*)(std::uint32_t color_equation, std::uint32_t alpha_equation) noexcept;
using OpenGlUseProgramFn = void (*)(std::uint32_t program) 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 OpenGlBindFramebufferFn = void (*)(std::uint32_t target, std::uint32_t framebuffer) noexcept;
using OpenGlBindTextureFn = void (*)(std::uint32_t target, std::uint32_t texture) 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; using OpenGlBindSamplerFn = void (*)(std::uint32_t unit, std::uint32_t sampler) noexcept;
@@ -433,6 +445,43 @@ struct OpenGlSamplerBindDispatch {
OpenGlBindSamplerFn bind_sampler = nullptr; 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( [[nodiscard]] OpenGlCapabilities detect_opengl_capabilities(
std::span<const std::string_view> extensions, std::span<const std::string_view> extensions,
OpenGlRuntime runtime) noexcept; OpenGlRuntime runtime) noexcept;
@@ -520,6 +569,40 @@ struct OpenGlSamplerBindDispatch {
std::uint32_t unit, std::uint32_t unit,
std::uint32_t sampler_id, std::uint32_t sampler_id,
OpenGlSamplerBindDispatch dispatch) noexcept; 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_count_query() noexcept;
[[nodiscard]] std::uint32_t extension_string_name() 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()); 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); std::string data((char*)a.read_all(), a.m_len);
// split path // split path
std::string name, base, ext; std::string base;
std::regex reg_path(R"((.*)[\\/]([^\\/]+)\.(\w+)$)"); std::regex reg_path(R"((.*)[\\/]([^\\/]+)\.(\w+)$)");
std::smatch m; std::smatch m;
if (std::regex_search(path, m, reg_path)) if (std::regex_search(path, m, reg_path))
{ {
base = m[1].str(); base = m[1].str();
name = m[2].str();
ext = m[3].str();
} }
for (const auto& l : split(data, '\n')) for (const auto& l : split(data, '\n'))
@@ -319,8 +365,14 @@ void Shader::destroy()
{ {
App::I->render_task_async([prog=prog] App::I->render_task_async([prog=prog]
{ {
glUseProgram(0); const auto status = pp::renderer::gl::delete_opengl_program(
glDeleteProgram(prog); 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; prog = 0;
} }
@@ -329,54 +381,148 @@ void Shader::destroy()
void Shader::use() 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) void Shader::u_vec4(kShaderUniform id, const glm::vec4& v)
{ {
if (m_umap.count(id) == 0) if (m_umap.count(id) == 0)
LOG("UNIFORM vec4 %d NOT FOUND in shader %d", (int)id, (int)name) 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) void Shader::u_vec3(kShaderUniform id, const glm::vec3& v)
{ {
if (m_umap.count(id) == 0) if (m_umap.count(id) == 0)
LOG("UNIFORM vec3 %d NOT FOUND in shader %d", (int)id, (int)name) 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) void Shader::u_vec2(kShaderUniform id, const glm::vec2& v)
{ {
if (m_umap.count(id) == 0) if (m_umap.count(id) == 0)
LOG("UNIFORM vec2 %d NOT FOUND in shader %d", (int)id, (int)name) 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) void Shader::u_mat4(kShaderUniform id, const glm::mat4& m)
{ {
if (m_umap.count(id) == 0) if (m_umap.count(id) == 0)
LOG("UNIFORM mat4 %d NOT FOUND in shader %d", (int)id, (int)name) 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) void Shader::u_int(kShaderUniform id, int i)
{ {
if (m_umap.count(id) == 0) if (m_umap.count(id) == 0)
LOG("UNIFORM int %d NOT FOUND in shader %d", (int)id, (int)name) LOG("UNIFORM int %d NOT FOUND in shader %d", (int)id, (int)name)
else 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) 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) void Shader::u_float(kShaderUniform id, float f)
{ {
if (m_umap.count(id) == 0) if (m_umap.count(id) == 0)
LOG("UNIFORM float %d NOT FOUND in shader %d", (int)id, (int)name) 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) 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) bool ShaderManager::load(kShader id, const std::string& path)

View File

@@ -96,6 +96,21 @@ struct RecordedOpenGlSamplerBorderCall {
const float* values = nullptr; 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<RecordedOpenGlStateCall> recorded_state_calls;
std::vector<std::uint32_t> recorded_string_queries; std::vector<std::uint32_t> recorded_string_queries;
std::vector<pp::renderer::gl::OpenGlDefaultClear> recorded_clear_calls; 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<std::uint32_t> recorded_generated_sampler_counts;
std::vector<RecordedOpenGlSamplerParameterCall> recorded_sampler_parameter_calls; std::vector<RecordedOpenGlSamplerParameterCall> recorded_sampler_parameter_calls;
std::vector<RecordedOpenGlSamplerBorderCall> recorded_sampler_border_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_texture_id = 91U;
std::uint32_t next_framebuffer_id = 44U; std::uint32_t next_framebuffer_id = 44U;
std::uint32_t next_sampler_id = 71U; 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) void detects_common_extension_capabilities(pp::tests::Harness& h)
{ {
constexpr std::array<std::string_view, 2> extensions { 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); 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) void updates_texture_2d_through_dispatch(pp::tests::Harness& h)
{ {
recorded_binding_calls.clear(); 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("sets_sampler_border_color_through_dispatch", sets_sampler_border_color_through_dispatch);
harness.run("binds_sampler_through_dispatch", binds_sampler_through_dispatch); harness.run("binds_sampler_through_dispatch", binds_sampler_through_dispatch);
harness.run("rejects_invalid_sampler_dispatch", rejects_invalid_sampler_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("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("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); harness.run("reads_back_texture_2d_through_framebuffer_dispatch", reads_back_texture_2d_through_framebuffer_dispatch);