#include "renderer_gl/opengl_capabilities.h" #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_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_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_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_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 = 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_clamp_to_edge = 0x812FU; 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; [[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; } 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 {}; } } 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 { return static_cast(width) * height * 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; } 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 color_write_mask_query() noexcept { return gl_color_writemask; } std::uint32_t framebuffer_blit_filter(bool linear) noexcept { return linear ? gl_linear : gl_nearest; } 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 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; } }