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

@@ -465,8 +465,9 @@ Known local toolchain state:
color/buffer dispatch consumed by `App::clear`, tested app UI color/buffer dispatch consumed by `App::clear`, tested app UI
viewport/scissor dispatch consumed by `App::draw` and `App::vr_draw_ui`, viewport/scissor dispatch consumed by `App::draw` and `App::vr_draw_ui`,
tested generic capability/buffer-clear dispatch consumed by VR draw state tested generic capability/buffer-clear dispatch consumed by VR draw state
setup, plus renderer API to OpenGL token mapping and command-planning setup, tested saved-state snapshot/restore dispatch consumed by the retained
contracts used by the OpenGL parity work. `gl_state` utility, plus renderer API to OpenGL token mapping and
command-planning contracts used by the OpenGL parity work.
- `pano_cli plan-cloud-upload` exposes `pp_app_core` cloud upload availability, - `pano_cli plan-cloud-upload` exposes `pp_app_core` cloud upload availability,
new-document warning, publish prompt, and save-before-upload planning as JSON; new-document warning, publish prompt, and save-before-upload planning as JSON;
the live cloud upload command consumes the same start contract before the live cloud upload command consumes the same start contract before

View File

@@ -535,6 +535,11 @@ VR draw blend/depth state transitions and depth-buffer clears now use generic
tested `pp_renderer_gl` capability and clear dispatch contracts, reducing tested `pp_renderer_gl` capability and clear dispatch contracts, reducing
direct OpenGL execution in the retained VR app path without changing state direct OpenGL execution in the retained VR app path without changing state
restore behavior. restore behavior.
The retained `gl_state` save/restore utility now snapshots and restores through
tested `pp_renderer_gl` saved-state dispatch contracts, covering capability
state, viewport, clear color, framebuffer/program bindings, active texture,
2D texture slots, samplers, and cube-map binding without changing the legacy
utility's public fields.
Windows RenderDoc frame capture hooks now also dispatch through Windows RenderDoc frame capture hooks now also dispatch through
`PlatformServices`, keeping capture integration in the platform service while `PlatformServices`, keeping capture integration in the platform service while
leaving non-Windows adapters as no-ops. leaving non-Windows adapters as no-ops.

View File

