Move OpenGL extension query into renderer backend
This commit is contained in:
@@ -791,7 +791,9 @@ Known warnings after the current CMake app build:
|
||||
`pp_renderer_gl` now owns OpenGL runtime build-target classification through
|
||||
CMake target compile definitions and `opengl_runtime_for_current_build()`,
|
||||
so app shader startup asks the backend for desktop GL/GLES/WebGL policy
|
||||
instead of carrying local platform branches.
|
||||
instead of carrying local platform branches. It also owns headless-tested
|
||||
OpenGL extension enumeration through `query_opengl_extensions`, moving the
|
||||
extension count/string query loop out of `app_shaders.cpp`.
|
||||
- `pp_legacy_ui_core` is an object-library containment boundary because the
|
||||
retained base `Node` controls still depend on legacy renderer and app
|
||||
headers. It should shrink as layout parsing, colors, generic controls, and
|
||||
|
||||
@@ -676,6 +676,10 @@ OpenGL runtime build-target classification now lives in `pp_renderer_gl`
|
||||
through CMake-owned compile definitions and
|
||||
`opengl_runtime_for_current_build()`, so `app_shaders.cpp` no longer decides
|
||||
desktop GL/GLES/WebGL capability policy with local platform branches.
|
||||
OpenGL extension enumeration now also lives in `pp_renderer_gl` through a
|
||||
dispatch-tested `query_opengl_extensions` helper; shader startup still logs and
|
||||
applies the resulting feature flags, but the GL extension query loop is no
|
||||
longer app-owned.
|
||||
Prepared-file save/download handoff is now also part of the service contract,
|
||||
so iOS/Web export completion routes through `PlatformServices` after the app
|
||||
writes the temporary/exported payload.
|
||||
|
||||
@@ -6,16 +6,6 @@
|
||||
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] GLenum extension_count_query() noexcept
|
||||
{
|
||||
return static_cast<GLenum>(pp::renderer::gl::extension_count_query());
|
||||
}
|
||||
|
||||
[[nodiscard]] GLenum extension_string_name() noexcept
|
||||
{
|
||||
return static_cast<GLenum>(pp::renderer::gl::extension_string_name());
|
||||
}
|
||||
|
||||
[[nodiscard]] pp::renderer::gl::OpenGlCapabilities shader_manager_capabilities() noexcept
|
||||
{
|
||||
pp::renderer::gl::OpenGlCapabilities capabilities;
|
||||
@@ -27,6 +17,19 @@ namespace {
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
void query_gl_integer(std::uint32_t name, std::int32_t* value) noexcept
|
||||
{
|
||||
GLint queried_value = 0;
|
||||
glGetIntegerv(static_cast<GLenum>(name), &queried_value);
|
||||
*value = static_cast<std::int32_t>(queried_value);
|
||||
}
|
||||
|
||||
const char* query_gl_string_indexed(std::uint32_t name, std::uint32_t index) noexcept
|
||||
{
|
||||
return reinterpret_cast<const char*>(
|
||||
glGetStringi(static_cast<GLenum>(name), static_cast<GLuint>(index)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void App::initShaders()
|
||||
@@ -37,17 +40,22 @@ void App::initShaders()
|
||||
#endif // _DEBUG
|
||||
|
||||
render_task([] {
|
||||
GLint n_exts;
|
||||
glGetIntegerv(extension_count_query(), &n_exts);
|
||||
std::vector<std::string> extension_storage;
|
||||
const auto extensions_result = pp::renderer::gl::query_opengl_extensions(
|
||||
pp::renderer::gl::OpenGlExtensionQueryDispatch {
|
||||
.get_integer = query_gl_integer,
|
||||
.get_string_indexed = query_gl_string_indexed,
|
||||
});
|
||||
if (!extensions_result.ok()) {
|
||||
LOG("OpenGL extension query failed: %s", extensions_result.status().message);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& extension_storage = extensions_result.value();
|
||||
std::vector<std::string_view> extension_views;
|
||||
extension_storage.reserve(n_exts);
|
||||
extension_views.reserve(n_exts);
|
||||
for (int i = 0; i < n_exts; i++)
|
||||
{
|
||||
extension_storage.emplace_back((const char*)glGetStringi(extension_string_name(), i));
|
||||
extension_views.push_back(extension_storage.back());
|
||||
LOG("EXT: %s", extension_storage.back().c_str());
|
||||
extension_views.reserve(extension_storage.size());
|
||||
for (const auto& extension : extension_storage) {
|
||||
extension_views.push_back(extension);
|
||||
LOG("EXT: %s", extension.c_str());
|
||||
}
|
||||
|
||||
const auto runtime = pp::renderer::gl::opengl_runtime_for_current_build();
|
||||
|
||||
@@ -344,6 +344,31 @@ pp::foundation::Result<OpenGlRuntimeInfo> query_opengl_runtime_info(
|
||||
});
|
||||
}
|
||||
|
||||
pp::foundation::Result<std::vector<std::string>> query_opengl_extensions(
|
||||
OpenGlExtensionQueryDispatch dispatch)
|
||||
{
|
||||
if (dispatch.get_integer == nullptr || dispatch.get_string_indexed == nullptr) {
|
||||
return pp::foundation::Result<std::vector<std::string>>::failure(
|
||||
pp::foundation::Status::invalid_argument("OpenGL extension query callbacks must not be null"));
|
||||
}
|
||||
|
||||
std::int32_t extension_count = 0;
|
||||
dispatch.get_integer(extension_count_query(), &extension_count);
|
||||
if (extension_count <= 0) {
|
||||
return pp::foundation::Result<std::vector<std::string>>::success({});
|
||||
}
|
||||
|
||||
std::vector<std::string> extensions;
|
||||
extensions.reserve(static_cast<std::size_t>(extension_count));
|
||||
for (std::int32_t index = 0; index < extension_count; ++index) {
|
||||
const char* extension = dispatch.get_string_indexed(
|
||||
extension_string_name(),
|
||||
static_cast<std::uint32_t>(index));
|
||||
extensions.emplace_back(extension != nullptr ? extension : "");
|
||||
}
|
||||
return pp::foundation::Result<std::vector<std::string>>::success(std::move(extensions));
|
||||
}
|
||||
|
||||
OpenGlDefaultClear panopainter_default_clear() noexcept
|
||||
{
|
||||
return OpenGlDefaultClear {
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace pp::renderer::gl {
|
||||
|
||||
@@ -459,11 +461,17 @@ struct OpenGlRuntimeInfo {
|
||||
};
|
||||
|
||||
using OpenGlStringQueryFn = const char* (*)(std::uint32_t name) noexcept;
|
||||
using OpenGlIndexedStringQueryFn = const char* (*)(std::uint32_t name, std::uint32_t index) noexcept;
|
||||
|
||||
struct OpenGlRuntimeInfoDispatch {
|
||||
OpenGlStringQueryFn get_string = nullptr;
|
||||
};
|
||||
|
||||
struct OpenGlExtensionQueryDispatch {
|
||||
OpenGlGetIntegerFn get_integer = nullptr;
|
||||
OpenGlIndexedStringQueryFn get_string_indexed = nullptr;
|
||||
};
|
||||
|
||||
struct OpenGlDefaultClear {
|
||||
std::array<float, 4> color {};
|
||||
std::uint32_t mask = 0;
|
||||
@@ -690,6 +698,8 @@ struct OpenGlMeshDeleteDispatch {
|
||||
OpenGlStateRestoreDispatch dispatch) noexcept;
|
||||
[[nodiscard]] pp::foundation::Result<OpenGlRuntimeInfo> query_opengl_runtime_info(
|
||||
OpenGlRuntimeInfoDispatch dispatch) noexcept;
|
||||
[[nodiscard]] pp::foundation::Result<std::vector<std::string>> query_opengl_extensions(
|
||||
OpenGlExtensionQueryDispatch dispatch);
|
||||
[[nodiscard]] OpenGlDefaultClear panopainter_default_clear() noexcept;
|
||||
[[nodiscard]] pp::foundation::Status clear_panopainter_default_target(OpenGlClearDispatch dispatch) noexcept;
|
||||
[[nodiscard]] pp::foundation::Status apply_opengl_viewport(
|
||||
|
||||
@@ -167,6 +167,7 @@ struct RecordedOpenGlMeshDrawCall {
|
||||
|
||||
std::vector<RecordedOpenGlStateCall> recorded_state_calls;
|
||||
std::vector<std::uint32_t> recorded_string_queries;
|
||||
std::vector<std::uint32_t> recorded_indexed_string_queries;
|
||||
std::vector<pp::renderer::gl::OpenGlDefaultClear> recorded_clear_calls;
|
||||
std::vector<pp::renderer::gl::OpenGlViewportRect> recorded_viewport_calls;
|
||||
std::vector<pp::renderer::gl::OpenGlScissorRect> recorded_scissor_calls;
|
||||
@@ -188,6 +189,7 @@ 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::int32_t recorded_extension_count = 3;
|
||||
std::vector<RecordedOpenGlUniformFloatVectorCall> recorded_uniform_vector_calls;
|
||||
std::vector<RecordedOpenGlUniformScalarCall> recorded_uniform_scalar_calls;
|
||||
std::vector<std::uint32_t> recorded_attrib_location_programs;
|
||||
@@ -278,6 +280,25 @@ const char* record_string_query(std::uint32_t name) noexcept
|
||||
}
|
||||
}
|
||||
|
||||
const char* record_indexed_string_query(std::uint32_t name, std::uint32_t index) noexcept
|
||||
{
|
||||
recorded_indexed_string_queries.push_back(name);
|
||||
if (name != 0x1F03U) {
|
||||
return "unexpected";
|
||||
}
|
||||
|
||||
switch (index) {
|
||||
case 0U:
|
||||
return "GL_EXT_shader_framebuffer_fetch";
|
||||
case 1U:
|
||||
return "GL_EXT_map_buffer_alignment";
|
||||
case 2U:
|
||||
return "GL_OES_texture_float";
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void record_clear_color(float r, float g, float b, float a) noexcept
|
||||
{
|
||||
recorded_clear_calls.push_back(pp::renderer::gl::OpenGlDefaultClear {
|
||||
@@ -331,6 +352,9 @@ void record_get_integer(std::uint32_t name, std::int32_t* value) noexcept
|
||||
{
|
||||
recorded_integer_queries.push_back(name);
|
||||
switch (name) {
|
||||
case 0x821DU:
|
||||
*value = recorded_extension_count;
|
||||
break;
|
||||
case 0x0BA2U:
|
||||
value[0] = 2;
|
||||
value[1] = 4;
|
||||
@@ -1805,6 +1829,67 @@ void rejects_incomplete_app_runtime_info_dispatch(pp::tests::Harness& h)
|
||||
PP_EXPECT(h, result.status().code == pp::foundation::StatusCode::invalid_argument);
|
||||
}
|
||||
|
||||
void queries_app_extensions(pp::tests::Harness& h)
|
||||
{
|
||||
recorded_integer_queries.clear();
|
||||
recorded_indexed_string_queries.clear();
|
||||
recorded_extension_count = 3;
|
||||
|
||||
const auto result = pp::renderer::gl::query_opengl_extensions(
|
||||
pp::renderer::gl::OpenGlExtensionQueryDispatch {
|
||||
.get_integer = record_get_integer,
|
||||
.get_string_indexed = record_indexed_string_query,
|
||||
});
|
||||
|
||||
PP_EXPECT(h, result.ok());
|
||||
PP_EXPECT(h, recorded_integer_queries.size() == 1U);
|
||||
PP_EXPECT(h, recorded_integer_queries[0] == 0x821DU);
|
||||
PP_EXPECT(h, recorded_indexed_string_queries.size() == 3U);
|
||||
PP_EXPECT(h, result.value().size() == 3U);
|
||||
PP_EXPECT(h, result.value()[0] == "GL_EXT_shader_framebuffer_fetch");
|
||||
PP_EXPECT(h, result.value()[1] == "GL_EXT_map_buffer_alignment");
|
||||
PP_EXPECT(h, result.value()[2] == "GL_OES_texture_float");
|
||||
}
|
||||
|
||||
void converts_null_extension_names_to_empty_strings(pp::tests::Harness& h)
|
||||
{
|
||||
recorded_extension_count = 4;
|
||||
|
||||
const auto result = pp::renderer::gl::query_opengl_extensions(
|
||||
pp::renderer::gl::OpenGlExtensionQueryDispatch {
|
||||
.get_integer = record_get_integer,
|
||||
.get_string_indexed = record_indexed_string_query,
|
||||
});
|
||||
|
||||
PP_EXPECT(h, result.ok());
|
||||
PP_EXPECT(h, result.value().size() == 4U);
|
||||
PP_EXPECT(h, result.value()[3].empty());
|
||||
}
|
||||
|
||||
void treats_negative_extension_count_as_empty(pp::tests::Harness& h)
|
||||
{
|
||||
recorded_indexed_string_queries.clear();
|
||||
recorded_extension_count = -7;
|
||||
|
||||
const auto result = pp::renderer::gl::query_opengl_extensions(
|
||||
pp::renderer::gl::OpenGlExtensionQueryDispatch {
|
||||
.get_integer = record_get_integer,
|
||||
.get_string_indexed = record_indexed_string_query,
|
||||
});
|
||||
|
||||
PP_EXPECT(h, result.ok());
|
||||
PP_EXPECT(h, result.value().empty());
|
||||
PP_EXPECT(h, recorded_indexed_string_queries.empty());
|
||||
}
|
||||
|
||||
void rejects_incomplete_extension_query_dispatch(pp::tests::Harness& h)
|
||||
{
|
||||
const auto result = pp::renderer::gl::query_opengl_extensions(pp::renderer::gl::OpenGlExtensionQueryDispatch {});
|
||||
|
||||
PP_EXPECT(h, !result.ok());
|
||||
PP_EXPECT(h, result.status().code == pp::foundation::StatusCode::invalid_argument);
|
||||
}
|
||||
|
||||
void clears_app_default_target(pp::tests::Harness& h)
|
||||
{
|
||||
recorded_clear_calls.clear();
|
||||
@@ -4130,6 +4215,10 @@ int main()
|
||||
harness.run("rejects_incomplete_gl_state_restore_dispatch", rejects_incomplete_gl_state_restore_dispatch);
|
||||
harness.run("queries_app_runtime_info", queries_app_runtime_info);
|
||||
harness.run("rejects_incomplete_app_runtime_info_dispatch", rejects_incomplete_app_runtime_info_dispatch);
|
||||
harness.run("queries_app_extensions", queries_app_extensions);
|
||||
harness.run("converts_null_extension_names_to_empty_strings", converts_null_extension_names_to_empty_strings);
|
||||
harness.run("treats_negative_extension_count_as_empty", treats_negative_extension_count_as_empty);
|
||||
harness.run("rejects_incomplete_extension_query_dispatch", rejects_incomplete_extension_query_dispatch);
|
||||
harness.run("clears_app_default_target", clears_app_default_target);
|
||||
harness.run("rejects_incomplete_app_clear_dispatch", rejects_incomplete_app_clear_dispatch);
|
||||
harness.run("applies_viewport_dispatch", applies_viewport_dispatch);
|
||||
|
||||
Reference in New Issue
Block a user