Route GL state snapshot through renderer GL

This commit is contained in:
2026-06-03 06:15:51 +02:00
parent 3e15b2f46c
commit 9971b2b7f2
6 changed files with 516 additions and 37 deletions

View File

@@ -245,6 +245,73 @@ pp::foundation::Status apply_panopainter_initial_state(OpenGlStateDispatch dispa
return pp::foundation::Status::success();
}
pp::foundation::Result<OpenGlSavedState> snapshot_opengl_state(OpenGlStateSnapshotDispatch dispatch) noexcept
{
if (dispatch.is_enabled == nullptr
|| dispatch.get_integer == nullptr
|| dispatch.get_float == nullptr
|| dispatch.active_texture == nullptr) {
return pp::foundation::Result<OpenGlSavedState>::failure(
pp::foundation::Status::invalid_argument("OpenGL state snapshot dispatch callbacks must not be null"));
}
OpenGlSavedState state {};
state.blend_enabled = dispatch.is_enabled(blend_state());
state.depth_test_enabled = dispatch.is_enabled(depth_test_state());
state.scissor_test_enabled = dispatch.is_enabled(scissor_test_state());
dispatch.get_integer(viewport_query(), state.viewport.data());
dispatch.get_float(color_clear_value_query(), state.clear_color.data());
dispatch.get_integer(current_program_query(), &state.program);
dispatch.get_integer(draw_framebuffer_binding_query(), &state.draw_framebuffer);
dispatch.get_integer(read_framebuffer_binding_query(), &state.read_framebuffer);
dispatch.get_integer(active_texture_query(), &state.active_texture);
dispatch.get_integer(texture_binding_cube_map_query(), &state.cube_map_binding);
for (std::size_t i = 0; i < state.texture_2d_bindings.size(); ++i) {
dispatch.active_texture(active_texture_unit(static_cast<std::uint32_t>(i)));
dispatch.get_integer(texture_binding_2d_query(), &state.texture_2d_bindings[i]);
dispatch.get_integer(sampler_binding_query(), &state.sampler_bindings[i]);
}
return pp::foundation::Result<OpenGlSavedState>::success(state);
}
pp::foundation::Status restore_opengl_state(
const OpenGlSavedState& state,
OpenGlStateRestoreDispatch dispatch) noexcept
{
if (dispatch.enable == nullptr
|| dispatch.disable == nullptr
|| dispatch.viewport == nullptr
|| dispatch.clear_color == nullptr
|| dispatch.bind_framebuffer == nullptr
|| dispatch.use_program == nullptr
|| dispatch.active_texture == nullptr
|| dispatch.bind_texture == nullptr
|| dispatch.bind_sampler == nullptr) {
return pp::foundation::Status::invalid_argument("OpenGL state restore dispatch callbacks must not be null");
}
(state.blend_enabled != 0U ? dispatch.enable : dispatch.disable)(blend_state());
(state.depth_test_enabled != 0U ? dispatch.enable : dispatch.disable)(depth_test_state());
(state.scissor_test_enabled != 0U ? dispatch.enable : dispatch.disable)(scissor_test_state());
dispatch.viewport(state.viewport[0], state.viewport[1], state.viewport[2], state.viewport[3]);
dispatch.clear_color(state.clear_color[0], state.clear_color[1], state.clear_color[2], state.clear_color[3]);
dispatch.bind_framebuffer(draw_framebuffer_target(), static_cast<std::uint32_t>(state.draw_framebuffer));
dispatch.bind_framebuffer(read_framebuffer_target(), static_cast<std::uint32_t>(state.read_framebuffer));
dispatch.use_program(static_cast<std::uint32_t>(state.program));
for (std::size_t i = 0; i < state.texture_2d_bindings.size(); ++i) {
dispatch.active_texture(active_texture_unit(static_cast<std::uint32_t>(i)));
dispatch.bind_texture(texture_2d_target(), static_cast<std::uint32_t>(state.texture_2d_bindings[i]));
dispatch.bind_sampler(static_cast<std::uint32_t>(i), static_cast<std::uint32_t>(state.sampler_bindings[i]));
}
dispatch.active_texture(static_cast<std::uint32_t>(state.active_texture));
dispatch.bind_texture(texture_cube_map_target(), static_cast<std::uint32_t>(state.cube_map_binding));
return pp::foundation::Status::success();
}
pp::foundation::Result<OpenGlRuntimeInfo> query_opengl_runtime_info(
OpenGlRuntimeInfoDispatch dispatch) noexcept
{

View File

@@ -124,9 +124,36 @@ struct OpenGlInitialState {
std::uint32_t alpha_equation = 0;
};
struct OpenGlSavedState {
std::uint8_t blend_enabled = 0;
std::uint8_t depth_test_enabled = 0;
std::uint8_t scissor_test_enabled = 0;
std::array<std::int32_t, 4> viewport {};
std::array<float, 4> clear_color {};
std::array<std::int32_t, 10> texture_2d_bindings {};
std::array<std::int32_t, 10> sampler_bindings {};
std::int32_t cube_map_binding = 0;
std::int32_t program = 0;
std::int32_t draw_framebuffer = 0;
std::int32_t read_framebuffer = 0;
std::int32_t active_texture = 0;
};
using OpenGlCapabilityFn = void (*)(std::uint32_t state) noexcept;
using OpenGlIsEnabledFn = std::uint8_t (*)(std::uint32_t state) noexcept;
using OpenGlGetIntegerFn = void (*)(std::uint32_t name, std::int32_t* value) noexcept;
using OpenGlGetFloatFn = void (*)(std::uint32_t name, float* value) noexcept;
using OpenGlActiveTextureFn = void (*)(std::uint32_t texture_unit) noexcept;
using OpenGlClearColorFn = void (*)(float r, float g, float b, float a) noexcept;
using OpenGlClearFn = void (*)(std::uint32_t mask) noexcept;
using OpenGlViewportFn = void (*)(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height) noexcept;
using OpenGlScissorFn = void (*)(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height) 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 OpenGlUseProgramFn = void (*)(std::uint32_t program) 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;
struct OpenGlStateDispatch {
OpenGlCapabilityFn enable = nullptr;
@@ -135,6 +162,25 @@ struct OpenGlStateDispatch {
OpenGlBlendEquationSeparateFn blend_equation_separate = nullptr;
};
struct OpenGlStateSnapshotDispatch {
OpenGlIsEnabledFn is_enabled = nullptr;
OpenGlGetIntegerFn get_integer = nullptr;
OpenGlGetFloatFn get_float = nullptr;
OpenGlActiveTextureFn active_texture = nullptr;
};
struct OpenGlStateRestoreDispatch {
OpenGlCapabilityFn enable = nullptr;
OpenGlCapabilityFn disable = nullptr;
OpenGlViewportFn viewport = nullptr;
OpenGlClearColorFn clear_color = nullptr;
OpenGlBindFramebufferFn bind_framebuffer = nullptr;
OpenGlUseProgramFn use_program = nullptr;
OpenGlActiveTextureFn active_texture = nullptr;
OpenGlBindTextureFn bind_texture = nullptr;
OpenGlBindSamplerFn bind_sampler = nullptr;
};
struct OpenGlRuntimeInfo {
const char* version = "";
const char* vendor = "";
@@ -153,11 +199,6 @@ struct OpenGlDefaultClear {
std::uint32_t mask = 0;
};
using OpenGlClearColorFn = void (*)(float r, float g, float b, float a) noexcept;
using OpenGlClearFn = void (*)(std::uint32_t mask) noexcept;
using OpenGlViewportFn = void (*)(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height) noexcept;
using OpenGlScissorFn = void (*)(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height) noexcept;
struct OpenGlClearDispatch {
OpenGlClearColorFn clear_color = nullptr;
OpenGlClearFn clear = nullptr;
@@ -194,6 +235,11 @@ struct OpenGlBufferClearDispatch {
OpenGlCapabilities capabilities) noexcept;
[[nodiscard]] OpenGlInitialState panopainter_initial_state() noexcept;
[[nodiscard]] pp::foundation::Status apply_panopainter_initial_state(OpenGlStateDispatch dispatch) noexcept;
[[nodiscard]] pp::foundation::Result<OpenGlSavedState> snapshot_opengl_state(
OpenGlStateSnapshotDispatch dispatch) noexcept;
[[nodiscard]] pp::foundation::Status restore_opengl_state(
const OpenGlSavedState& state,
OpenGlStateRestoreDispatch dispatch) noexcept;
[[nodiscard]] pp::foundation::Result<OpenGlRuntimeInfo> query_opengl_runtime_info(
OpenGlRuntimeInfoDispatch dispatch) noexcept;
[[nodiscard]] OpenGlDefaultClear panopainter_default_clear() noexcept;

View File

@@ -5,6 +5,70 @@
#include "app.h"
#include "renderer_gl/opengl_capabilities.h"
namespace {
std::uint8_t is_opengl_enabled(std::uint32_t state) noexcept
{
return static_cast<std::uint8_t>(glIsEnabled(static_cast<GLenum>(state)));
}
void query_opengl_integer(std::uint32_t name, std::int32_t* value) noexcept
{
glGetIntegerv(static_cast<GLenum>(name), reinterpret_cast<GLint*>(value));
}
void query_opengl_float(std::uint32_t name, float* value) noexcept
{
glGetFloatv(static_cast<GLenum>(name), value);
}
void set_opengl_active_texture(std::uint32_t texture_unit) noexcept
{
glActiveTexture(static_cast<GLenum>(texture_unit));
}
void enable_opengl_state(std::uint32_t state) noexcept
{
glEnable(static_cast<GLenum>(state));
}
void disable_opengl_state(std::uint32_t state) noexcept
{
glDisable(static_cast<GLenum>(state));
}
void set_opengl_viewport(std::int32_t x, std::int32_t y, std::int32_t width, std::int32_t height) noexcept
{
glViewport(static_cast<GLint>(x), static_cast<GLint>(y), static_cast<GLsizei>(width), static_cast<GLsizei>(height));
}
void set_opengl_clear_color(float r, float g, float b, float a) noexcept
{
glClearColor(r, g, b, a);
}
void bind_opengl_framebuffer(std::uint32_t target, std::uint32_t framebuffer) noexcept
{
glBindFramebuffer(static_cast<GLenum>(target), static_cast<GLuint>(framebuffer));
}
void use_opengl_program(std::uint32_t program) noexcept
{
glUseProgram(static_cast<GLuint>(program));
}
void bind_opengl_texture(std::uint32_t target, std::uint32_t texture) noexcept
{
glBindTexture(static_cast<GLenum>(target), static_cast<GLuint>(texture));
}
void bind_opengl_sampler(std::uint32_t unit, std::uint32_t sampler) noexcept
{
glBindSampler(static_cast<GLuint>(unit), static_cast<GLuint>(sampler));
}
}
template<>
std::vector<vertex_t> poly_remove_duplicate<vertex_t>(const std::vector<vertex_t>& v, const float tollerance)
{
@@ -727,41 +791,73 @@ void parallel_for(size_t nb_elements, std::function<void(size_t i)> functor, boo
void gl_state::save()
{
assert(App::I->is_render_thread());
blend = glIsEnabled(pp::renderer::gl::blend_state());
depth_test = glIsEnabled(pp::renderer::gl::depth_test_state());
scissor_test = glIsEnabled(pp::renderer::gl::scissor_test_state());
glGetIntegerv(pp::renderer::gl::viewport_query(), vp);
glGetFloatv(pp::renderer::gl::color_clear_value_query(), cc);
glGetIntegerv(pp::renderer::gl::current_program_query(), &program);
glGetIntegerv(pp::renderer::gl::draw_framebuffer_binding_query(), &fbd);
glGetIntegerv(pp::renderer::gl::read_framebuffer_binding_query(), &fbr);
glGetIntegerv(pp::renderer::gl::active_texture_query(), &active_tex);
glGetIntegerv(pp::renderer::gl::texture_binding_cube_map_query(), &cube);
for (int i = 0; i < 10; ++i)
{
glActiveTexture(pp::renderer::gl::active_texture_unit(static_cast<std::uint32_t>(i)));
glGetIntegerv(pp::renderer::gl::texture_binding_2d_query(), tex + i);
glGetIntegerv(pp::renderer::gl::sampler_binding_query(), sampler + i);
const auto snapshot = pp::renderer::gl::snapshot_opengl_state(
pp::renderer::gl::OpenGlStateSnapshotDispatch {
.is_enabled = is_opengl_enabled,
.get_integer = query_opengl_integer,
.get_float = query_opengl_float,
.active_texture = set_opengl_active_texture,
});
if (!snapshot.ok()) {
LOG("OpenGL state snapshot failed: %s", snapshot.status().message);
return;
}
const auto& state = snapshot.value();
blend = static_cast<GLboolean>(state.blend_enabled);
depth_test = static_cast<GLboolean>(state.depth_test_enabled);
scissor_test = static_cast<GLboolean>(state.scissor_test_enabled);
for (std::size_t i = 0; i < state.viewport.size(); ++i) {
vp[i] = static_cast<GLint>(state.viewport[i]);
cc[i] = state.clear_color[i];
}
for (std::size_t i = 0; i < state.texture_2d_bindings.size(); ++i) {
tex[i] = static_cast<GLint>(state.texture_2d_bindings[i]);
sampler[i] = static_cast<GLint>(state.sampler_bindings[i]);
}
cube = static_cast<GLint>(state.cube_map_binding);
program = static_cast<GLint>(state.program);
fbd = static_cast<GLint>(state.draw_framebuffer);
fbr = static_cast<GLint>(state.read_framebuffer);
active_tex = static_cast<GLint>(state.active_texture);
}
void gl_state::restore()
{
assert(App::I->is_render_thread());
blend ? glEnable(pp::renderer::gl::blend_state()) : glDisable(pp::renderer::gl::blend_state());
depth_test ? glEnable(pp::renderer::gl::depth_test_state()) : glDisable(pp::renderer::gl::depth_test_state());
scissor_test ? glEnable(pp::renderer::gl::scissor_test_state()) : glDisable(pp::renderer::gl::scissor_test_state());
glViewport(vp[0], vp[1], vp[2], vp[3]);
glClearColor(cc[0], cc[1], cc[2], cc[3]);
glBindFramebuffer(pp::renderer::gl::draw_framebuffer_target(), fbd);
glBindFramebuffer(pp::renderer::gl::read_framebuffer_target(), fbr);
glUseProgram(program);
for (int i = 0; i < 10; ++i)
{
glActiveTexture(pp::renderer::gl::active_texture_unit(static_cast<std::uint32_t>(i)));
glBindTexture(pp::renderer::gl::texture_2d_target(), tex[i]);
glBindSampler(i, sampler[i]);
pp::renderer::gl::OpenGlSavedState state {};
state.blend_enabled = blend;
state.depth_test_enabled = depth_test;
state.scissor_test_enabled = scissor_test;
for (std::size_t i = 0; i < state.viewport.size(); ++i) {
state.viewport[i] = static_cast<std::int32_t>(vp[i]);
state.clear_color[i] = cc[i];
}
glActiveTexture(active_tex);
glBindTexture(pp::renderer::gl::texture_cube_map_target(), cube);
for (std::size_t i = 0; i < state.texture_2d_bindings.size(); ++i) {
state.texture_2d_bindings[i] = static_cast<std::int32_t>(tex[i]);
state.sampler_bindings[i] = static_cast<std::int32_t>(sampler[i]);
}
state.cube_map_binding = static_cast<std::int32_t>(cube);
state.program = static_cast<std::int32_t>(program);
state.draw_framebuffer = static_cast<std::int32_t>(fbd);
state.read_framebuffer = static_cast<std::int32_t>(fbr);
state.active_texture = static_cast<std::int32_t>(active_tex);
const auto status = pp::renderer::gl::restore_opengl_state(
state,
pp::renderer::gl::OpenGlStateRestoreDispatch {
.enable = enable_opengl_state,
.disable = disable_opengl_state,
.viewport = set_opengl_viewport,
.clear_color = set_opengl_clear_color,
.bind_framebuffer = bind_opengl_framebuffer,
.use_program = use_opengl_program,
.active_texture = set_opengl_active_texture,
.bind_texture = bind_opengl_texture,
.bind_sampler = bind_opengl_sampler,
});
if (!status.ok())
LOG("OpenGL state restore failed: %s", status.message);
}