#include "renderer_gl/opengl_capabilities.h" #include "renderer_gl/shader_bindings.h" #include #include 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 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 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 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::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(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::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(state.draw_framebuffer)); dispatch.bind_framebuffer(read_framebuffer_target(), static_cast(state.read_framebuffer)); dispatch.use_program(static_cast(state.program)); for (std::size_t i = 0; i < state.texture_2d_bindings.size(); ++i) { dispatch.active_texture(active_texture_unit(static_cast(i))); dispatch.bind_texture(texture_2d_target(), static_cast(state.texture_2d_bindings[i])); dispatch.bind_sampler(static_cast(i), static_cast(state.sampler_bindings[i])); } dispatch.active_texture(static_cast(state.active_texture)); dispatch.bind_texture(texture_cube_map_target(), static_cast(state.cube_map_binding)); return pp::foundation::Status::success(); } pp::foundation::Result query_opengl_runtime_info( OpenGlRuntimeInfoDispatch dispatch) noexcept { if (dispatch.get_string == nullptr) { return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument("OpenGL runtime info string callback must not be null")); } return pp::foundation::Result::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> query_opengl_extensions( OpenGlExtensionQueryDispatch dispatch) { if (dispatch.get_integer == nullptr || dispatch.get_string_indexed == nullptr) { return pp::foundation::Result>::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>::success({}); } std::vector extensions; extensions.reserve(static_cast(extension_count)); for (std::int32_t index = 0; index < extension_count; ++index) { const char* extension = dispatch.get_string_indexed( extension_string_name(), static_cast(index)); extensions.emplace_back(extension != nullptr ? extension : ""); } return pp::foundation::Result>::success(std::move(extensions)); } pp::foundation::Result query_opengl_capability_detection( OpenGlExtensionQueryDispatch dispatch, OpenGlRuntime runtime) { auto extensions_result = query_opengl_extensions(dispatch); if (!extensions_result.ok()) { return pp::foundation::Result::failure(extensions_result.status()); } auto extensions = std::move(extensions_result.value()); std::vector extension_views; extension_views.reserve(extensions.size()); for (const auto& extension : extensions) { extension_views.push_back(extension); } return pp::foundation::Result::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 clear_opengl_render_target( OpenGlDefaultClear clear, OpenGlClearDispatch dispatch) noexcept { if (dispatch.clear_color == nullptr || dispatch.clear == nullptr) { return pp::foundation::Status::invalid_argument("OpenGL render-target clear dispatch callbacks must not be null"); } if (clear.mask == 0U) { return pp::foundation::Status::invalid_argument("OpenGL render-target clear mask is invalid"); } 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 clear_opengl_color_buffer_with_write_mask( OpenGlColorMaskedClear clear, OpenGlColorMaskedClearDispatch dispatch) noexcept { if (dispatch.get_boolean == nullptr || dispatch.color_mask == nullptr || dispatch.clear_color == nullptr || dispatch.clear == nullptr) { return pp::foundation::Status::invalid_argument( "OpenGL color-masked clear dispatch callbacks must not be null"); } std::array old_mask {}; dispatch.get_boolean(color_write_mask_query(), old_mask.data()); dispatch.color_mask(clear.mask.r, clear.mask.g, clear.mask.b, clear.mask.a); dispatch.clear_color(clear.color[0], clear.color[1], clear.color[2], clear.color[3]); dispatch.clear(framebuffer_color_buffer_mask()); dispatch.color_mask(old_mask[0], old_mask[1], old_mask[2], old_mask[3]); 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 query_opengl_capability_state( std::uint32_t state, OpenGlCapabilityStateQueryDispatch dispatch) noexcept { if (dispatch.is_enabled == nullptr) { return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument("OpenGL capability state query callback must not be null")); } return pp::foundation::Result::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 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::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::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::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::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 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(texture_ids.size()), texture_ids.data()); return pp::foundation::Status::success(); } pp::foundation::Result 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::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::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::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::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 set_opengl_texture_2d_parameters( std::uint32_t texture_id, std::span parameters, OpenGlTexture2DParameterDispatch dispatch) noexcept { if (dispatch.bind_texture == nullptr || dispatch.tex_parameter_f == nullptr) { return pp::foundation::Status::invalid_argument("OpenGL texture parameter dispatch callbacks must not be null"); } if (texture_id == 0U || parameters.empty()) { return pp::foundation::Status::invalid_argument("OpenGL texture parameter request is invalid"); } for (const auto parameter : parameters) { if (parameter.name == 0U) { return pp::foundation::Status::invalid_argument("OpenGL texture parameter name is invalid"); } } dispatch.bind_texture(texture_2d_target(), texture_id); for (const auto parameter : parameters) { dispatch.tex_parameter_f(texture_2d_target(), parameter.name, static_cast(parameter.value)); } dispatch.bind_texture(texture_2d_target(), default_framebuffer_id()); 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 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::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::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::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(previous_framebuffer)); dispatch.delete_framebuffers(1U, &framebuffer_id); return pp::foundation::Result::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(previous_draw_framebuffer)); dispatch.bind_framebuffer(read_framebuffer_target(), static_cast(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(previous_read_framebuffer)); return pp::foundation::Status::success(); } pp::foundation::Result 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::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::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(binding.draw_framebuffer)); dispatch.bind_framebuffer(read_framebuffer_target(), static_cast(binding.read_framebuffer)); return pp::foundation::Status::success(); } pp::foundation::Result allocate_opengl_render_target_framebuffer( std::uint32_t texture_id, std::uint32_t depth_renderbuffer_id, OpenGlRenderTargetFramebufferAllocationDispatch dispatch) noexcept { if (dispatch.gen_framebuffers == nullptr || dispatch.get_integer == nullptr || dispatch.bind_framebuffer == nullptr || dispatch.framebuffer_texture_2d == nullptr || dispatch.framebuffer_renderbuffer == nullptr || dispatch.check_framebuffer_status == nullptr) { return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument( "OpenGL render-target framebuffer allocation dispatch callbacks must not be null")); } if (texture_id == 0U) { return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument("OpenGL render-target framebuffer texture id is invalid")); } std::int32_t old_framebuffer = 0; dispatch.get_integer(draw_framebuffer_binding_query(), &old_framebuffer); std::uint32_t framebuffer_id = 0U; dispatch.gen_framebuffers(1U, &framebuffer_id); if (framebuffer_id == 0U) { dispatch.bind_framebuffer(framebuffer_target(), static_cast(old_framebuffer)); return pp::foundation::Result::failure( pp::foundation::Status::out_of_range("OpenGL render-target framebuffer allocation returned id 0")); } dispatch.bind_framebuffer(framebuffer_target(), framebuffer_id); dispatch.framebuffer_texture_2d( framebuffer_target(), framebuffer_color_attachment(), texture_2d_target(), texture_id, 0); if (depth_renderbuffer_id != 0U) { dispatch.framebuffer_renderbuffer( framebuffer_target(), framebuffer_depth_attachment(), renderbuffer_target(), depth_renderbuffer_id); } const auto framebuffer_status = dispatch.check_framebuffer_status(framebuffer_target()); dispatch.bind_framebuffer(framebuffer_target(), static_cast(old_framebuffer)); return pp::foundation::Result::success( OpenGlRenderTargetFramebufferAllocationResult { .framebuffer_id = framebuffer_id, .framebuffer_status = framebuffer_status, }); } pp::foundation::Status delete_opengl_framebuffer( std::uint32_t framebuffer_id, OpenGlFramebufferDeleteDispatch dispatch) noexcept { if (dispatch.delete_framebuffers == nullptr) { return pp::foundation::Status::invalid_argument("OpenGL framebuffer delete dispatch callback must not be null"); } if (framebuffer_id == 0U) { return pp::foundation::Status::success(); } dispatch.delete_framebuffers(1U, &framebuffer_id); return pp::foundation::Status::success(); } pp::foundation::Result allocate_opengl_pixel_buffer( OpenGlPixelBufferAllocationDispatch dispatch) noexcept { if (dispatch.gen_buffers == nullptr) { return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument("OpenGL pixel-buffer allocation dispatch callback must not be null")); } std::uint32_t buffer_id = 0U; dispatch.gen_buffers(1U, &buffer_id); if (buffer_id == 0U) { return pp::foundation::Result::failure( pp::foundation::Status::out_of_range("OpenGL pixel-buffer allocation returned id 0")); } return pp::foundation::Result::success(buffer_id); } pp::foundation::Result readback_opengl_framebuffer_to_pixel_buffer( std::int32_t width, std::int32_t height, OpenGlReadbackFormat format, OpenGlPixelBufferReadbackDispatch dispatch) noexcept { if (dispatch.gen_buffers == nullptr || dispatch.bind_buffer == nullptr || dispatch.buffer_data == nullptr || dispatch.read_pixels == nullptr) { return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument("OpenGL pixel-buffer readback dispatch callbacks must not be null")); } if (width <= 0 || height <= 0 || format.pixel_format == 0U || format.component_type == 0U || format.bytes_per_pixel == 0U) { return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument("OpenGL pixel-buffer readback parameters are invalid")); } const auto buffer_id = allocate_opengl_pixel_buffer(OpenGlPixelBufferAllocationDispatch { .gen_buffers = dispatch.gen_buffers, }); if (!buffer_id.ok()) { return buffer_id; } const auto target = pixel_pack_buffer_target(); dispatch.bind_buffer(target, buffer_id.value()); dispatch.buffer_data( target, static_cast(readback_byte_count( format, static_cast(width), static_cast(height))), nullptr, pixel_buffer_stream_read_usage()); dispatch.read_pixels(0, 0, width, height, format.pixel_format, format.component_type, nullptr); dispatch.bind_buffer(target, 0U); return pp::foundation::Result::success(buffer_id.value()); } pp::foundation::Result map_opengl_pixel_buffer( std::uint32_t buffer_id, std::int32_t width, std::int32_t height, OpenGlReadbackFormat format, OpenGlPixelBufferMapDispatch dispatch) noexcept { if (dispatch.bind_buffer == nullptr || dispatch.map_buffer_range == nullptr) { return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument("OpenGL pixel-buffer map dispatch callbacks must not be null")); } if (buffer_id == 0U || width <= 0 || height <= 0 || format.pixel_format == 0U || format.component_type == 0U || format.bytes_per_pixel == 0U) { return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument("OpenGL pixel-buffer map parameters are invalid")); } const auto target = pixel_pack_buffer_target(); dispatch.bind_buffer(target, buffer_id); void* const mapped = dispatch.map_buffer_range( target, 0, static_cast(readback_byte_count( format, static_cast(width), static_cast(height))), pixel_buffer_map_read_access()); dispatch.bind_buffer(target, 0U); if (mapped == nullptr) { return pp::foundation::Result::failure( pp::foundation::Status::out_of_range("OpenGL pixel-buffer map returned null")); } return pp::foundation::Result::success(mapped); } pp::foundation::Status unmap_opengl_pixel_buffer( std::uint32_t buffer_id, OpenGlPixelBufferUnmapDispatch dispatch) noexcept { if (dispatch.bind_buffer == nullptr || dispatch.unmap_buffer == nullptr) { return pp::foundation::Status::invalid_argument("OpenGL pixel-buffer unmap dispatch callbacks must not be null"); } if (buffer_id == 0U) { return pp::foundation::Status::invalid_argument("OpenGL pixel-buffer unmap buffer id is invalid"); } const auto target = pixel_pack_buffer_target(); dispatch.bind_buffer(target, buffer_id); dispatch.unmap_buffer(target); dispatch.bind_buffer(target, 0U); return pp::foundation::Status::success(); } pp::foundation::Status delete_opengl_pixel_buffer( std::uint32_t buffer_id, OpenGlPixelBufferDeleteDispatch dispatch) noexcept { if (dispatch.delete_buffers == nullptr) { return pp::foundation::Status::invalid_argument("OpenGL pixel-buffer delete dispatch callback must not be null"); } if (buffer_id == 0U) { return pp::foundation::Status::success(); } dispatch.delete_buffers(1U, &buffer_id); return pp::foundation::Status::success(); } pp::foundation::Result 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::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::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::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::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 create_opengl_sampler( std::span parameters, OpenGlSamplerCreateDispatch dispatch) noexcept { if (dispatch.gen_samplers == nullptr || dispatch.sampler_parameter_i == nullptr) { return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument("OpenGL sampler create dispatch callbacks must not be null")); } if (parameters.empty()) { return pp::foundation::Result::failure( pp::foundation::Status::invalid_argument("OpenGL sampler parameters are invalid")); } for (const auto parameter : parameters) { if (parameter.name == 0U) { return pp::foundation::Result::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::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::failure(status); } return pp::foundation::Result::success(sampler_id); } pp::foundation::Status set_opengl_sampler_parameters( std::uint32_t sampler_id, std::span 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(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(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 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::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::failure( pp::foundation::Status::invalid_argument("OpenGL attribute-location parameters are invalid")); } return pp::foundation::Result::success( dispatch.get_attrib_location(program_id, attribute_name)); } pp::foundation::Result 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::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::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::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::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 link_opengl_shader_program( std::uint32_t vertex_shader_id, std::uint32_t fragment_shader_id, std::span 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::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::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::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::success(info); } pp::foundation::Result 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::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::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::success(value); } pp::foundation::Result 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::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::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::success(info); } pp::foundation::Result 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::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::failure( pp::foundation::Status::invalid_argument("OpenGL uniform-location parameters are invalid")); } return pp::foundation::Result::success( dispatch.get_uniform_location(program_id, uniform_name)); } pp::foundation::Result 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::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::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::failure( pp::foundation::Status::invalid_argument("OpenGL mesh vertex attribute parameters are invalid")); } } std::array 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::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 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::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(attribute.offset)); } } dispatch.bind_vertex_array(0U); return pp::foundation::Result::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(width); const auto wide_height = static_cast(height); if (wide_width != 0U && wide_height > std::numeric_limits::max() / wide_width) { return 0U; } const auto pixels = wide_width * wide_height; if (pixels != 0U && format.bytes_per_pixel > std::numeric_limits::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(viewport.width), .height = static_cast(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(scissor.width), .height = static_cast(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 panopainter_cube_face_texture_targets() noexcept { static constexpr std::array 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 default_render_target_texture_parameters() noexcept { static constexpr std::array 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 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 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(gl_boolean_true), wgl_support_opengl_arb, static_cast(gl_boolean_true), wgl_double_buffer_arb, static_cast(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, }, }; } }