2537 lines
88 KiB
C++
2537 lines
88 KiB
C++
#include "renderer_gl/opengl_capabilities.h"
|
|
#include "renderer_gl/shader_bindings.h"
|
|
|
|
#include <array>
|
|
#include <limits>
|
|
|
|
namespace pp::renderer::gl {
|
|
|
|
namespace {
|
|
|
|
constexpr std::uint32_t gl_unsigned_byte = 0x1401U;
|
|
constexpr std::uint32_t gl_unsigned_short = 0x1403U;
|
|
constexpr std::uint32_t gl_unsigned_int = 0x1405U;
|
|
constexpr std::uint32_t gl_float = 0x1406U;
|
|
constexpr std::uint32_t gl_half_float = 0x140BU;
|
|
constexpr std::uint32_t gl_no_error = 0U;
|
|
constexpr std::uint32_t gl_invalid_enum = 0x0500U;
|
|
constexpr std::uint32_t gl_invalid_value = 0x0501U;
|
|
constexpr std::uint32_t gl_invalid_operation = 0x0502U;
|
|
constexpr std::uint32_t gl_out_of_memory = 0x0505U;
|
|
constexpr std::uint32_t gl_invalid_framebuffer_operation = 0x0506U;
|
|
constexpr std::uint32_t gl_false = 0U;
|
|
constexpr std::uint32_t gl_points = 0x0000U;
|
|
constexpr std::uint32_t gl_lines = 0x0001U;
|
|
constexpr std::uint32_t gl_triangles = 0x0004U;
|
|
constexpr std::uint32_t gl_triangle_strip = 0x0005U;
|
|
constexpr std::uint32_t gl_fragment_shader = 0x8B30U;
|
|
constexpr std::uint32_t gl_vertex_shader = 0x8B31U;
|
|
constexpr std::uint32_t gl_compile_status = 0x8B81U;
|
|
constexpr std::uint32_t gl_link_status = 0x8B82U;
|
|
constexpr std::uint32_t gl_active_uniforms = 0x8B86U;
|
|
constexpr std::uint32_t gl_extensions = 0x1F03U;
|
|
constexpr std::uint32_t gl_num_extensions = 0x821DU;
|
|
constexpr std::uint32_t gl_version = 0x1F02U;
|
|
constexpr std::uint32_t gl_vendor = 0x1F00U;
|
|
constexpr std::uint32_t gl_renderer = 0x1F01U;
|
|
constexpr std::uint32_t gl_shading_language_version = 0x8B8CU;
|
|
constexpr std::uint32_t gl_debug_output_synchronous = 0x8242U;
|
|
constexpr std::uint32_t gl_debug_output = 0x92E0U;
|
|
constexpr std::uint32_t gl_debug_severity_high = 0x9146U;
|
|
constexpr std::uint32_t gl_debug_severity_medium = 0x9147U;
|
|
constexpr std::uint32_t gl_debug_severity_low = 0x9148U;
|
|
constexpr std::uint32_t gl_debug_severity_notification = 0x826BU;
|
|
constexpr std::uint32_t gl_zero = 0U;
|
|
constexpr std::uint32_t gl_one = 1U;
|
|
constexpr std::uint32_t gl_src_alpha = 0x0302U;
|
|
constexpr std::uint32_t gl_one_minus_src_alpha = 0x0303U;
|
|
constexpr std::uint32_t gl_dst_alpha = 0x0304U;
|
|
constexpr std::uint32_t gl_one_minus_dst_alpha = 0x0305U;
|
|
constexpr std::uint32_t gl_line_smooth = 0x0B20U;
|
|
constexpr std::uint32_t gl_viewport = 0x0BA2U;
|
|
constexpr std::uint32_t gl_blend = 0x0BE2U;
|
|
constexpr std::uint32_t gl_color_clear_value = 0x0C22U;
|
|
constexpr std::uint32_t gl_scissor_test = 0x0C11U;
|
|
constexpr std::uint32_t gl_depth_test = 0x0B71U;
|
|
constexpr std::uint32_t gl_never = 0x0200U;
|
|
constexpr std::uint32_t gl_less = 0x0201U;
|
|
constexpr std::uint32_t gl_equal = 0x0202U;
|
|
constexpr std::uint32_t gl_lequal = 0x0203U;
|
|
constexpr std::uint32_t gl_greater = 0x0204U;
|
|
constexpr std::uint32_t gl_notequal = 0x0205U;
|
|
constexpr std::uint32_t gl_gequal = 0x0206U;
|
|
constexpr std::uint32_t gl_always = 0x0207U;
|
|
constexpr std::uint32_t gl_current_program = 0x8B8DU;
|
|
constexpr std::uint32_t gl_active_texture = 0x84E0U;
|
|
constexpr std::uint32_t gl_texture_binding_2d = 0x8069U;
|
|
constexpr std::uint32_t gl_texture_binding_cube_map = 0x8514U;
|
|
constexpr std::uint32_t gl_sampler_binding = 0x8919U;
|
|
constexpr std::uint32_t gl_func_add = 0x8006U;
|
|
constexpr std::uint32_t gl_max = 0x8008U;
|
|
constexpr std::uint32_t gl_func_subtract = 0x800AU;
|
|
constexpr std::uint32_t gl_func_reverse_subtract = 0x800BU;
|
|
constexpr std::uint32_t gl_program_point_size = 0x8642U;
|
|
constexpr std::uint32_t gl_array_buffer = 0x8892U;
|
|
constexpr std::uint32_t gl_element_array_buffer = 0x8893U;
|
|
constexpr std::uint32_t gl_static_draw = 0x88E4U;
|
|
constexpr std::uint32_t gl_red = 0x1903U;
|
|
constexpr std::uint32_t gl_rgb = 0x1907U;
|
|
constexpr std::uint32_t gl_rgba = 0x1908U;
|
|
constexpr std::uint32_t gl_rg = 0x8227U;
|
|
constexpr std::uint32_t gl_r8 = 0x8229U;
|
|
constexpr std::uint32_t gl_rg8 = 0x822BU;
|
|
constexpr std::uint32_t gl_rgb8 = 0x8051U;
|
|
constexpr std::uint32_t gl_rgba8 = 0x8058U;
|
|
constexpr std::uint32_t gl_rgba32f = 0x8814U;
|
|
constexpr std::uint32_t gl_rgba16f = 0x881AU;
|
|
constexpr std::uint32_t gl_depth_stencil = 0x84F9U;
|
|
constexpr std::uint32_t gl_unsigned_int_24_8 = 0x84FAU;
|
|
constexpr std::uint32_t gl_depth24_stencil8 = 0x88F0U;
|
|
constexpr std::uint32_t gl_texture_2d = 0x0DE1U;
|
|
constexpr std::uint32_t gl_renderbuffer = 0x8D41U;
|
|
constexpr std::uint32_t gl_depth_component24 = 0x81A6U;
|
|
constexpr std::uint32_t gl_framebuffer = 0x8D40U;
|
|
constexpr std::uint32_t gl_read_framebuffer = 0x8CA8U;
|
|
constexpr std::uint32_t gl_draw_framebuffer = 0x8CA9U;
|
|
constexpr std::uint32_t gl_draw_framebuffer_binding = 0x8CA6U;
|
|
constexpr std::uint32_t gl_read_framebuffer_binding = 0x8CAAU;
|
|
constexpr std::uint32_t gl_color_attachment0 = 0x8CE0U;
|
|
constexpr std::uint32_t gl_depth_attachment = 0x8D00U;
|
|
constexpr std::uint32_t gl_framebuffer_complete = 0x8CD5U;
|
|
constexpr std::uint32_t gl_framebuffer_incomplete_attachment = 0x8CD6U;
|
|
constexpr std::uint32_t gl_framebuffer_incomplete_missing_attachment = 0x8CD7U;
|
|
constexpr std::uint32_t gl_framebuffer_incomplete_draw_buffer = 0x8CDBU;
|
|
constexpr std::uint32_t gl_framebuffer_incomplete_read_buffer = 0x8CDCU;
|
|
constexpr std::uint32_t gl_framebuffer_unsupported = 0x8CDDU;
|
|
constexpr std::uint32_t gl_framebuffer_undefined = 0x8219U;
|
|
constexpr std::uint32_t gl_framebuffer_incomplete_multisample = 0x8D56U;
|
|
constexpr std::uint32_t gl_color_buffer_bit = 0x00004000U;
|
|
constexpr std::uint32_t gl_depth_buffer_bit = 0x00000100U;
|
|
constexpr std::uint32_t gl_stencil_buffer_bit = 0x00000400U;
|
|
constexpr std::uint32_t gl_color_writemask = 0x0C23U;
|
|
constexpr std::uint32_t gl_texture_cube_map = 0x8513U;
|
|
constexpr std::uint32_t gl_texture_cube_map_positive_x = 0x8515U;
|
|
constexpr std::uint32_t gl_texture_cube_map_negative_x = 0x8516U;
|
|
constexpr std::uint32_t gl_texture_cube_map_positive_y = 0x8517U;
|
|
constexpr std::uint32_t gl_texture_cube_map_negative_y = 0x8518U;
|
|
constexpr std::uint32_t gl_texture_cube_map_positive_z = 0x8519U;
|
|
constexpr std::uint32_t gl_texture_cube_map_negative_z = 0x851AU;
|
|
constexpr std::uint32_t gl_linear = 0x2601U;
|
|
constexpr std::uint32_t gl_nearest_mipmap_nearest = 0x2700U;
|
|
constexpr std::uint32_t gl_linear_mipmap_nearest = 0x2701U;
|
|
constexpr std::uint32_t gl_nearest_mipmap_linear = 0x2702U;
|
|
constexpr std::uint32_t gl_linear_mipmap_linear = 0x2703U;
|
|
constexpr std::uint32_t gl_nearest = 0x2600U;
|
|
constexpr std::uint32_t gl_texture_mag_filter = 0x2800U;
|
|
constexpr std::uint32_t gl_texture_min_filter = 0x2801U;
|
|
constexpr std::uint32_t gl_texture_wrap_s = 0x2802U;
|
|
constexpr std::uint32_t gl_texture_wrap_t = 0x2803U;
|
|
constexpr std::uint32_t gl_texture_wrap_r = 0x8072U;
|
|
constexpr std::uint32_t gl_texture_border_color = 0x1004U;
|
|
constexpr std::uint32_t gl_repeat = 0x2901U;
|
|
constexpr std::uint32_t gl_mirrored_repeat = 0x8370U;
|
|
constexpr std::uint32_t gl_clamp_to_border = 0x812DU;
|
|
constexpr std::uint32_t gl_clamp_to_edge = 0x812FU;
|
|
constexpr std::uint32_t gl_texture0 = 0x84C0U;
|
|
constexpr std::uint32_t gl_pixel_pack_buffer = 0x88EBU;
|
|
constexpr std::uint32_t gl_pixel_unpack_buffer = 0x88ECU;
|
|
constexpr std::uint32_t gl_stream_read = 0x88E1U;
|
|
constexpr std::uint32_t gl_map_read_bit = 0x0001U;
|
|
constexpr std::uint8_t gl_boolean_false = 0U;
|
|
constexpr std::uint8_t gl_boolean_true = 1U;
|
|
constexpr std::int32_t wgl_draw_to_window_arb = 0x2001;
|
|
constexpr std::int32_t wgl_acceleration_arb = 0x2003;
|
|
constexpr std::int32_t wgl_support_opengl_arb = 0x2010;
|
|
constexpr std::int32_t wgl_double_buffer_arb = 0x2011;
|
|
constexpr std::int32_t wgl_pixel_type_arb = 0x2013;
|
|
constexpr std::int32_t wgl_color_bits_arb = 0x2014;
|
|
constexpr std::int32_t wgl_depth_bits_arb = 0x2022;
|
|
constexpr std::int32_t wgl_full_acceleration_arb = 0x2027;
|
|
constexpr std::int32_t wgl_type_rgba_arb = 0x202B;
|
|
constexpr std::int32_t wgl_context_major_version_arb = 0x2091;
|
|
constexpr std::int32_t wgl_context_minor_version_arb = 0x2092;
|
|
constexpr std::int32_t wgl_context_flags_arb = 0x2094;
|
|
constexpr std::int32_t wgl_context_forward_compatible_bit_arb = 0x0002;
|
|
constexpr std::int32_t wgl_context_profile_mask_arb = 0x9126;
|
|
constexpr std::int32_t wgl_context_core_profile_bit_arb = 0x00000001;
|
|
|
|
[[nodiscard]] bool contains(std::string_view text, std::string_view needle) noexcept
|
|
{
|
|
return text.find(needle) != std::string_view::npos;
|
|
}
|
|
|
|
}
|
|
|
|
OpenGlCapabilities detect_opengl_capabilities(
|
|
std::span<const std::string_view> extensions,
|
|
OpenGlRuntime runtime) noexcept
|
|
{
|
|
OpenGlCapabilities capabilities;
|
|
|
|
if (runtime.desktop_gl) {
|
|
capabilities.float32_textures = true;
|
|
capabilities.float32_linear = true;
|
|
capabilities.float16_textures = true;
|
|
}
|
|
|
|
for (const auto extension : extensions) {
|
|
if (contains(extension, "shader_framebuffer_fetch")) {
|
|
capabilities.framebuffer_fetch = true;
|
|
}
|
|
|
|
if (contains(extension, "map_buffer_alignment")) {
|
|
capabilities.map_buffer_alignment = true;
|
|
}
|
|
|
|
if (runtime.gles && !runtime.web) {
|
|
if (contains(extension, "texture_float") || contains(extension, "color_buffer_float")) {
|
|
capabilities.float32_textures = true;
|
|
}
|
|
|
|
if (contains(extension, "texture_float_linear")) {
|
|
capabilities.float32_linear = true;
|
|
}
|
|
|
|
if (contains(extension, "texture_half_float") || contains(extension, "color_buffer_half_float")) {
|
|
capabilities.float16_textures = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return capabilities;
|
|
}
|
|
|
|
OpenGlRuntime opengl_runtime_for_current_build() noexcept
|
|
{
|
|
OpenGlRuntime runtime;
|
|
#if defined(PP_RENDERER_GL_RUNTIME_DESKTOP)
|
|
runtime.desktop_gl = true;
|
|
#endif
|
|
#if defined(PP_RENDERER_GL_RUNTIME_GLES)
|
|
runtime.gles = true;
|
|
#endif
|
|
#if defined(PP_RENDERER_GL_RUNTIME_WEB)
|
|
runtime.web = true;
|
|
#endif
|
|
return runtime;
|
|
}
|
|
|
|
pp::renderer::RenderDeviceFeatures render_device_features(OpenGlCapabilities capabilities) noexcept
|
|
{
|
|
return pp::renderer::RenderDeviceFeatures {
|
|
.framebuffer_fetch = capabilities.framebuffer_fetch,
|
|
.texture_copy = true,
|
|
.render_target_blit = true,
|
|
.frame_capture = true,
|
|
.float16_render_targets = capabilities.float16_textures,
|
|
.float32_render_targets = capabilities.float32_textures,
|
|
.float32_linear_filtering = capabilities.float32_linear,
|
|
};
|
|
}
|
|
|
|
OpenGlFeatureState detect_opengl_feature_state(
|
|
std::span<const std::string_view> extensions,
|
|
OpenGlRuntime runtime) noexcept
|
|
{
|
|
const auto capabilities = detect_opengl_capabilities(extensions, runtime);
|
|
return OpenGlFeatureState {
|
|
.capabilities = capabilities,
|
|
.features = render_device_features(capabilities),
|
|
};
|
|
}
|
|
|
|
OpenGlInitialState panopainter_initial_state() noexcept
|
|
{
|
|
return OpenGlInitialState {
|
|
.depth_test_enabled = false,
|
|
.depth_test_state = depth_test_state(),
|
|
.source_color_factor = source_alpha_blend_factor(),
|
|
.destination_color_factor = one_minus_source_alpha_blend_factor(),
|
|
.color_equation = add_blend_equation(),
|
|
.alpha_equation = max_blend_equation(),
|
|
};
|
|
}
|
|
|
|
pp::foundation::Status apply_panopainter_initial_state(OpenGlStateDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.enable == nullptr
|
|
|| dispatch.disable == nullptr
|
|
|| dispatch.blend_func == nullptr
|
|
|| dispatch.blend_equation_separate == nullptr)
|
|
{
|
|
return pp::foundation::Status::invalid_argument("OpenGL state dispatch callbacks must not be null");
|
|
}
|
|
|
|
const auto state = panopainter_initial_state();
|
|
if (state.depth_test_enabled)
|
|
dispatch.enable(state.depth_test_state);
|
|
else
|
|
dispatch.disable(state.depth_test_state);
|
|
|
|
dispatch.blend_func(state.source_color_factor, state.destination_color_factor);
|
|
dispatch.blend_equation_separate(state.color_equation, state.alpha_equation);
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
OpenGlConvertCommandState panopainter_convert_command_state() noexcept
|
|
{
|
|
return OpenGlConvertCommandState {
|
|
.depth_test_enabled = false,
|
|
.program_point_size_enabled = true,
|
|
.depth_test_state = depth_test_state(),
|
|
.program_point_size_state = program_point_size_state(),
|
|
.source_color_factor = source_alpha_blend_factor(),
|
|
.destination_color_factor = one_minus_source_alpha_blend_factor(),
|
|
.blend_equation = add_blend_equation(),
|
|
};
|
|
}
|
|
|
|
pp::foundation::Status apply_panopainter_convert_command_state(
|
|
OpenGlConvertCommandStateDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.enable == nullptr
|
|
|| dispatch.disable == nullptr
|
|
|| dispatch.blend_func == nullptr
|
|
|| dispatch.blend_equation == nullptr)
|
|
{
|
|
return pp::foundation::Status::invalid_argument(
|
|
"OpenGL convert command state dispatch callbacks must not be null");
|
|
}
|
|
|
|
const auto state = panopainter_convert_command_state();
|
|
if (state.depth_test_enabled)
|
|
dispatch.enable(state.depth_test_state);
|
|
else
|
|
dispatch.disable(state.depth_test_state);
|
|
|
|
if (state.program_point_size_enabled)
|
|
dispatch.enable(state.program_point_size_state);
|
|
else
|
|
dispatch.disable(state.program_point_size_state);
|
|
|
|
dispatch.blend_func(state.source_color_factor, state.destination_color_factor);
|
|
dispatch.blend_equation(state.blend_equation);
|
|
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
|
|
{
|
|
if (dispatch.get_string == nullptr) {
|
|
return pp::foundation::Result<OpenGlRuntimeInfo>::failure(
|
|
pp::foundation::Status::invalid_argument("OpenGL runtime info string callback must not be null"));
|
|
}
|
|
|
|
return pp::foundation::Result<OpenGlRuntimeInfo>::success(OpenGlRuntimeInfo {
|
|
.version = dispatch.get_string(version_string_name()),
|
|
.vendor = dispatch.get_string(vendor_string_name()),
|
|
.renderer = dispatch.get_string(renderer_string_name()),
|
|
.shading_language_version = dispatch.get_string(shading_language_version_string_name()),
|
|
});
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
pp::foundation::Result<OpenGlCapabilityDetectionResult> query_opengl_capability_detection(
|
|
OpenGlExtensionQueryDispatch dispatch,
|
|
OpenGlRuntime runtime)
|
|
{
|
|
auto extensions_result = query_opengl_extensions(dispatch);
|
|
if (!extensions_result.ok()) {
|
|
return pp::foundation::Result<OpenGlCapabilityDetectionResult>::failure(extensions_result.status());
|
|
}
|
|
|
|
auto extensions = std::move(extensions_result.value());
|
|
std::vector<std::string_view> extension_views;
|
|
extension_views.reserve(extensions.size());
|
|
for (const auto& extension : extensions) {
|
|
extension_views.push_back(extension);
|
|
}
|
|
|
|
return pp::foundation::Result<OpenGlCapabilityDetectionResult>::success(OpenGlCapabilityDetectionResult {
|
|
.extensions = std::move(extensions),
|
|
.feature_state = detect_opengl_feature_state(extension_views, runtime),
|
|
});
|
|
}
|
|
|
|
OpenGlDefaultClear panopainter_default_clear() noexcept
|
|
{
|
|
return OpenGlDefaultClear {
|
|
.color = { 0.1F, 0.1F, 0.1F, 1.0F },
|
|
.mask = framebuffer_color_buffer_mask(),
|
|
};
|
|
}
|
|
|
|
pp::foundation::Status clear_panopainter_default_target(OpenGlClearDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.clear_color == nullptr || dispatch.clear == nullptr) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL clear dispatch callbacks must not be null");
|
|
}
|
|
|
|
const auto clear = panopainter_default_clear();
|
|
dispatch.clear_color(clear.color[0], clear.color[1], clear.color[2], clear.color[3]);
|
|
dispatch.clear(clear.mask);
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
pp::foundation::Status apply_opengl_viewport(
|
|
OpenGlViewportRect viewport,
|
|
OpenGlViewportDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.viewport == nullptr) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL viewport dispatch callback must not be null");
|
|
}
|
|
|
|
dispatch.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
pp::foundation::Status apply_opengl_scissor_rect(
|
|
OpenGlScissorRect scissor,
|
|
OpenGlScissorDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.enable == nullptr || dispatch.disable == nullptr || dispatch.scissor == nullptr) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL scissor dispatch callbacks must not be null");
|
|
}
|
|
|
|
if (scissor.enabled != 0U) {
|
|
dispatch.enable(scissor_test_state());
|
|
dispatch.scissor(scissor.x, scissor.y, scissor.width, scissor.height);
|
|
} else {
|
|
dispatch.disable(scissor_test_state());
|
|
}
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
pp::foundation::Status apply_opengl_scissor_test(
|
|
bool enabled,
|
|
OpenGlScissorTestDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.enable == nullptr || dispatch.disable == nullptr) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL scissor test dispatch callbacks must not be null");
|
|
}
|
|
|
|
if (enabled) {
|
|
dispatch.enable(scissor_test_state());
|
|
} else {
|
|
dispatch.disable(scissor_test_state());
|
|
}
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
pp::foundation::Status apply_opengl_capability(
|
|
std::uint32_t state,
|
|
bool enabled,
|
|
OpenGlCapabilityDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.enable == nullptr || dispatch.disable == nullptr) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL capability dispatch callbacks must not be null");
|
|
}
|
|
|
|
if (enabled) {
|
|
dispatch.enable(state);
|
|
} else {
|
|
dispatch.disable(state);
|
|
}
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
pp::foundation::Result<bool> query_opengl_capability_state(
|
|
std::uint32_t state,
|
|
OpenGlCapabilityStateQueryDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.is_enabled == nullptr) {
|
|
return pp::foundation::Result<bool>::failure(
|
|
pp::foundation::Status::invalid_argument("OpenGL capability state query callback must not be null"));
|
|
}
|
|
|
|
return pp::foundation::Result<bool>::success(dispatch.is_enabled(state) != 0U);
|
|
}
|
|
|
|
pp::foundation::Status apply_opengl_render_platform_hints(
|
|
OpenGlRenderPlatformHintDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.enable == nullptr) {
|
|
return pp::foundation::Status::invalid_argument(
|
|
"OpenGL render platform hint dispatch callback must not be null");
|
|
}
|
|
|
|
dispatch.enable(program_point_size_state());
|
|
dispatch.enable(line_smooth_state());
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
pp::foundation::Status apply_opengl_debug_output_states(
|
|
OpenGlDebugOutputStateDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.enable == nullptr) {
|
|
return pp::foundation::Status::invalid_argument(
|
|
"OpenGL debug output state dispatch callback must not be null");
|
|
}
|
|
|
|
dispatch.enable(debug_output_state());
|
|
dispatch.enable(debug_output_synchronous_state());
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
pp::foundation::Status clear_opengl_buffers(
|
|
std::uint32_t mask,
|
|
OpenGlBufferClearDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.clear == nullptr) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL buffer clear dispatch callback must not be null");
|
|
}
|
|
|
|
dispatch.clear(mask);
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
pp::foundation::Result<std::uint32_t> allocate_opengl_texture_2d(
|
|
OpenGlTexture2DAllocation allocation,
|
|
OpenGlTexture2DAllocationDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.gen_textures == nullptr
|
|
|| dispatch.bind_texture == nullptr
|
|
|| dispatch.tex_image_2d == nullptr) {
|
|
return pp::foundation::Result<std::uint32_t>::failure(
|
|
pp::foundation::Status::invalid_argument("OpenGL texture allocation dispatch callbacks must not be null"));
|
|
}
|
|
|
|
if (allocation.width <= 0
|
|
|| allocation.height <= 0
|
|
|| allocation.internal_format == 0
|
|
|| allocation.pixel_format == 0U
|
|
|| allocation.component_type == 0U) {
|
|
return pp::foundation::Result<std::uint32_t>::failure(
|
|
pp::foundation::Status::invalid_argument("OpenGL texture allocation parameters are invalid"));
|
|
}
|
|
|
|
std::uint32_t texture_id = 0U;
|
|
dispatch.gen_textures(1U, &texture_id);
|
|
if (texture_id == 0U) {
|
|
return pp::foundation::Result<std::uint32_t>::failure(
|
|
pp::foundation::Status::out_of_range("OpenGL texture allocation returned id 0"));
|
|
}
|
|
|
|
dispatch.bind_texture(texture_2d_target(), texture_id);
|
|
dispatch.tex_image_2d(
|
|
texture_2d_target(),
|
|
0,
|
|
allocation.internal_format,
|
|
allocation.width,
|
|
allocation.height,
|
|
0,
|
|
allocation.pixel_format,
|
|
allocation.component_type,
|
|
allocation.data);
|
|
dispatch.bind_texture(texture_2d_target(), default_framebuffer_id());
|
|
return pp::foundation::Result<std::uint32_t>::success(texture_id);
|
|
}
|
|
|
|
pp::foundation::Status delete_opengl_texture_2d(
|
|
std::uint32_t texture_id,
|
|
OpenGlTexture2DDeleteDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.delete_textures == nullptr) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL texture delete dispatch callback must not be null");
|
|
}
|
|
|
|
if (texture_id == 0U) {
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
dispatch.delete_textures(1U, &texture_id);
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
pp::foundation::Status delete_opengl_texture_objects(
|
|
std::span<const std::uint32_t> texture_ids,
|
|
OpenGlTexture2DDeleteDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.delete_textures == nullptr) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL texture delete dispatch callback must not be null");
|
|
}
|
|
|
|
if (texture_ids.empty()) {
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
dispatch.delete_textures(static_cast<std::uint32_t>(texture_ids.size()), texture_ids.data());
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
pp::foundation::Result<std::uint32_t> allocate_opengl_texture_cube(
|
|
OpenGlTextureCubeAllocation allocation,
|
|
OpenGlTextureCubeAllocationDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.gen_textures == nullptr
|
|
|| dispatch.bind_texture == nullptr
|
|
|| dispatch.tex_image_2d == nullptr) {
|
|
return pp::foundation::Result<std::uint32_t>::failure(
|
|
pp::foundation::Status::invalid_argument(
|
|
"OpenGL cube texture allocation dispatch callbacks must not be null"));
|
|
}
|
|
|
|
if (allocation.resolution <= 0
|
|
|| allocation.internal_format == 0
|
|
|| allocation.pixel_format == 0U
|
|
|| allocation.component_type == 0U) {
|
|
return pp::foundation::Result<std::uint32_t>::failure(
|
|
pp::foundation::Status::invalid_argument("OpenGL cube texture allocation parameters are invalid"));
|
|
}
|
|
|
|
std::uint32_t texture_id = 0U;
|
|
dispatch.gen_textures(1U, &texture_id);
|
|
if (texture_id == 0U) {
|
|
return pp::foundation::Result<std::uint32_t>::failure(
|
|
pp::foundation::Status::out_of_range("OpenGL cube texture allocation returned id 0"));
|
|
}
|
|
|
|
dispatch.bind_texture(texture_cube_map_target(), texture_id);
|
|
for (std::uint32_t face_index = 0U; face_index < 6U; ++face_index) {
|
|
dispatch.tex_image_2d(
|
|
cube_map_allocation_face_texture_target(face_index),
|
|
0,
|
|
allocation.internal_format,
|
|
allocation.resolution,
|
|
allocation.resolution,
|
|
0,
|
|
allocation.pixel_format,
|
|
allocation.component_type,
|
|
nullptr);
|
|
}
|
|
return pp::foundation::Result<std::uint32_t>::success(texture_id);
|
|
}
|
|
|
|
pp::foundation::Status bind_opengl_texture_2d(
|
|
std::uint32_t texture_id,
|
|
OpenGlTexture2DBindDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.bind_texture == nullptr) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL texture bind dispatch callback must not be null");
|
|
}
|
|
|
|
dispatch.bind_texture(texture_2d_target(), texture_id);
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
pp::foundation::Status bind_opengl_texture_cube(
|
|
std::uint32_t texture_id,
|
|
OpenGlTexture2DBindDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.bind_texture == nullptr) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL cube texture bind dispatch callback must not be null");
|
|
}
|
|
|
|
dispatch.bind_texture(texture_cube_map_target(), texture_id);
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
pp::foundation::Status update_opengl_texture_2d(
|
|
OpenGlTexture2DUpdate update,
|
|
OpenGlTexture2DUpdateDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.bind_texture == nullptr || dispatch.tex_sub_image_2d == nullptr) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL texture update dispatch callbacks must not be null");
|
|
}
|
|
|
|
if (update.texture_id == 0U
|
|
|| update.width <= 0
|
|
|| update.height <= 0
|
|
|| update.pixel_format == 0U
|
|
|| update.component_type == 0U
|
|
|| update.data == nullptr) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL texture update parameters are invalid");
|
|
}
|
|
|
|
dispatch.bind_texture(texture_2d_target(), update.texture_id);
|
|
dispatch.tex_sub_image_2d(
|
|
texture_2d_target(),
|
|
0,
|
|
0,
|
|
0,
|
|
update.width,
|
|
update.height,
|
|
update.pixel_format,
|
|
update.component_type,
|
|
update.data);
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
pp::foundation::Status copy_opengl_framebuffer_to_texture_2d(
|
|
OpenGlTexture2DFramebufferCopy copy,
|
|
OpenGlTexture2DFramebufferCopyDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.copy_tex_sub_image_2d == nullptr) {
|
|
return pp::foundation::Status::invalid_argument(
|
|
"OpenGL framebuffer-to-texture copy dispatch callback must not be null");
|
|
}
|
|
|
|
if (copy.width < 0 || copy.height < 0) {
|
|
return pp::foundation::Status::invalid_argument(
|
|
"OpenGL framebuffer-to-texture copy dimensions are invalid");
|
|
}
|
|
|
|
if (copy.width == 0 || copy.height == 0) {
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
dispatch.copy_tex_sub_image_2d(
|
|
texture_2d_target(),
|
|
copy.level,
|
|
copy.destination_x,
|
|
copy.destination_y,
|
|
copy.source_x,
|
|
copy.source_y,
|
|
copy.width,
|
|
copy.height);
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
pp::foundation::Status generate_opengl_texture_2d_mipmaps(
|
|
std::uint32_t texture_id,
|
|
OpenGlTexture2DMipmapDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.bind_texture == nullptr || dispatch.generate_mipmap == nullptr) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL texture mipmap dispatch callbacks must not be null");
|
|
}
|
|
|
|
if (texture_id == 0U) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL texture mipmap target is invalid");
|
|
}
|
|
|
|
dispatch.bind_texture(texture_2d_target(), texture_id);
|
|
dispatch.generate_mipmap(texture_2d_target());
|
|
dispatch.bind_texture(texture_2d_target(), default_framebuffer_id());
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
pp::foundation::Result<OpenGlTexture2DReadbackResult> readback_opengl_texture_2d(
|
|
OpenGlTexture2DReadback readback,
|
|
OpenGlTexture2DReadbackDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.bind_texture == nullptr
|
|
|| dispatch.gen_framebuffers == nullptr
|
|
|| dispatch.get_integer == nullptr
|
|
|| dispatch.bind_framebuffer == nullptr
|
|
|| dispatch.framebuffer_texture_2d == nullptr
|
|
|| dispatch.check_framebuffer_status == nullptr
|
|
|| dispatch.read_pixels == nullptr
|
|
|| dispatch.delete_framebuffers == nullptr) {
|
|
return pp::foundation::Result<OpenGlTexture2DReadbackResult>::failure(
|
|
pp::foundation::Status::invalid_argument("OpenGL texture readback dispatch callbacks must not be null"));
|
|
}
|
|
|
|
if (readback.texture_id == 0U
|
|
|| readback.width <= 0
|
|
|| readback.height <= 0
|
|
|| readback.format.pixel_format == 0U
|
|
|| readback.format.component_type == 0U
|
|
|| readback.format.bytes_per_pixel == 0U
|
|
|| readback.pixels == nullptr) {
|
|
return pp::foundation::Result<OpenGlTexture2DReadbackResult>::failure(
|
|
pp::foundation::Status::invalid_argument("OpenGL texture readback parameters are invalid"));
|
|
}
|
|
|
|
std::uint32_t framebuffer_id = 0U;
|
|
dispatch.gen_framebuffers(1U, &framebuffer_id);
|
|
if (framebuffer_id == 0U) {
|
|
return pp::foundation::Result<OpenGlTexture2DReadbackResult>::failure(
|
|
pp::foundation::Status::out_of_range("OpenGL framebuffer allocation returned id 0"));
|
|
}
|
|
|
|
std::int32_t previous_framebuffer = 0;
|
|
dispatch.get_integer(draw_framebuffer_binding_query(), &previous_framebuffer);
|
|
dispatch.bind_texture(texture_2d_target(), readback.texture_id);
|
|
dispatch.bind_framebuffer(framebuffer_target(), framebuffer_id);
|
|
dispatch.framebuffer_texture_2d(
|
|
framebuffer_target(),
|
|
framebuffer_color_attachment(),
|
|
texture_2d_target(),
|
|
readback.texture_id,
|
|
0);
|
|
|
|
const auto framebuffer_status = dispatch.check_framebuffer_status(framebuffer_target());
|
|
OpenGlTexture2DReadbackResult result {
|
|
.framebuffer_status = framebuffer_status,
|
|
.pixels_read = false,
|
|
};
|
|
if (framebuffer_status == framebuffer_complete_status()) {
|
|
dispatch.read_pixels(
|
|
0,
|
|
0,
|
|
readback.width,
|
|
readback.height,
|
|
readback.format.pixel_format,
|
|
readback.format.component_type,
|
|
readback.pixels);
|
|
result.pixels_read = true;
|
|
}
|
|
|
|
dispatch.bind_framebuffer(framebuffer_target(), static_cast<std::uint32_t>(previous_framebuffer));
|
|
dispatch.delete_framebuffers(1U, &framebuffer_id);
|
|
return pp::foundation::Result<OpenGlTexture2DReadbackResult>::success(result);
|
|
}
|
|
|
|
pp::foundation::Status blit_opengl_framebuffer(
|
|
OpenGlFramebufferBlit blit,
|
|
OpenGlFramebufferBlitDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.get_integer == nullptr
|
|
|| dispatch.bind_framebuffer == nullptr
|
|
|| dispatch.blit_framebuffer == nullptr) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL framebuffer blit dispatch callbacks must not be null");
|
|
}
|
|
|
|
if (blit.source_rect.x1 <= blit.source_rect.x0
|
|
|| blit.source_rect.y1 <= blit.source_rect.y0
|
|
|| blit.destination_rect.x1 <= blit.destination_rect.x0
|
|
|| blit.destination_rect.y1 <= blit.destination_rect.y0
|
|
|| blit.mask == 0U
|
|
|| blit.filter == 0U) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL framebuffer blit parameters are invalid");
|
|
}
|
|
|
|
std::int32_t previous_draw_framebuffer = 0;
|
|
std::int32_t previous_read_framebuffer = 0;
|
|
dispatch.get_integer(draw_framebuffer_binding_query(), &previous_draw_framebuffer);
|
|
dispatch.get_integer(read_framebuffer_binding_query(), &previous_read_framebuffer);
|
|
dispatch.bind_framebuffer(draw_framebuffer_target(), blit.destination_framebuffer);
|
|
dispatch.bind_framebuffer(read_framebuffer_target(), blit.source_framebuffer);
|
|
dispatch.blit_framebuffer(
|
|
blit.source_rect.x0,
|
|
blit.source_rect.y0,
|
|
blit.source_rect.x1,
|
|
blit.source_rect.y1,
|
|
blit.destination_rect.x0,
|
|
blit.destination_rect.y0,
|
|
blit.destination_rect.x1,
|
|
blit.destination_rect.y1,
|
|
blit.mask,
|
|
blit.filter);
|
|
dispatch.bind_framebuffer(draw_framebuffer_target(), static_cast<std::uint32_t>(previous_draw_framebuffer));
|
|
dispatch.bind_framebuffer(read_framebuffer_target(), static_cast<std::uint32_t>(previous_read_framebuffer));
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
pp::foundation::Status readback_opengl_framebuffer(
|
|
OpenGlFramebufferReadback readback,
|
|
OpenGlFramebufferReadbackDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.get_integer == nullptr
|
|
|| dispatch.bind_framebuffer == nullptr
|
|
|| dispatch.read_pixels == nullptr) {
|
|
return pp::foundation::Status::invalid_argument(
|
|
"OpenGL framebuffer readback dispatch callbacks must not be null");
|
|
}
|
|
|
|
if (readback.width <= 0
|
|
|| readback.height <= 0
|
|
|| readback.format.pixel_format == 0U
|
|
|| readback.format.component_type == 0U
|
|
|| readback.format.bytes_per_pixel == 0U
|
|
|| readback.pixels == nullptr) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL framebuffer readback parameters are invalid");
|
|
}
|
|
|
|
std::int32_t previous_read_framebuffer = 0;
|
|
dispatch.get_integer(read_framebuffer_binding_query(), &previous_read_framebuffer);
|
|
dispatch.bind_framebuffer(read_framebuffer_target(), readback.framebuffer);
|
|
dispatch.read_pixels(
|
|
readback.x,
|
|
readback.y,
|
|
readback.width,
|
|
readback.height,
|
|
readback.format.pixel_format,
|
|
readback.format.component_type,
|
|
readback.pixels);
|
|
dispatch.bind_framebuffer(read_framebuffer_target(), static_cast<std::uint32_t>(previous_read_framebuffer));
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
pp::foundation::Result<OpenGlFramebufferBindingState> bind_opengl_framebuffer_for_draw_read(
|
|
std::uint32_t framebuffer,
|
|
OpenGlFramebufferBindDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.get_integer == nullptr || dispatch.bind_framebuffer == nullptr) {
|
|
return pp::foundation::Result<OpenGlFramebufferBindingState>::failure(
|
|
pp::foundation::Status::invalid_argument("OpenGL framebuffer bind dispatch callbacks must not be null"));
|
|
}
|
|
|
|
OpenGlFramebufferBindingState binding {};
|
|
dispatch.get_integer(draw_framebuffer_binding_query(), &binding.draw_framebuffer);
|
|
dispatch.get_integer(read_framebuffer_binding_query(), &binding.read_framebuffer);
|
|
dispatch.bind_framebuffer(draw_framebuffer_target(), framebuffer);
|
|
dispatch.bind_framebuffer(read_framebuffer_target(), framebuffer);
|
|
return pp::foundation::Result<OpenGlFramebufferBindingState>::success(binding);
|
|
}
|
|
|
|
pp::foundation::Status restore_opengl_framebuffer_binding(
|
|
OpenGlFramebufferBindingState binding,
|
|
OpenGlFramebufferRestoreDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.bind_framebuffer == nullptr) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL framebuffer restore dispatch callback must not be null");
|
|
}
|
|
|
|
dispatch.bind_framebuffer(draw_framebuffer_target(), static_cast<std::uint32_t>(binding.draw_framebuffer));
|
|
dispatch.bind_framebuffer(read_framebuffer_target(), static_cast<std::uint32_t>(binding.read_framebuffer));
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
pp::foundation::Result<std::uint32_t> allocate_opengl_depth_renderbuffer(
|
|
std::int32_t width,
|
|
std::int32_t height,
|
|
OpenGlDepthRenderbufferAllocationDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.gen_renderbuffers == nullptr
|
|
|| dispatch.bind_renderbuffer == nullptr
|
|
|| dispatch.renderbuffer_storage == nullptr) {
|
|
return pp::foundation::Result<std::uint32_t>::failure(
|
|
pp::foundation::Status::invalid_argument(
|
|
"OpenGL depth renderbuffer allocation dispatch callbacks must not be null"));
|
|
}
|
|
|
|
if (width <= 0 || height <= 0) {
|
|
return pp::foundation::Result<std::uint32_t>::failure(
|
|
pp::foundation::Status::invalid_argument("OpenGL depth renderbuffer dimensions are invalid"));
|
|
}
|
|
|
|
std::uint32_t renderbuffer_id = 0U;
|
|
dispatch.gen_renderbuffers(1U, &renderbuffer_id);
|
|
if (renderbuffer_id == 0U) {
|
|
return pp::foundation::Result<std::uint32_t>::failure(
|
|
pp::foundation::Status::out_of_range("OpenGL renderbuffer allocation returned id 0"));
|
|
}
|
|
|
|
dispatch.bind_renderbuffer(renderbuffer_target(), renderbuffer_id);
|
|
dispatch.renderbuffer_storage(renderbuffer_target(), depth_component24_format(), width, height);
|
|
dispatch.bind_renderbuffer(renderbuffer_target(), default_framebuffer_id());
|
|
return pp::foundation::Result<std::uint32_t>::success(renderbuffer_id);
|
|
}
|
|
|
|
pp::foundation::Status delete_opengl_renderbuffer(
|
|
std::uint32_t renderbuffer_id,
|
|
OpenGlRenderbufferDeleteDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.delete_renderbuffers == nullptr) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL renderbuffer delete callback must not be null");
|
|
}
|
|
|
|
if (renderbuffer_id == 0U) {
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
dispatch.delete_renderbuffers(1U, &renderbuffer_id);
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
pp::foundation::Status attach_opengl_depth_renderbuffer(
|
|
std::uint32_t renderbuffer_id,
|
|
OpenGlDepthRenderbufferAttachmentDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.framebuffer_renderbuffer == nullptr) {
|
|
return pp::foundation::Status::invalid_argument(
|
|
"OpenGL depth renderbuffer attachment callback must not be null");
|
|
}
|
|
|
|
dispatch.framebuffer_renderbuffer(
|
|
framebuffer_target(),
|
|
framebuffer_depth_attachment(),
|
|
renderbuffer_target(),
|
|
renderbuffer_id);
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
pp::foundation::Result<std::uint32_t> create_opengl_sampler(
|
|
std::span<const OpenGlTextureParameter> parameters,
|
|
OpenGlSamplerCreateDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.gen_samplers == nullptr || dispatch.sampler_parameter_i == nullptr) {
|
|
return pp::foundation::Result<std::uint32_t>::failure(
|
|
pp::foundation::Status::invalid_argument("OpenGL sampler create dispatch callbacks must not be null"));
|
|
}
|
|
|
|
if (parameters.empty()) {
|
|
return pp::foundation::Result<std::uint32_t>::failure(
|
|
pp::foundation::Status::invalid_argument("OpenGL sampler parameters are invalid"));
|
|
}
|
|
|
|
for (const auto parameter : parameters) {
|
|
if (parameter.name == 0U) {
|
|
return pp::foundation::Result<std::uint32_t>::failure(
|
|
pp::foundation::Status::invalid_argument("OpenGL sampler parameter name is invalid"));
|
|
}
|
|
}
|
|
|
|
std::uint32_t sampler_id = 0U;
|
|
dispatch.gen_samplers(1U, &sampler_id);
|
|
if (sampler_id == 0U) {
|
|
return pp::foundation::Result<std::uint32_t>::failure(
|
|
pp::foundation::Status::out_of_range("OpenGL sampler allocation returned id 0"));
|
|
}
|
|
|
|
const auto status = set_opengl_sampler_parameters(
|
|
sampler_id,
|
|
parameters,
|
|
OpenGlSamplerParameterDispatch {
|
|
.sampler_parameter_i = dispatch.sampler_parameter_i,
|
|
});
|
|
if (!status.ok()) {
|
|
return pp::foundation::Result<std::uint32_t>::failure(status);
|
|
}
|
|
|
|
return pp::foundation::Result<std::uint32_t>::success(sampler_id);
|
|
}
|
|
|
|
pp::foundation::Status set_opengl_sampler_parameters(
|
|
std::uint32_t sampler_id,
|
|
std::span<const OpenGlTextureParameter> parameters,
|
|
OpenGlSamplerParameterDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.sampler_parameter_i == nullptr) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL sampler parameter dispatch callback must not be null");
|
|
}
|
|
|
|
if (sampler_id == 0U || parameters.empty()) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL sampler parameters are invalid");
|
|
}
|
|
|
|
for (const auto parameter : parameters) {
|
|
if (parameter.name == 0U) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL sampler parameter name is invalid");
|
|
}
|
|
dispatch.sampler_parameter_i(
|
|
sampler_id,
|
|
parameter.name,
|
|
static_cast<std::int32_t>(parameter.value));
|
|
}
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
pp::foundation::Status set_opengl_sampler_border_color(
|
|
std::uint32_t sampler_id,
|
|
std::uint32_t parameter,
|
|
const float* rgba,
|
|
OpenGlSamplerBorderDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.sampler_parameter_fv == nullptr) {
|
|
return pp::foundation::Status::invalid_argument(
|
|
"OpenGL sampler border color dispatch callback must not be null");
|
|
}
|
|
|
|
if (sampler_id == 0U || parameter == 0U || rgba == nullptr) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL sampler border color parameters are invalid");
|
|
}
|
|
|
|
dispatch.sampler_parameter_fv(sampler_id, parameter, rgba);
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
pp::foundation::Status bind_opengl_sampler_object(
|
|
std::uint32_t unit,
|
|
std::uint32_t sampler_id,
|
|
OpenGlSamplerBindDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.bind_sampler == nullptr) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL sampler bind dispatch callback must not be null");
|
|
}
|
|
|
|
dispatch.bind_sampler(unit, sampler_id);
|
|
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));
|
|
}
|
|
|
|
pp::foundation::Result<OpenGlShaderCompileInfo> compile_opengl_shader_source(
|
|
std::uint32_t stage,
|
|
const char* source,
|
|
char* info_log,
|
|
std::int32_t info_log_capacity,
|
|
OpenGlShaderCompileDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.create_shader == nullptr
|
|
|| dispatch.shader_source == nullptr
|
|
|| dispatch.compile_shader == nullptr
|
|
|| dispatch.get_shader_integer == nullptr
|
|
|| dispatch.get_shader_info_log == nullptr) {
|
|
return pp::foundation::Result<OpenGlShaderCompileInfo>::failure(
|
|
pp::foundation::Status::invalid_argument("OpenGL shader compile dispatch callbacks must not be null"));
|
|
}
|
|
|
|
if (stage == 0U || source == nullptr || source[0] == '\0' || info_log == nullptr || info_log_capacity <= 0) {
|
|
return pp::foundation::Result<OpenGlShaderCompileInfo>::failure(
|
|
pp::foundation::Status::invalid_argument("OpenGL shader compile parameters are invalid"));
|
|
}
|
|
|
|
const auto shader_id = dispatch.create_shader(stage);
|
|
if (shader_id == 0U) {
|
|
return pp::foundation::Result<OpenGlShaderCompileInfo>::failure(
|
|
pp::foundation::Status::out_of_range("OpenGL shader allocation returned id 0"));
|
|
}
|
|
|
|
const char* const sources[] { source };
|
|
dispatch.shader_source(shader_id, 1, sources);
|
|
dispatch.compile_shader(shader_id);
|
|
|
|
OpenGlShaderCompileInfo info {
|
|
.shader_id = shader_id,
|
|
};
|
|
dispatch.get_shader_integer(shader_id, shader_compile_status_query(), &info.compile_status);
|
|
dispatch.get_shader_info_log(shader_id, info_log_capacity, &info.info_log_length, info_log);
|
|
return pp::foundation::Result<OpenGlShaderCompileInfo>::success(info);
|
|
}
|
|
|
|
pp::foundation::Status delete_opengl_shader(
|
|
std::uint32_t shader_id,
|
|
OpenGlShaderDeleteDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.delete_shader == nullptr) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL shader delete dispatch callback must not be null");
|
|
}
|
|
|
|
if (shader_id == 0U) {
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
dispatch.delete_shader(shader_id);
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
pp::foundation::Result<OpenGlProgramLinkInfo> link_opengl_shader_program(
|
|
std::uint32_t vertex_shader_id,
|
|
std::uint32_t fragment_shader_id,
|
|
std::span<const OpenGlAttributeBinding> attribute_bindings,
|
|
char* info_log,
|
|
std::int32_t info_log_capacity,
|
|
OpenGlProgramLinkDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.create_program == nullptr
|
|
|| dispatch.attach_shader == nullptr
|
|
|| dispatch.delete_shader == nullptr
|
|
|| dispatch.link_program == nullptr
|
|
|| dispatch.get_attrib_location == nullptr
|
|
|| dispatch.bind_attrib_location == nullptr
|
|
|| dispatch.get_program_integer == nullptr
|
|
|| dispatch.get_program_info_log == nullptr) {
|
|
return pp::foundation::Result<OpenGlProgramLinkInfo>::failure(
|
|
pp::foundation::Status::invalid_argument("OpenGL program link dispatch callbacks must not be null"));
|
|
}
|
|
|
|
if (vertex_shader_id == 0U
|
|
|| fragment_shader_id == 0U
|
|
|| attribute_bindings.empty()
|
|
|| info_log == nullptr
|
|
|| info_log_capacity <= 0) {
|
|
return pp::foundation::Result<OpenGlProgramLinkInfo>::failure(
|
|
pp::foundation::Status::invalid_argument("OpenGL program link parameters are invalid"));
|
|
}
|
|
|
|
const auto program_id = dispatch.create_program();
|
|
if (program_id == 0U) {
|
|
return pp::foundation::Result<OpenGlProgramLinkInfo>::failure(
|
|
pp::foundation::Status::out_of_range("OpenGL program allocation returned id 0"));
|
|
}
|
|
|
|
dispatch.attach_shader(program_id, vertex_shader_id);
|
|
dispatch.attach_shader(program_id, fragment_shader_id);
|
|
dispatch.delete_shader(vertex_shader_id);
|
|
dispatch.delete_shader(fragment_shader_id);
|
|
dispatch.link_program(program_id);
|
|
|
|
for (const auto& binding : attribute_bindings) {
|
|
if (binding.name != nullptr && dispatch.get_attrib_location(program_id, binding.name) != -1) {
|
|
dispatch.bind_attrib_location(program_id, binding.location, binding.name);
|
|
}
|
|
}
|
|
|
|
OpenGlProgramLinkInfo info {
|
|
.program_id = program_id,
|
|
};
|
|
dispatch.link_program(program_id);
|
|
dispatch.get_program_integer(program_id, program_link_status_query(), &info.link_status);
|
|
dispatch.get_program_info_log(program_id, info_log_capacity, &info.info_log_length, info_log);
|
|
return pp::foundation::Result<OpenGlProgramLinkInfo>::success(info);
|
|
}
|
|
|
|
pp::foundation::Result<std::int32_t> query_opengl_program_integer(
|
|
std::uint32_t program_id,
|
|
std::uint32_t query,
|
|
OpenGlProgramIntegerDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.get_program_integer == nullptr) {
|
|
return pp::foundation::Result<std::int32_t>::failure(
|
|
pp::foundation::Status::invalid_argument("OpenGL program integer dispatch callback must not be null"));
|
|
}
|
|
|
|
if (program_id == 0U || query == 0U) {
|
|
return pp::foundation::Result<std::int32_t>::failure(
|
|
pp::foundation::Status::invalid_argument("OpenGL program integer parameters are invalid"));
|
|
}
|
|
|
|
std::int32_t value = 0;
|
|
dispatch.get_program_integer(program_id, query, &value);
|
|
return pp::foundation::Result<std::int32_t>::success(value);
|
|
}
|
|
|
|
pp::foundation::Result<OpenGlActiveUniformInfo> get_opengl_active_uniform(
|
|
std::uint32_t program_id,
|
|
std::uint32_t index,
|
|
char* name,
|
|
std::int32_t name_capacity,
|
|
OpenGlActiveUniformDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.get_active_uniform == nullptr) {
|
|
return pp::foundation::Result<OpenGlActiveUniformInfo>::failure(
|
|
pp::foundation::Status::invalid_argument("OpenGL active uniform dispatch callback must not be null"));
|
|
}
|
|
|
|
if (program_id == 0U || name == nullptr || name_capacity <= 0) {
|
|
return pp::foundation::Result<OpenGlActiveUniformInfo>::failure(
|
|
pp::foundation::Status::invalid_argument("OpenGL active uniform parameters are invalid"));
|
|
}
|
|
|
|
OpenGlActiveUniformInfo info {};
|
|
dispatch.get_active_uniform(
|
|
program_id,
|
|
index,
|
|
name_capacity,
|
|
&info.length,
|
|
&info.size,
|
|
&info.type,
|
|
name);
|
|
if (info.length >= 0 && info.length < name_capacity) {
|
|
name[info.length] = '\0';
|
|
} else {
|
|
name[name_capacity - 1] = '\0';
|
|
}
|
|
return pp::foundation::Result<OpenGlActiveUniformInfo>::success(info);
|
|
}
|
|
|
|
pp::foundation::Result<std::int32_t> get_opengl_uniform_location(
|
|
std::uint32_t program_id,
|
|
const char* uniform_name,
|
|
OpenGlUniformLocationDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.get_uniform_location == nullptr) {
|
|
return pp::foundation::Result<std::int32_t>::failure(
|
|
pp::foundation::Status::invalid_argument("OpenGL uniform-location dispatch callback must not be null"));
|
|
}
|
|
|
|
if (program_id == 0U || uniform_name == nullptr || uniform_name[0] == '\0') {
|
|
return pp::foundation::Result<std::int32_t>::failure(
|
|
pp::foundation::Status::invalid_argument("OpenGL uniform-location parameters are invalid"));
|
|
}
|
|
|
|
return pp::foundation::Result<std::int32_t>::success(
|
|
dispatch.get_uniform_location(program_id, uniform_name));
|
|
}
|
|
|
|
pp::foundation::Result<OpenGlMeshObjects> create_opengl_mesh_objects(
|
|
OpenGlMeshUpload upload,
|
|
OpenGlMeshCreateDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.gen_buffers == nullptr
|
|
|| dispatch.bind_buffer == nullptr
|
|
|| dispatch.buffer_data == nullptr
|
|
|| dispatch.gen_vertex_arrays == nullptr
|
|
|| dispatch.bind_vertex_array == nullptr
|
|
|| dispatch.enable_vertex_attrib_array == nullptr
|
|
|| dispatch.vertex_attrib_pointer == nullptr) {
|
|
return pp::foundation::Result<OpenGlMeshObjects>::failure(
|
|
pp::foundation::Status::invalid_argument("OpenGL mesh create dispatch callbacks must not be null"));
|
|
}
|
|
|
|
if (upload.vertex_byte_count < 0
|
|
|| (upload.vertex_byte_count > 0 && upload.vertex_data == nullptr)
|
|
|| upload.index_byte_count < 0
|
|
|| (upload.index_byte_count > 0 && upload.index_data == nullptr)
|
|
|| (!upload.indexed && upload.index_byte_count != 0)
|
|
|| upload.vertex_array_count == 0U
|
|
|| upload.vertex_array_count > 2U
|
|
|| upload.attributes.empty()) {
|
|
return pp::foundation::Result<OpenGlMeshObjects>::failure(
|
|
pp::foundation::Status::invalid_argument("OpenGL mesh upload parameters are invalid"));
|
|
}
|
|
|
|
for (const auto& attribute : upload.attributes) {
|
|
if (attribute.component_count <= 0 || attribute.component_type == 0U || attribute.stride <= 0) {
|
|
return pp::foundation::Result<OpenGlMeshObjects>::failure(
|
|
pp::foundation::Status::invalid_argument("OpenGL mesh vertex attribute parameters are invalid"));
|
|
}
|
|
}
|
|
|
|
std::array<std::uint32_t, 2> buffers {};
|
|
const auto buffer_count = upload.indexed ? 2U : 1U;
|
|
dispatch.gen_buffers(buffer_count, buffers.data());
|
|
if (buffers[0] == 0U || (upload.indexed && buffers[1] == 0U)) {
|
|
return pp::foundation::Result<OpenGlMeshObjects>::failure(
|
|
pp::foundation::Status::out_of_range("OpenGL mesh buffer allocation returned id 0"));
|
|
}
|
|
|
|
if (upload.indexed && upload.index_byte_count > 0) {
|
|
dispatch.bind_buffer(element_array_buffer_target(), buffers[1]);
|
|
dispatch.buffer_data(element_array_buffer_target(), upload.index_byte_count, upload.index_data, static_draw_buffer_usage());
|
|
}
|
|
if (upload.vertex_byte_count > 0) {
|
|
dispatch.bind_buffer(array_buffer_target(), buffers[0]);
|
|
dispatch.buffer_data(array_buffer_target(), upload.vertex_byte_count, upload.vertex_data, static_draw_buffer_usage());
|
|
}
|
|
dispatch.bind_buffer(element_array_buffer_target(), 0U);
|
|
dispatch.bind_buffer(array_buffer_target(), 0U);
|
|
|
|
std::array<std::uint32_t, 2> vertex_arrays {};
|
|
dispatch.gen_vertex_arrays(upload.vertex_array_count, vertex_arrays.data());
|
|
if (vertex_arrays[0] == 0U || (upload.vertex_array_count > 1U && vertex_arrays[1] == 0U)) {
|
|
return pp::foundation::Result<OpenGlMeshObjects>::failure(
|
|
pp::foundation::Status::out_of_range("OpenGL mesh vertex array allocation returned id 0"));
|
|
}
|
|
|
|
for (std::uint32_t vertex_array_index = 0U; vertex_array_index < upload.vertex_array_count; ++vertex_array_index) {
|
|
const auto vertex_array = vertex_arrays[vertex_array_index];
|
|
dispatch.bind_vertex_array(vertex_array);
|
|
for (const auto& attribute : upload.attributes) {
|
|
dispatch.enable_vertex_attrib_array(attribute.index);
|
|
}
|
|
if (upload.indexed) {
|
|
dispatch.bind_buffer(element_array_buffer_target(), buffers[1]);
|
|
}
|
|
dispatch.bind_buffer(array_buffer_target(), buffers[0]);
|
|
for (const auto& attribute : upload.attributes) {
|
|
dispatch.vertex_attrib_pointer(
|
|
attribute.index,
|
|
attribute.component_count,
|
|
attribute.component_type,
|
|
attribute.normalized,
|
|
attribute.stride,
|
|
reinterpret_cast<const void*>(attribute.offset));
|
|
}
|
|
}
|
|
dispatch.bind_vertex_array(0U);
|
|
|
|
return pp::foundation::Result<OpenGlMeshObjects>::success(OpenGlMeshObjects {
|
|
.vertex_buffer = buffers[0],
|
|
.index_buffer = buffers[1],
|
|
.vertex_arrays = vertex_arrays,
|
|
});
|
|
}
|
|
|
|
pp::foundation::Status upload_opengl_buffer_data(
|
|
OpenGlBufferUpload upload,
|
|
OpenGlBufferUploadDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.bind_buffer == nullptr || dispatch.buffer_data == nullptr) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL buffer upload dispatch callbacks must not be null");
|
|
}
|
|
|
|
if (upload.target == 0U
|
|
|| upload.buffer_id == 0U
|
|
|| upload.byte_count < 0
|
|
|| (upload.byte_count > 0 && upload.data == nullptr)
|
|
|| upload.usage == 0U) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL buffer upload parameters are invalid");
|
|
}
|
|
|
|
dispatch.bind_buffer(upload.target, upload.buffer_id);
|
|
dispatch.buffer_data(upload.target, upload.byte_count, upload.data, upload.usage);
|
|
dispatch.bind_buffer(upload.target, 0U);
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
pp::foundation::Status draw_opengl_mesh(
|
|
OpenGlMeshDraw draw,
|
|
OpenGlMeshDrawDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.bind_vertex_array == nullptr
|
|
|| dispatch.draw_elements == nullptr
|
|
|| dispatch.draw_arrays == nullptr) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL mesh draw dispatch callbacks must not be null");
|
|
}
|
|
|
|
if (draw.vertex_array == 0U || draw.count < 0 || (draw.indexed && draw.index_type == 0U)) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL mesh draw parameters are invalid");
|
|
}
|
|
|
|
dispatch.bind_vertex_array(draw.vertex_array);
|
|
if (draw.indexed) {
|
|
dispatch.draw_elements(draw.mode, draw.count, draw.index_type, draw.index_offset);
|
|
} else {
|
|
dispatch.draw_arrays(draw.mode, 0, draw.count);
|
|
}
|
|
dispatch.bind_vertex_array(0U);
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
pp::foundation::Status delete_opengl_mesh_objects(
|
|
OpenGlMeshDelete objects,
|
|
OpenGlMeshDeleteDispatch dispatch) noexcept
|
|
{
|
|
if (dispatch.delete_buffers == nullptr || dispatch.delete_vertex_arrays == nullptr) {
|
|
return pp::foundation::Status::invalid_argument("OpenGL mesh delete dispatch callbacks must not be null");
|
|
}
|
|
|
|
if (objects.buffers[0] != 0U || objects.buffers[1] != 0U) {
|
|
dispatch.delete_buffers(1U, &objects.buffers[0]);
|
|
dispatch.delete_buffers(1U, &objects.buffers[1]);
|
|
}
|
|
if (objects.vertex_arrays[0] != 0U || objects.vertex_arrays[1] != 0U) {
|
|
dispatch.delete_vertex_arrays(1U, &objects.vertex_arrays[0]);
|
|
dispatch.delete_vertex_arrays(1U, &objects.vertex_arrays[1]);
|
|
}
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
std::uint32_t extension_count_query() noexcept
|
|
{
|
|
return gl_num_extensions;
|
|
}
|
|
|
|
std::uint32_t extension_string_name() noexcept
|
|
{
|
|
return gl_extensions;
|
|
}
|
|
|
|
std::uint32_t no_error_code() noexcept
|
|
{
|
|
return gl_no_error;
|
|
}
|
|
|
|
const char* opengl_error_name(std::uint32_t error_code) noexcept
|
|
{
|
|
switch (error_code) {
|
|
case gl_no_error:
|
|
return "GL_NO_ERROR";
|
|
case gl_invalid_enum:
|
|
return "GL_INVALID_ENUM";
|
|
case gl_invalid_value:
|
|
return "GL_INVALID_VALUE";
|
|
case gl_invalid_operation:
|
|
return "GL_INVALID_OPERATION";
|
|
case gl_invalid_framebuffer_operation:
|
|
return "GL_INVALID_FRAMEBUFFER_OPERATION";
|
|
case gl_out_of_memory:
|
|
return "GL_OUT_OF_MEMORY";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
std::uint32_t texture_upload_type_for_internal_format(std::uint32_t internal_format) noexcept
|
|
{
|
|
switch (internal_format) {
|
|
case gl_rgba32f:
|
|
return gl_float;
|
|
case gl_rgba16f:
|
|
return gl_half_float;
|
|
default:
|
|
return gl_unsigned_byte;
|
|
}
|
|
}
|
|
|
|
std::uint32_t unsigned_byte_component_type() noexcept
|
|
{
|
|
return gl_unsigned_byte;
|
|
}
|
|
|
|
std::uint32_t rgba_pixel_format() noexcept
|
|
{
|
|
return gl_rgba;
|
|
}
|
|
|
|
OpenGlPixelFormat texture_format_for_channel_count(std::uint32_t channel_count) noexcept
|
|
{
|
|
switch (channel_count) {
|
|
case 1:
|
|
return OpenGlPixelFormat { .internal_format = gl_r8, .pixel_format = gl_red, .channel_count = 1 };
|
|
case 2:
|
|
return OpenGlPixelFormat { .internal_format = gl_rg8, .pixel_format = gl_rg, .channel_count = 2 };
|
|
case 3:
|
|
return OpenGlPixelFormat { .internal_format = gl_rgb8, .pixel_format = gl_rgb, .channel_count = 3 };
|
|
case 4:
|
|
return OpenGlPixelFormat { .internal_format = gl_rgba8, .pixel_format = gl_rgba, .channel_count = 4 };
|
|
default:
|
|
return OpenGlPixelFormat {};
|
|
}
|
|
}
|
|
|
|
OpenGlRendererTextureFormat texture_format_for_renderer_format(pp::renderer::TextureFormat format) noexcept
|
|
{
|
|
switch (format) {
|
|
case pp::renderer::TextureFormat::rgba8:
|
|
return OpenGlRendererTextureFormat {
|
|
.internal_format = gl_rgba8,
|
|
.pixel_format = gl_rgba,
|
|
.component_type = gl_unsigned_byte,
|
|
.bytes_per_pixel = 4U,
|
|
};
|
|
case pp::renderer::TextureFormat::r8:
|
|
return OpenGlRendererTextureFormat {
|
|
.internal_format = gl_r8,
|
|
.pixel_format = gl_red,
|
|
.component_type = gl_unsigned_byte,
|
|
.bytes_per_pixel = 1U,
|
|
};
|
|
case pp::renderer::TextureFormat::depth24_stencil8:
|
|
return OpenGlRendererTextureFormat {
|
|
.internal_format = gl_depth24_stencil8,
|
|
.pixel_format = gl_depth_stencil,
|
|
.component_type = gl_unsigned_int_24_8,
|
|
.bytes_per_pixel = 4U,
|
|
};
|
|
default:
|
|
return OpenGlRendererTextureFormat {};
|
|
}
|
|
}
|
|
|
|
OpenGlReadbackFormat rgba8_readback_format() noexcept
|
|
{
|
|
return OpenGlReadbackFormat {
|
|
.pixel_format = gl_rgba,
|
|
.component_type = gl_unsigned_byte,
|
|
.bytes_per_pixel = 4U,
|
|
};
|
|
}
|
|
|
|
OpenGlReadbackFormat rgba32f_readback_format() noexcept
|
|
{
|
|
return OpenGlReadbackFormat {
|
|
.pixel_format = gl_rgba,
|
|
.component_type = gl_float,
|
|
.bytes_per_pixel = 16U,
|
|
};
|
|
}
|
|
|
|
std::uint64_t readback_byte_count(
|
|
OpenGlReadbackFormat format,
|
|
std::uint32_t width,
|
|
std::uint32_t height) noexcept
|
|
{
|
|
if (format.bytes_per_pixel == 0U) {
|
|
return 0U;
|
|
}
|
|
|
|
const auto wide_width = static_cast<std::uint64_t>(width);
|
|
const auto wide_height = static_cast<std::uint64_t>(height);
|
|
if (wide_width != 0U && wide_height > std::numeric_limits<std::uint64_t>::max() / wide_width) {
|
|
return 0U;
|
|
}
|
|
|
|
const auto pixels = wide_width * wide_height;
|
|
if (pixels != 0U && format.bytes_per_pixel > std::numeric_limits<std::uint64_t>::max() / pixels) {
|
|
return 0U;
|
|
}
|
|
|
|
return pixels * format.bytes_per_pixel;
|
|
}
|
|
|
|
std::uint32_t pixel_pack_buffer_target() noexcept
|
|
{
|
|
return gl_pixel_pack_buffer;
|
|
}
|
|
|
|
std::uint32_t pixel_unpack_buffer_target() noexcept
|
|
{
|
|
return gl_pixel_unpack_buffer;
|
|
}
|
|
|
|
std::uint32_t pixel_buffer_stream_read_usage() noexcept
|
|
{
|
|
return gl_stream_read;
|
|
}
|
|
|
|
std::uint32_t pixel_buffer_map_read_access() noexcept
|
|
{
|
|
return gl_map_read_bit;
|
|
}
|
|
|
|
std::uint32_t texture_2d_target() noexcept
|
|
{
|
|
return gl_texture_2d;
|
|
}
|
|
|
|
std::uint32_t renderbuffer_target() noexcept
|
|
{
|
|
return gl_renderbuffer;
|
|
}
|
|
|
|
std::uint32_t depth_component24_format() noexcept
|
|
{
|
|
return gl_depth_component24;
|
|
}
|
|
|
|
std::uint32_t framebuffer_target() noexcept
|
|
{
|
|
return gl_framebuffer;
|
|
}
|
|
|
|
std::uint32_t draw_framebuffer_target() noexcept
|
|
{
|
|
return gl_draw_framebuffer;
|
|
}
|
|
|
|
std::uint32_t read_framebuffer_target() noexcept
|
|
{
|
|
return gl_read_framebuffer;
|
|
}
|
|
|
|
std::uint32_t draw_framebuffer_binding_query() noexcept
|
|
{
|
|
return gl_draw_framebuffer_binding;
|
|
}
|
|
|
|
std::uint32_t read_framebuffer_binding_query() noexcept
|
|
{
|
|
return gl_read_framebuffer_binding;
|
|
}
|
|
|
|
std::uint32_t framebuffer_color_attachment() noexcept
|
|
{
|
|
return gl_color_attachment0;
|
|
}
|
|
|
|
std::uint32_t framebuffer_depth_attachment() noexcept
|
|
{
|
|
return gl_depth_attachment;
|
|
}
|
|
|
|
std::uint32_t framebuffer_complete_status() noexcept
|
|
{
|
|
return gl_framebuffer_complete;
|
|
}
|
|
|
|
std::uint32_t default_framebuffer_id() noexcept
|
|
{
|
|
return 0U;
|
|
}
|
|
|
|
const char* framebuffer_status_name(std::uint32_t status) noexcept
|
|
{
|
|
switch (status) {
|
|
case gl_framebuffer_complete:
|
|
return "GL_FRAMEBUFFER_COMPLETE";
|
|
case gl_framebuffer_undefined:
|
|
return "GL_FRAMEBUFFER_UNDEFINED";
|
|
case gl_framebuffer_incomplete_attachment:
|
|
return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
|
|
case gl_framebuffer_incomplete_missing_attachment:
|
|
return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
|
|
case gl_framebuffer_incomplete_draw_buffer:
|
|
return "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER";
|
|
case gl_framebuffer_incomplete_read_buffer:
|
|
return "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER";
|
|
case gl_framebuffer_unsupported:
|
|
return "GL_FRAMEBUFFER_UNSUPPORTED";
|
|
case gl_framebuffer_incomplete_multisample:
|
|
return "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE";
|
|
default:
|
|
return "UNKNOWN";
|
|
}
|
|
}
|
|
|
|
std::uint32_t framebuffer_color_buffer_mask() noexcept
|
|
{
|
|
return gl_color_buffer_bit;
|
|
}
|
|
|
|
std::uint32_t framebuffer_depth_buffer_mask() noexcept
|
|
{
|
|
return gl_depth_buffer_bit;
|
|
}
|
|
|
|
std::uint32_t framebuffer_stencil_buffer_mask() noexcept
|
|
{
|
|
return gl_stencil_buffer_bit;
|
|
}
|
|
|
|
std::uint32_t clear_mask_for_render_pass(pp::renderer::RenderPassDesc desc) noexcept
|
|
{
|
|
std::uint32_t mask = 0U;
|
|
if (desc.clear_color_enabled) {
|
|
mask |= gl_color_buffer_bit;
|
|
}
|
|
|
|
if (desc.clear_depth_enabled) {
|
|
mask |= gl_depth_buffer_bit;
|
|
}
|
|
|
|
if (desc.clear_stencil_enabled) {
|
|
mask |= gl_stencil_buffer_bit;
|
|
}
|
|
|
|
return mask;
|
|
}
|
|
|
|
OpenGlClearValues clear_values_for_render_pass(pp::renderer::RenderPassDesc desc) noexcept
|
|
{
|
|
return OpenGlClearValues {
|
|
.color = {
|
|
desc.clear_color.r,
|
|
desc.clear_color.g,
|
|
desc.clear_color.b,
|
|
desc.clear_color.a,
|
|
},
|
|
.depth = desc.clear_depth,
|
|
.stencil = desc.clear_stencil,
|
|
};
|
|
}
|
|
|
|
std::uint32_t color_write_mask_query() noexcept
|
|
{
|
|
return gl_color_writemask;
|
|
}
|
|
|
|
std::uint32_t framebuffer_blit_filter(bool linear) noexcept
|
|
{
|
|
return linear ? gl_linear : gl_nearest;
|
|
}
|
|
|
|
OpenGlEnumMapping blit_filter_for_renderer_filter(pp::renderer::BlitFilter filter) noexcept
|
|
{
|
|
switch (filter) {
|
|
case pp::renderer::BlitFilter::nearest:
|
|
return OpenGlEnumMapping { .value = gl_nearest, .supported = true };
|
|
case pp::renderer::BlitFilter::linear:
|
|
return OpenGlEnumMapping { .value = gl_linear, .supported = true };
|
|
default:
|
|
return OpenGlEnumMapping {};
|
|
}
|
|
}
|
|
|
|
std::uint32_t primitive_mode_for_renderer_topology(pp::renderer::PrimitiveTopology topology) noexcept
|
|
{
|
|
switch (topology) {
|
|
case pp::renderer::PrimitiveTopology::triangles:
|
|
return gl_triangles;
|
|
case pp::renderer::PrimitiveTopology::triangle_strip:
|
|
return gl_triangle_strip;
|
|
case pp::renderer::PrimitiveTopology::lines:
|
|
return gl_lines;
|
|
default:
|
|
return 0U;
|
|
}
|
|
}
|
|
|
|
std::uint32_t index_type_for_index_size(std::uint32_t index_size_bytes) noexcept
|
|
{
|
|
switch (index_size_bytes) {
|
|
case 2:
|
|
return gl_unsigned_short;
|
|
case 4:
|
|
return gl_unsigned_int;
|
|
default:
|
|
return 0U;
|
|
}
|
|
}
|
|
|
|
std::uint32_t primitive_mode_for_fill_count(std::uint32_t vertex_or_index_count) noexcept
|
|
{
|
|
if (vertex_or_index_count == 1U) {
|
|
return gl_points;
|
|
}
|
|
|
|
if (vertex_or_index_count == 2U) {
|
|
return gl_lines;
|
|
}
|
|
|
|
return gl_triangles;
|
|
}
|
|
|
|
std::uint32_t primitive_mode_for_stroke_count(std::uint32_t vertex_or_index_count) noexcept
|
|
{
|
|
if (vertex_or_index_count == 1U) {
|
|
return gl_points;
|
|
}
|
|
|
|
return gl_lines;
|
|
}
|
|
|
|
std::uint32_t array_buffer_target() noexcept
|
|
{
|
|
return gl_array_buffer;
|
|
}
|
|
|
|
std::uint32_t element_array_buffer_target() noexcept
|
|
{
|
|
return gl_element_array_buffer;
|
|
}
|
|
|
|
std::uint32_t static_draw_buffer_usage() noexcept
|
|
{
|
|
return gl_static_draw;
|
|
}
|
|
|
|
std::uint32_t vertex_attribute_float_component_type() noexcept
|
|
{
|
|
return gl_float;
|
|
}
|
|
|
|
std::uint32_t vertex_attribute_not_normalized() noexcept
|
|
{
|
|
return gl_false;
|
|
}
|
|
|
|
std::uint32_t vertex_shader_stage() noexcept
|
|
{
|
|
return gl_vertex_shader;
|
|
}
|
|
|
|
std::uint32_t fragment_shader_stage() noexcept
|
|
{
|
|
return gl_fragment_shader;
|
|
}
|
|
|
|
std::uint32_t shader_compile_status_query() noexcept
|
|
{
|
|
return gl_compile_status;
|
|
}
|
|
|
|
std::uint32_t program_link_status_query() noexcept
|
|
{
|
|
return gl_link_status;
|
|
}
|
|
|
|
std::uint32_t active_uniform_count_query() noexcept
|
|
{
|
|
return gl_active_uniforms;
|
|
}
|
|
|
|
std::uint32_t matrix_uniform_not_transposed() noexcept
|
|
{
|
|
return gl_false;
|
|
}
|
|
|
|
std::uint32_t debug_severity_notification() noexcept
|
|
{
|
|
return gl_debug_severity_notification;
|
|
}
|
|
|
|
std::uint32_t debug_severity_low() noexcept
|
|
{
|
|
return gl_debug_severity_low;
|
|
}
|
|
|
|
std::uint32_t debug_severity_medium() noexcept
|
|
{
|
|
return gl_debug_severity_medium;
|
|
}
|
|
|
|
std::uint32_t debug_severity_high() noexcept
|
|
{
|
|
return gl_debug_severity_high;
|
|
}
|
|
|
|
std::uint32_t debug_output_state() noexcept
|
|
{
|
|
return gl_debug_output;
|
|
}
|
|
|
|
std::uint32_t debug_output_synchronous_state() noexcept
|
|
{
|
|
return gl_debug_output_synchronous;
|
|
}
|
|
|
|
std::uint32_t version_string_name() noexcept
|
|
{
|
|
return gl_version;
|
|
}
|
|
|
|
std::uint32_t vendor_string_name() noexcept
|
|
{
|
|
return gl_vendor;
|
|
}
|
|
|
|
std::uint32_t renderer_string_name() noexcept
|
|
{
|
|
return gl_renderer;
|
|
}
|
|
|
|
std::uint32_t shading_language_version_string_name() noexcept
|
|
{
|
|
return gl_shading_language_version;
|
|
}
|
|
|
|
std::uint32_t viewport_query() noexcept
|
|
{
|
|
return gl_viewport;
|
|
}
|
|
|
|
OpenGlViewportRect viewport_for_renderer_viewport(pp::renderer::Viewport viewport) noexcept
|
|
{
|
|
return OpenGlViewportRect {
|
|
.x = viewport.x,
|
|
.y = viewport.y,
|
|
.width = static_cast<std::int32_t>(viewport.width),
|
|
.height = static_cast<std::int32_t>(viewport.height),
|
|
.min_depth = viewport.min_depth,
|
|
.max_depth = viewport.max_depth,
|
|
};
|
|
}
|
|
|
|
std::uint32_t color_clear_value_query() noexcept
|
|
{
|
|
return gl_color_clear_value;
|
|
}
|
|
|
|
std::uint32_t current_program_query() noexcept
|
|
{
|
|
return gl_current_program;
|
|
}
|
|
|
|
std::uint32_t active_texture_query() noexcept
|
|
{
|
|
return gl_active_texture;
|
|
}
|
|
|
|
std::uint32_t texture_binding_2d_query() noexcept
|
|
{
|
|
return gl_texture_binding_2d;
|
|
}
|
|
|
|
std::uint32_t texture_binding_cube_map_query() noexcept
|
|
{
|
|
return gl_texture_binding_cube_map;
|
|
}
|
|
|
|
std::uint32_t sampler_binding_query() noexcept
|
|
{
|
|
return gl_sampler_binding;
|
|
}
|
|
|
|
std::uint32_t blend_state() noexcept
|
|
{
|
|
return gl_blend;
|
|
}
|
|
|
|
std::uint32_t depth_test_state() noexcept
|
|
{
|
|
return gl_depth_test;
|
|
}
|
|
|
|
OpenGlEnumMapping compare_function_for_renderer_compare_op(pp::renderer::CompareOp op) noexcept
|
|
{
|
|
switch (op) {
|
|
case pp::renderer::CompareOp::never:
|
|
return OpenGlEnumMapping { .value = gl_never, .supported = true };
|
|
case pp::renderer::CompareOp::less:
|
|
return OpenGlEnumMapping { .value = gl_less, .supported = true };
|
|
case pp::renderer::CompareOp::equal:
|
|
return OpenGlEnumMapping { .value = gl_equal, .supported = true };
|
|
case pp::renderer::CompareOp::less_or_equal:
|
|
return OpenGlEnumMapping { .value = gl_lequal, .supported = true };
|
|
case pp::renderer::CompareOp::greater:
|
|
return OpenGlEnumMapping { .value = gl_greater, .supported = true };
|
|
case pp::renderer::CompareOp::not_equal:
|
|
return OpenGlEnumMapping { .value = gl_notequal, .supported = true };
|
|
case pp::renderer::CompareOp::greater_or_equal:
|
|
return OpenGlEnumMapping { .value = gl_gequal, .supported = true };
|
|
case pp::renderer::CompareOp::always:
|
|
return OpenGlEnumMapping { .value = gl_always, .supported = true };
|
|
default:
|
|
return OpenGlEnumMapping {};
|
|
}
|
|
}
|
|
|
|
OpenGlDepthState depth_state_for_renderer_depth_state(pp::renderer::DepthState state) noexcept
|
|
{
|
|
const auto compare_function = compare_function_for_renderer_compare_op(state.compare);
|
|
return OpenGlDepthState {
|
|
.test_enabled = state.test_enabled ? gl_boolean_true : gl_boolean_false,
|
|
.write_enabled = state.write_enabled ? gl_boolean_true : gl_boolean_false,
|
|
.compare_function = compare_function.value,
|
|
.supported = compare_function.supported,
|
|
};
|
|
}
|
|
|
|
std::uint32_t scissor_test_state() noexcept
|
|
{
|
|
return gl_scissor_test;
|
|
}
|
|
|
|
OpenGlScissorRect scissor_rect_for_renderer_scissor(pp::renderer::ScissorRect scissor) noexcept
|
|
{
|
|
return OpenGlScissorRect {
|
|
.enabled = scissor.enabled ? gl_boolean_true : gl_boolean_false,
|
|
.x = scissor.x,
|
|
.y = scissor.y,
|
|
.width = static_cast<std::int32_t>(scissor.width),
|
|
.height = static_cast<std::int32_t>(scissor.height),
|
|
};
|
|
}
|
|
|
|
std::uint32_t program_point_size_state() noexcept
|
|
{
|
|
return gl_program_point_size;
|
|
}
|
|
|
|
std::uint32_t line_smooth_state() noexcept
|
|
{
|
|
return gl_line_smooth;
|
|
}
|
|
|
|
std::uint32_t source_alpha_blend_factor() noexcept
|
|
{
|
|
return gl_src_alpha;
|
|
}
|
|
|
|
std::uint32_t one_minus_source_alpha_blend_factor() noexcept
|
|
{
|
|
return gl_one_minus_src_alpha;
|
|
}
|
|
|
|
std::uint32_t add_blend_equation() noexcept
|
|
{
|
|
return gl_func_add;
|
|
}
|
|
|
|
std::uint32_t max_blend_equation() noexcept
|
|
{
|
|
return gl_max;
|
|
}
|
|
|
|
OpenGlEnumMapping blend_factor_for_renderer_factor(pp::renderer::BlendFactor factor) noexcept
|
|
{
|
|
switch (factor) {
|
|
case pp::renderer::BlendFactor::zero:
|
|
return OpenGlEnumMapping { .value = gl_zero, .supported = true };
|
|
case pp::renderer::BlendFactor::one:
|
|
return OpenGlEnumMapping { .value = gl_one, .supported = true };
|
|
case pp::renderer::BlendFactor::source_alpha:
|
|
return OpenGlEnumMapping { .value = gl_src_alpha, .supported = true };
|
|
case pp::renderer::BlendFactor::one_minus_source_alpha:
|
|
return OpenGlEnumMapping { .value = gl_one_minus_src_alpha, .supported = true };
|
|
case pp::renderer::BlendFactor::destination_alpha:
|
|
return OpenGlEnumMapping { .value = gl_dst_alpha, .supported = true };
|
|
case pp::renderer::BlendFactor::one_minus_destination_alpha:
|
|
return OpenGlEnumMapping { .value = gl_one_minus_dst_alpha, .supported = true };
|
|
default:
|
|
return OpenGlEnumMapping {};
|
|
}
|
|
}
|
|
|
|
OpenGlEnumMapping blend_equation_for_renderer_op(pp::renderer::BlendOp op) noexcept
|
|
{
|
|
switch (op) {
|
|
case pp::renderer::BlendOp::add:
|
|
return OpenGlEnumMapping { .value = gl_func_add, .supported = true };
|
|
case pp::renderer::BlendOp::subtract:
|
|
return OpenGlEnumMapping { .value = gl_func_subtract, .supported = true };
|
|
case pp::renderer::BlendOp::reverse_subtract:
|
|
return OpenGlEnumMapping { .value = gl_func_reverse_subtract, .supported = true };
|
|
default:
|
|
return OpenGlEnumMapping {};
|
|
}
|
|
}
|
|
|
|
OpenGlBlendState blend_state_for_renderer_blend_state(pp::renderer::BlendState state) noexcept
|
|
{
|
|
const auto source_color = blend_factor_for_renderer_factor(state.source_color);
|
|
const auto destination_color = blend_factor_for_renderer_factor(state.destination_color);
|
|
const auto color_op = blend_equation_for_renderer_op(state.color_op);
|
|
const auto source_alpha = blend_factor_for_renderer_factor(state.source_alpha);
|
|
const auto destination_alpha = blend_factor_for_renderer_factor(state.destination_alpha);
|
|
const auto alpha_op = blend_equation_for_renderer_op(state.alpha_op);
|
|
|
|
return OpenGlBlendState {
|
|
.enabled = state.enabled ? gl_boolean_true : gl_boolean_false,
|
|
.source_color_factor = source_color.value,
|
|
.destination_color_factor = destination_color.value,
|
|
.color_equation = color_op.value,
|
|
.source_alpha_factor = source_alpha.value,
|
|
.destination_alpha_factor = destination_alpha.value,
|
|
.alpha_equation = alpha_op.value,
|
|
.color_write_mask = color_write_mask_for_renderer_blend_state(state),
|
|
.supported = source_color.supported
|
|
&& destination_color.supported
|
|
&& color_op.supported
|
|
&& source_alpha.supported
|
|
&& destination_alpha.supported
|
|
&& alpha_op.supported,
|
|
};
|
|
}
|
|
|
|
std::uint32_t rgba8_internal_format() noexcept
|
|
{
|
|
return gl_rgba8;
|
|
}
|
|
|
|
std::uint32_t rgba16f_internal_format() noexcept
|
|
{
|
|
return gl_rgba16f;
|
|
}
|
|
|
|
std::uint32_t rgba32f_internal_format() noexcept
|
|
{
|
|
return gl_rgba32f;
|
|
}
|
|
|
|
std::uint8_t color_write_disabled() noexcept
|
|
{
|
|
return gl_boolean_false;
|
|
}
|
|
|
|
std::uint8_t color_write_enabled() noexcept
|
|
{
|
|
return gl_boolean_true;
|
|
}
|
|
|
|
OpenGlColorWriteMask color_write_mask_for_renderer_blend_state(pp::renderer::BlendState state) noexcept
|
|
{
|
|
return OpenGlColorWriteMask {
|
|
.r = state.write_r ? gl_boolean_true : gl_boolean_false,
|
|
.g = state.write_g ? gl_boolean_true : gl_boolean_false,
|
|
.b = state.write_b ? gl_boolean_true : gl_boolean_false,
|
|
.a = state.write_a ? gl_boolean_true : gl_boolean_false,
|
|
};
|
|
}
|
|
|
|
std::uint32_t linear_texture_filter() noexcept
|
|
{
|
|
return gl_linear;
|
|
}
|
|
|
|
std::uint32_t linear_mipmap_linear_texture_filter() noexcept
|
|
{
|
|
return gl_linear_mipmap_linear;
|
|
}
|
|
|
|
std::uint32_t nearest_texture_filter() noexcept
|
|
{
|
|
return gl_nearest;
|
|
}
|
|
|
|
std::uint32_t repeat_texture_wrap() noexcept
|
|
{
|
|
return gl_repeat;
|
|
}
|
|
|
|
std::uint32_t clamp_to_edge_texture_wrap() noexcept
|
|
{
|
|
return gl_clamp_to_edge;
|
|
}
|
|
|
|
std::uint32_t clamp_to_border_texture_wrap() noexcept
|
|
{
|
|
return gl_clamp_to_border;
|
|
}
|
|
|
|
OpenGlEnumMapping sampler_filter_for_renderer_filter(pp::renderer::SamplerFilter filter) noexcept
|
|
{
|
|
switch (filter) {
|
|
case pp::renderer::SamplerFilter::nearest:
|
|
return OpenGlEnumMapping { .value = gl_nearest, .supported = true };
|
|
case pp::renderer::SamplerFilter::linear:
|
|
return OpenGlEnumMapping { .value = gl_linear, .supported = true };
|
|
default:
|
|
return OpenGlEnumMapping {};
|
|
}
|
|
}
|
|
|
|
OpenGlEnumMapping sampler_min_filter_for_renderer_filters(
|
|
pp::renderer::SamplerFilter min_filter,
|
|
pp::renderer::SamplerFilter mip_filter) noexcept
|
|
{
|
|
switch (min_filter) {
|
|
case pp::renderer::SamplerFilter::nearest:
|
|
switch (mip_filter) {
|
|
case pp::renderer::SamplerFilter::nearest:
|
|
return OpenGlEnumMapping { .value = gl_nearest_mipmap_nearest, .supported = true };
|
|
case pp::renderer::SamplerFilter::linear:
|
|
return OpenGlEnumMapping { .value = gl_nearest_mipmap_linear, .supported = true };
|
|
default:
|
|
return OpenGlEnumMapping {};
|
|
}
|
|
case pp::renderer::SamplerFilter::linear:
|
|
switch (mip_filter) {
|
|
case pp::renderer::SamplerFilter::nearest:
|
|
return OpenGlEnumMapping { .value = gl_linear_mipmap_nearest, .supported = true };
|
|
case pp::renderer::SamplerFilter::linear:
|
|
return OpenGlEnumMapping { .value = gl_linear_mipmap_linear, .supported = true };
|
|
default:
|
|
return OpenGlEnumMapping {};
|
|
}
|
|
default:
|
|
return OpenGlEnumMapping {};
|
|
}
|
|
}
|
|
|
|
OpenGlEnumMapping sampler_address_mode_for_renderer_mode(pp::renderer::SamplerAddressMode mode) noexcept
|
|
{
|
|
switch (mode) {
|
|
case pp::renderer::SamplerAddressMode::clamp_to_edge:
|
|
return OpenGlEnumMapping { .value = gl_clamp_to_edge, .supported = true };
|
|
case pp::renderer::SamplerAddressMode::repeat:
|
|
return OpenGlEnumMapping { .value = gl_repeat, .supported = true };
|
|
case pp::renderer::SamplerAddressMode::mirrored_repeat:
|
|
return OpenGlEnumMapping { .value = gl_mirrored_repeat, .supported = true };
|
|
case pp::renderer::SamplerAddressMode::clamp_to_border:
|
|
return OpenGlEnumMapping { .value = gl_clamp_to_border, .supported = true };
|
|
default:
|
|
return OpenGlEnumMapping {};
|
|
}
|
|
}
|
|
|
|
OpenGlSamplerState sampler_state_for_renderer_sampler_desc(pp::renderer::SamplerDesc desc) noexcept
|
|
{
|
|
const auto min_filter = sampler_min_filter_for_renderer_filters(desc.min_filter, desc.mip_filter);
|
|
const auto mag_filter = sampler_filter_for_renderer_filter(desc.mag_filter);
|
|
const auto address_u = sampler_address_mode_for_renderer_mode(desc.address_u);
|
|
const auto address_v = sampler_address_mode_for_renderer_mode(desc.address_v);
|
|
const auto address_w = sampler_address_mode_for_renderer_mode(desc.address_w);
|
|
|
|
return OpenGlSamplerState {
|
|
.min_filter = min_filter.value,
|
|
.mag_filter = mag_filter.value,
|
|
.wrap_s = address_u.value,
|
|
.wrap_t = address_v.value,
|
|
.wrap_r = address_w.value,
|
|
.supported = min_filter.supported
|
|
&& mag_filter.supported
|
|
&& address_u.supported
|
|
&& address_v.supported
|
|
&& address_w.supported,
|
|
};
|
|
}
|
|
|
|
std::uint32_t active_texture_unit(std::uint32_t unit_index) noexcept
|
|
{
|
|
return gl_texture0 + unit_index;
|
|
}
|
|
|
|
std::uint32_t texture_cube_map_target() noexcept
|
|
{
|
|
return gl_texture_cube_map;
|
|
}
|
|
|
|
std::uint32_t cube_map_allocation_face_texture_target(std::uint32_t face_index) noexcept
|
|
{
|
|
if (face_index >= 6U) {
|
|
return 0U;
|
|
}
|
|
|
|
return gl_texture_cube_map_positive_x + face_index;
|
|
}
|
|
|
|
std::span<const std::uint32_t> panopainter_cube_face_texture_targets() noexcept
|
|
{
|
|
static constexpr std::array<std::uint32_t, 6> targets {
|
|
gl_texture_cube_map_negative_z, // front
|
|
gl_texture_cube_map_negative_x, // right
|
|
gl_texture_cube_map_positive_z, // back
|
|
gl_texture_cube_map_positive_x, // left
|
|
gl_texture_cube_map_negative_y, // top
|
|
gl_texture_cube_map_positive_y, // bottom
|
|
};
|
|
|
|
return targets;
|
|
}
|
|
|
|
std::uint32_t cube_face_texture_target(std::uint32_t face_index) noexcept
|
|
{
|
|
const auto targets = panopainter_cube_face_texture_targets();
|
|
if (face_index >= targets.size()) {
|
|
return 0U;
|
|
}
|
|
|
|
return targets[face_index];
|
|
}
|
|
|
|
std::span<const OpenGlTextureParameter> default_render_target_texture_parameters() noexcept
|
|
{
|
|
static constexpr std::array<OpenGlTextureParameter, 4> parameters {
|
|
OpenGlTextureParameter { .name = gl_texture_mag_filter, .value = gl_linear },
|
|
OpenGlTextureParameter { .name = gl_texture_min_filter, .value = gl_linear },
|
|
OpenGlTextureParameter { .name = gl_texture_wrap_s, .value = gl_clamp_to_edge },
|
|
OpenGlTextureParameter { .name = gl_texture_wrap_t, .value = gl_clamp_to_edge },
|
|
};
|
|
|
|
return parameters;
|
|
}
|
|
|
|
std::array<OpenGlTextureParameter, 5> sampler_parameters_for_filter_wrap(
|
|
std::uint32_t filter,
|
|
std::uint32_t wrap) noexcept
|
|
{
|
|
return {
|
|
OpenGlTextureParameter { .name = gl_texture_wrap_s, .value = wrap },
|
|
OpenGlTextureParameter { .name = gl_texture_wrap_t, .value = wrap },
|
|
OpenGlTextureParameter { .name = gl_texture_wrap_r, .value = wrap },
|
|
OpenGlTextureParameter { .name = gl_texture_min_filter, .value = filter },
|
|
OpenGlTextureParameter { .name = gl_texture_mag_filter, .value = filter },
|
|
};
|
|
}
|
|
|
|
std::array<OpenGlTextureParameter, 2> sampler_filter_parameters(
|
|
std::uint32_t filter_min,
|
|
std::uint32_t filter_mag) noexcept
|
|
{
|
|
return {
|
|
OpenGlTextureParameter { .name = gl_texture_min_filter, .value = filter_min },
|
|
OpenGlTextureParameter { .name = gl_texture_mag_filter, .value = filter_mag },
|
|
};
|
|
}
|
|
|
|
std::uint32_t sampler_border_color_parameter_name() noexcept
|
|
{
|
|
return gl_texture_border_color;
|
|
}
|
|
|
|
OpenGlWindowsWglContextConfig windows_wgl_core_context_3_3_config() noexcept
|
|
{
|
|
return {
|
|
.context_attributes = {
|
|
wgl_context_major_version_arb, 3,
|
|
wgl_context_minor_version_arb, 3,
|
|
wgl_context_flags_arb, wgl_context_forward_compatible_bit_arb,
|
|
wgl_context_profile_mask_arb, wgl_context_core_profile_bit_arb,
|
|
0,
|
|
},
|
|
.pixel_format_attributes = {
|
|
wgl_draw_to_window_arb, static_cast<std::int32_t>(gl_boolean_true),
|
|
wgl_support_opengl_arb, static_cast<std::int32_t>(gl_boolean_true),
|
|
wgl_double_buffer_arb, static_cast<std::int32_t>(gl_boolean_true),
|
|
wgl_acceleration_arb, wgl_full_acceleration_arb,
|
|
wgl_pixel_type_arb, wgl_type_rgba_arb,
|
|
wgl_color_bits_arb, 24,
|
|
wgl_depth_bits_arb, 16,
|
|
0,
|
|
},
|
|
};
|
|
}
|
|
|
|
}
|