@@ -245,6 +245,73 @@ pp::foundation::Status apply_panopainter_initial_state(OpenGlStateDispatch dispa
return pp::foundation::Status::success(); 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( pp::foundation::Result<OpenGlRuntimeInfo> query_opengl_runtime_info(
OpenGlRuntimeInfoDispatch dispatch) noexcept OpenGlRuntimeInfoDispatch dispatch) noexcept
{ {

View File

@@ -124,9 +124,36 @@ struct OpenGlInitialState {
std::uint32_t alpha_equation = 0; 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 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 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 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 { struct OpenGlStateDispatch {
OpenGlCapabilityFn enable = nullptr; OpenGlCapabilityFn enable = nullptr;
@@ -135,6 +162,25 @@ struct OpenGlStateDispatch {
OpenGlBlendEquationSeparateFn blend_equation_separate = nullptr; 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 { struct OpenGlRuntimeInfo {
const char* version = ""; const char* version = "";
const char* vendor = ""; const char* vendor = "";
@@ -153,11 +199,6 @@ struct OpenGlDefaultClear {
std::uint32_t mask = 0; 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 { struct OpenGlClearDispatch {
OpenGlClearColorFn clear_color = nullptr; OpenGlClearColorFn clear_color = nullptr;
OpenGlClearFn clear = nullptr; OpenGlClearFn clear = nullptr;
@@ -194,6 +235,11 @@ struct OpenGlBufferClearDispatch {
OpenGlCapabilities capabilities) noexcept; OpenGlCapabilities capabilities) noexcept;
[[nodiscard]] OpenGlInitialState panopainter_initial_state() noexcept; [[nodiscard]] OpenGlInitialState panopainter_initial_state() noexcept;
[[nodiscard]] pp::foundation::Status apply_panopainter_initial_state(OpenGlStateDispatch dispatch) 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( [[nodiscard]] pp::foundation::Result<OpenGlRuntimeInfo> query_opengl_runtime_info(
OpenGlRuntimeInfoDispatch dispatch) noexcept; OpenGlRuntimeInfoDispatch dispatch) noexcept;
[[nodiscard]] OpenGlDefaultClear panopainter_default_clear() noexcept; [[nodiscard]] OpenGlDefaultClear panopainter_default_clear() noexcept;

View File

@@ -5,6 +5,70 @@
#include "app.h" #include "app.h"
#include "renderer_gl/opengl_capabilities.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<> template<>
std::vector<vertex_t> poly_remove_duplicate<vertex_t>(const std::vector<vertex_t>& v, const float tollerance) 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() void gl_state::save()
{ {
assert(App::I->is_render_thread()); assert(App::I->is_render_thread());
blend = glIsEnabled(pp::renderer::gl::blend_state());
depth_test = glIsEnabled(pp::renderer::gl::depth_test_state()); const auto snapshot = pp::renderer::gl::snapshot_opengl_state(
scissor_test = glIsEnabled(pp::renderer::gl::scissor_test_state()); pp::renderer::gl::OpenGlStateSnapshotDispatch {
glGetIntegerv(pp::renderer::gl::viewport_query(), vp); .is_enabled = is_opengl_enabled,
glGetFloatv(pp::renderer::gl::color_clear_value_query(), cc); .get_integer = query_opengl_integer,
glGetIntegerv(pp::renderer::gl::current_program_query(), &program); .get_float = query_opengl_float,
glGetIntegerv(pp::renderer::gl::draw_framebuffer_binding_query(), &fbd); .active_texture = set_opengl_active_texture,
glGetIntegerv(pp::renderer::gl::read_framebuffer_binding_query(), &fbr); });
glGetIntegerv(pp::renderer::gl::active_texture_query(), &active_tex); if (!snapshot.ok()) {
glGetIntegerv(pp::renderer::gl::texture_binding_cube_map_query(), &cube); LOG("OpenGL state snapshot failed: %s", snapshot.status().message);
for (int i = 0; i < 10; ++i) return;
{
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& 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() void gl_state::restore()
{ {
assert(App::I->is_render_thread()); 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()); pp::renderer::gl::OpenGlSavedState state {};
scissor_test ? glEnable(pp::renderer::gl::scissor_test_state()) : glDisable(pp::renderer::gl::scissor_test_state()); state.blend_enabled = blend;
glViewport(vp[0], vp[1], vp[2], vp[3]); state.depth_test_enabled = depth_test;
glClearColor(cc[0], cc[1], cc[2], cc[3]); state.scissor_test_enabled = scissor_test;
glBindFramebuffer(pp::renderer::gl::draw_framebuffer_target(), fbd); for (std::size_t i = 0; i < state.viewport.size(); ++i) {
glBindFramebuffer(pp::renderer::gl::read_framebuffer_target(), fbr); state.viewport[i] = static_cast<std::int32_t>(vp[i]);
glUseProgram(program); state.clear_color[i] = cc[i];
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]);
} }
glActiveTexture(active_tex); for (std::size_t i = 0; i < state.texture_2d_bindings.size(); ++i) {
glBindTexture(pp::renderer::gl::texture_cube_map_target(), cube); 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);
} }

View File

@@ -24,11 +24,29 @@ struct RecordedOpenGlStateCall {
std::uint32_t second = 0; std::uint32_t second = 0;
}; };
struct RecordedOpenGlBindingCall {
enum class Kind {
bind_framebuffer,
use_program,
active_texture,
bind_texture,
bind_sampler,
};
Kind kind = Kind::bind_framebuffer;
std::uint32_t first = 0;
std::uint32_t second = 0;
};
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;
std::vector<pp::renderer::gl::OpenGlViewportRect> recorded_viewport_calls; std::vector<pp::renderer::gl::OpenGlViewportRect> recorded_viewport_calls;
std::vector<pp::renderer::gl::OpenGlScissorRect> recorded_scissor_calls; std::vector<pp::renderer::gl::OpenGlScissorRect> recorded_scissor_calls;
std::vector<std::uint32_t> recorded_integer_queries;
std::vector<std::uint32_t> recorded_float_queries;
std::vector<std::uint32_t> recorded_active_texture_calls;
std::vector<RecordedOpenGlBindingCall> recorded_binding_calls;
void record_enable(std::uint32_t state) noexcept void record_enable(std::uint32_t state) noexcept
{ {
@@ -116,6 +134,108 @@ void record_scissor(std::int32_t x, std::int32_t y, std::int32_t width, std::int
}); });
} }
std::uint8_t record_is_enabled(std::uint32_t state) noexcept
{
if (state == 0x0BE2U) {
return 1U;
}
if (state == 0x0B71U) {
return 0U;
}
if (state == 0x0C11U) {
return 1U;
}
return 0U;
}
void record_get_integer(std::uint32_t name, std::int32_t* value) noexcept
{
recorded_integer_queries.push_back(name);
switch (name) {
case 0x0BA2U:
value[0] = 2;
value[1] = 4;
value[2] = 640;
value[3] = 320;
break;
case 0x8B8DU:
*value = 42;
break;
case 0x8CA6U:
*value = 7;
break;
case 0x8CAAU:
*value = 9;
break;
case 0x84E0U:
*value = 0x84C4;
break;
case 0x8514U:
*value = 88;
break;
case 0x8069U:
*value = 100 + static_cast<std::int32_t>(recorded_active_texture_calls.size());
break;
case 0x8919U:
*value = 200 + static_cast<std::int32_t>(recorded_active_texture_calls.size());
break;
default:
*value = -1;
break;
}
}
void record_get_float(std::uint32_t name, float* value) noexcept
{
recorded_float_queries.push_back(name);
if (name == 0x0C22U) {
value[0] = 0.25F;
value[1] = 0.5F;
value[2] = 0.75F;
value[3] = 1.0F;
}
}
void record_active_texture(std::uint32_t texture_unit) noexcept
{
recorded_active_texture_calls.push_back(texture_unit);
}
void record_bind_framebuffer(std::uint32_t target, std::uint32_t framebuffer) noexcept
{
recorded_binding_calls.push_back(RecordedOpenGlBindingCall {
.kind = RecordedOpenGlBindingCall::Kind::bind_framebuffer,
.first = target,
.second = framebuffer,
});
}
void record_use_program(std::uint32_t program) noexcept
{
recorded_binding_calls.push_back(RecordedOpenGlBindingCall {
.kind = RecordedOpenGlBindingCall::Kind::use_program,
.first = program,
});
}
void record_bind_texture(std::uint32_t target, std::uint32_t texture) noexcept
{
recorded_binding_calls.push_back(RecordedOpenGlBindingCall {
.kind = RecordedOpenGlBindingCall::Kind::bind_texture,
.first = target,
.second = texture,
});
}
void record_bind_sampler(std::uint32_t unit, std::uint32_t sampler) noexcept
{
recorded_binding_calls.push_back(RecordedOpenGlBindingCall {
.kind = RecordedOpenGlBindingCall::Kind::bind_sampler,
.first = unit,
.second = sampler,
});
}
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 {
@@ -794,6 +914,146 @@ void rejects_incomplete_app_initialization_state_dispatch(pp::tests::Harness& h)
PP_EXPECT(h, status.code == pp::foundation::StatusCode::invalid_argument); PP_EXPECT(h, status.code == pp::foundation::StatusCode::invalid_argument);
} }
void snapshots_legacy_gl_state(pp::tests::Harness& h)
{
recorded_integer_queries.clear();
recorded_float_queries.clear();
recorded_active_texture_calls.clear();
const auto result = pp::renderer::gl::snapshot_opengl_state(
pp::renderer::gl::OpenGlStateSnapshotDispatch {
.is_enabled = record_is_enabled,
.get_integer = record_get_integer,
.get_float = record_get_float,
.active_texture = record_active_texture,
});
PP_EXPECT(h, result.ok());
const auto& state = result.value();
PP_EXPECT(h, state.blend_enabled == 1U);
PP_EXPECT(h, state.depth_test_enabled == 0U);
PP_EXPECT(h, state.scissor_test_enabled == 1U);
PP_EXPECT(h, state.viewport[0] == 2);
PP_EXPECT(h, state.viewport[1] == 4);
PP_EXPECT(h, state.viewport[2] == 640);
PP_EXPECT(h, state.viewport[3] == 320);
PP_EXPECT(h, state.clear_color[0] == 0.25F);
PP_EXPECT(h, state.clear_color[1] == 0.5F);
PP_EXPECT(h, state.clear_color[2] == 0.75F);
PP_EXPECT(h, state.clear_color[3] == 1.0F);
PP_EXPECT(h, state.program == 42);
PP_EXPECT(h, state.draw_framebuffer == 7);
PP_EXPECT(h, state.read_framebuffer == 9);
PP_EXPECT(h, state.active_texture == 0x84C4);
PP_EXPECT(h, state.cube_map_binding == 88);
PP_EXPECT(h, state.texture_2d_bindings[0] == 101);
PP_EXPECT(h, state.texture_2d_bindings[9] == 110);
PP_EXPECT(h, state.sampler_bindings[0] == 201);
PP_EXPECT(h, state.sampler_bindings[9] == 210);
PP_EXPECT(h, recorded_float_queries.size() == 1U);
PP_EXPECT(h, recorded_active_texture_calls.size() == 10U);
PP_EXPECT(h, recorded_active_texture_calls[0] == 0x84C0U);
PP_EXPECT(h, recorded_active_texture_calls[9] == 0x84C9U);
}
void rejects_incomplete_gl_state_snapshot_dispatch(pp::tests::Harness& h)
{
const auto result = pp::renderer::gl::snapshot_opengl_state(
pp::renderer::gl::OpenGlStateSnapshotDispatch {
.is_enabled = record_is_enabled,
.get_integer = record_get_integer,
.get_float = record_get_float,
});
PP_EXPECT(h, !result.ok());
PP_EXPECT(h, result.status().code == pp::foundation::StatusCode::invalid_argument);
}
void restores_legacy_gl_state(pp::tests::Harness& h)
{
recorded_state_calls.clear();
recorded_viewport_calls.clear();
recorded_clear_calls.clear();
recorded_active_texture_calls.clear();
recorded_binding_calls.clear();
pp::renderer::gl::OpenGlSavedState state {};
state.blend_enabled = 1U;
state.depth_test_enabled = 0U;
state.scissor_test_enabled = 1U;
state.viewport = { 3, 6, 800, 600 };
state.clear_color = { 0.1F, 0.2F, 0.3F, 0.4F };
state.program = 12;
state.draw_framebuffer = 21;
state.read_framebuffer = 22;
state.active_texture = 0x84C7;
state.cube_map_binding = 77;
for (std::size_t i = 0; i < state.texture_2d_bindings.size(); ++i) {
state.texture_2d_bindings[i] = 300 + static_cast<std::int32_t>(i);
state.sampler_bindings[i] = 400 + static_cast<std::int32_t>(i);
}
const auto status = pp::renderer::gl::restore_opengl_state(
state,
pp::renderer::gl::OpenGlStateRestoreDispatch {
.enable = record_enable,
.disable = record_disable,
.viewport = record_viewport,
.clear_color = record_clear_color,
.bind_framebuffer = record_bind_framebuffer,
.use_program = record_use_program,
.active_texture = record_active_texture,
.bind_texture = record_bind_texture,
.bind_sampler = record_bind_sampler,
});
PP_EXPECT(h, status.ok());
PP_EXPECT(h, recorded_state_calls.size() == 3U);
PP_EXPECT(h, recorded_state_calls[0].kind == RecordedOpenGlStateCall::Kind::enable);
PP_EXPECT(h, recorded_state_calls[0].first == 0x0BE2U);
PP_EXPECT(h, recorded_state_calls[1].kind == RecordedOpenGlStateCall::Kind::disable);
PP_EXPECT(h, recorded_state_calls[1].first == 0x0B71U);
PP_EXPECT(h, recorded_state_calls[2].kind == RecordedOpenGlStateCall::Kind::enable);
PP_EXPECT(h, recorded_state_calls[2].first == 0x0C11U);
PP_EXPECT(h, recorded_viewport_calls.size() == 1U);
PP_EXPECT(h, recorded_viewport_calls[0].width == 800);
PP_EXPECT(h, recorded_clear_calls.size() == 1U);
PP_EXPECT(h, recorded_clear_calls[0].color[2] == 0.3F);
PP_EXPECT(h, recorded_binding_calls.size() == 24U);
PP_EXPECT(h, recorded_binding_calls[0].kind == RecordedOpenGlBindingCall::Kind::bind_framebuffer);
PP_EXPECT(h, recorded_binding_calls[0].first == 0x8CA9U);
PP_EXPECT(h, recorded_binding_calls[0].second == 21U);
PP_EXPECT(h, recorded_binding_calls[1].kind == RecordedOpenGlBindingCall::Kind::bind_framebuffer);
PP_EXPECT(h, recorded_binding_calls[1].first == 0x8CA8U);
PP_EXPECT(h, recorded_binding_calls[1].second == 22U);
PP_EXPECT(h, recorded_binding_calls[2].kind == RecordedOpenGlBindingCall::Kind::use_program);
PP_EXPECT(h, recorded_binding_calls[2].first == 12U);
PP_EXPECT(h, recorded_active_texture_calls.size() == 11U);
PP_EXPECT(h, recorded_active_texture_calls[0] == 0x84C0U);
PP_EXPECT(h, recorded_active_texture_calls[9] == 0x84C9U);
PP_EXPECT(h, recorded_active_texture_calls[10] == 0x84C7U);
PP_EXPECT(h, recorded_binding_calls[3].kind == RecordedOpenGlBindingCall::Kind::bind_texture);
PP_EXPECT(h, recorded_binding_calls[3].first == 0x0DE1U);
PP_EXPECT(h, recorded_binding_calls[3].second == 300U);
PP_EXPECT(h, recorded_binding_calls[23].kind == RecordedOpenGlBindingCall::Kind::bind_texture);
PP_EXPECT(h, recorded_binding_calls[23].first == 0x8513U);
PP_EXPECT(h, recorded_binding_calls[23].second == 77U);
}
void rejects_incomplete_gl_state_restore_dispatch(pp::tests::Harness& h)
{
const auto status = pp::renderer::gl::restore_opengl_state(
pp::renderer::gl::OpenGlSavedState {},
pp::renderer::gl::OpenGlStateRestoreDispatch {
.enable = record_enable,
.disable = record_disable,
.viewport = record_viewport,
});
PP_EXPECT(h, !status.ok());
PP_EXPECT(h, status.code == pp::foundation::StatusCode::invalid_argument);
}
void queries_app_runtime_info(pp::tests::Harness& h) void queries_app_runtime_info(pp::tests::Harness& h)
{ {
recorded_string_queries.clear(); recorded_string_queries.clear();
@@ -1433,6 +1693,10 @@ int main()
harness.run("maps_app_initialization_parameters", maps_app_initialization_parameters); harness.run("maps_app_initialization_parameters", maps_app_initialization_parameters);
harness.run("applies_app_initialization_state", applies_app_initialization_state); harness.run("applies_app_initialization_state", applies_app_initialization_state);
harness.run("rejects_incomplete_app_initialization_state_dispatch", rejects_incomplete_app_initialization_state_dispatch); harness.run("rejects_incomplete_app_initialization_state_dispatch", rejects_incomplete_app_initialization_state_dispatch);
harness.run("snapshots_legacy_gl_state", snapshots_legacy_gl_state);
harness.run("rejects_incomplete_gl_state_snapshot_dispatch", rejects_incomplete_gl_state_snapshot_dispatch);
harness.run("restores_legacy_gl_state", restores_legacy_gl_state);
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("queries_app_runtime_info", queries_app_runtime_info);
harness.run("rejects_incomplete_app_runtime_info_dispatch", rejects_incomplete_app_runtime_info_dispatch); harness.run("rejects_incomplete_app_runtime_info_dispatch", rejects_incomplete_app_runtime_info_dispatch);
harness.run("clears_app_default_target", clears_app_default_target); harness.run("clears_app_default_target", clears_app_default_target);