ladybird/Userland/Libraries/LibGL/ContextParameter.cpp
Jelle Raaijmakers e615af886e LibGL: Report color buffer bits instead of texture bits
For `GL_RED_BITS`, `GL_GREEN_BITS`, `GL_BLUE_BITS` and `GL_ALPHA_BITS`
we were reporting the values we use in LibSoftGPU for textures. This
fixes these context parameters to actually report the color buffer
bits.
2022-08-27 12:28:05 +02:00

612 lines
23 KiB
C++

/*
* Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
* Copyright (c) 2021, Stephan Unverwerth <s.unverwerth@serenityos.org>
* Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Debug.h>
#include <LibGL/GLContext.h>
namespace GL {
Optional<ContextParameter> GLContext::get_context_parameter(GLenum name)
{
switch (name) {
case GL_ALPHA_BITS:
return ContextParameter { .type = GL_INT, .value = { .integer_value = sizeof(u8) * 8 } };
case GL_ALPHA_TEST:
return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_alpha_test_enabled } };
case GL_BLEND:
return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_blend_enabled } };
case GL_BLEND_DST_ALPHA:
return ContextParameter { .type = GL_INT, .value = { .integer_value = static_cast<GLint>(m_blend_destination_factor) } };
case GL_BLEND_SRC_ALPHA:
return ContextParameter { .type = GL_INT, .value = { .integer_value = static_cast<GLint>(m_blend_source_factor) } };
case GL_BLUE_BITS:
return ContextParameter { .type = GL_INT, .value = { .integer_value = sizeof(u8) * 8 } };
case GL_COLOR_MATERIAL:
return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_color_material_enabled } };
case GL_COLOR_MATERIAL_FACE:
return ContextParameter { .type = GL_INT, .value = { .integer_value = static_cast<GLint>(m_color_material_face) } };
case GL_COLOR_MATERIAL_MODE:
return ContextParameter { .type = GL_INT, .value = { .integer_value = static_cast<GLint>(m_color_material_mode) } };
case GL_CULL_FACE:
return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_cull_faces } };
case GL_DEPTH_BITS:
return ContextParameter { .type = GL_INT, .value = { .integer_value = sizeof(float) * 8 } };
case GL_DEPTH_TEST:
return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_depth_test_enabled } };
case GL_DITHER:
return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_dither_enabled } };
case GL_DOUBLEBUFFER:
return ContextParameter { .type = GL_BOOL, .value = { .boolean_value = true } };
case GL_FOG: {
auto fog_enabled = m_rasterizer->options().fog_enabled;
return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = fog_enabled } };
}
case GL_GREEN_BITS:
return ContextParameter { .type = GL_INT, .value = { .integer_value = sizeof(u8) * 8 } };
case GL_LIGHTING:
return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_lighting_enabled } };
case GL_LINE_SMOOTH:
return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_line_smooth } };
case GL_MAX_CLIP_PLANES:
return ContextParameter { .type = GL_INT, .value = { .integer_value = static_cast<GLint>(m_device_info.max_clip_planes) } };
case GL_MAX_LIGHTS:
return ContextParameter { .type = GL_INT, .value = { .integer_value = static_cast<GLint>(m_device_info.num_lights) } };
case GL_MAX_MODELVIEW_STACK_DEPTH:
return ContextParameter { .type = GL_INT, .value = { .integer_value = MODELVIEW_MATRIX_STACK_LIMIT } };
case GL_MAX_PROJECTION_STACK_DEPTH:
return ContextParameter { .type = GL_INT, .value = { .integer_value = PROJECTION_MATRIX_STACK_LIMIT } };
case GL_MAX_TEXTURE_SIZE:
return ContextParameter { .type = GL_INT, .value = { .integer_value = 4096 } };
case GL_MAX_TEXTURE_STACK_DEPTH:
return ContextParameter { .type = GL_INT, .value = { .integer_value = TEXTURE_MATRIX_STACK_LIMIT } };
case GL_MAX_TEXTURE_UNITS:
return ContextParameter { .type = GL_INT, .value = { .integer_value = static_cast<GLint>(m_texture_units.size()) } };
case GL_NORMAL_ARRAY_TYPE:
return ContextParameter { .type = GL_INT, .value = { .integer_value = GL_FLOAT } };
case GL_NORMALIZE:
return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_normalize } };
case GL_PACK_ALIGNMENT:
return ContextParameter { .type = GL_INT, .value = { .integer_value = m_pack_alignment } };
case GL_PACK_IMAGE_HEIGHT:
return ContextParameter { .type = GL_BOOL, .value = { .integer_value = 0 } };
case GL_PACK_LSB_FIRST:
return ContextParameter { .type = GL_BOOL, .value = { .boolean_value = false } };
case GL_PACK_ROW_LENGTH:
return ContextParameter { .type = GL_INT, .value = { .integer_value = 0 } };
case GL_PACK_SKIP_PIXELS:
return ContextParameter { .type = GL_INT, .value = { .integer_value = 0 } };
case GL_PACK_SKIP_ROWS:
return ContextParameter { .type = GL_INT, .value = { .integer_value = 0 } };
case GL_PACK_SWAP_BYTES:
return ContextParameter { .type = GL_BOOL, .value = { .boolean_value = false } };
case GL_POINT_SMOOTH:
return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_point_smooth } };
case GL_POINT_SIZE:
return ContextParameter { .type = GL_DOUBLE, .value = { .double_value = static_cast<GLdouble>(m_point_size) } };
case GL_POLYGON_OFFSET_FILL:
return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_depth_offset_enabled } };
case GL_RED_BITS:
return ContextParameter { .type = GL_INT, .value = { .integer_value = sizeof(u8) * 8 } };
case GL_SCISSOR_BOX: {
auto scissor_box = m_rasterizer->options().scissor_box;
return ContextParameter {
.type = GL_INT,
.count = 4,
.value = {
.integer_list = {
scissor_box.x(),
scissor_box.y(),
scissor_box.width(),
scissor_box.height(),
} }
};
}
case GL_SCISSOR_TEST: {
auto scissor_enabled = m_rasterizer->options().scissor_enabled;
return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = scissor_enabled } };
}
case GL_STENCIL_BITS:
return ContextParameter { .type = GL_INT, .value = { .integer_value = m_device_info.stencil_bits } };
case GL_STENCIL_CLEAR_VALUE:
return ContextParameter { .type = GL_INT, .value = { .integer_value = m_clear_stencil } };
case GL_STENCIL_TEST:
return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_stencil_test_enabled } };
case GL_TEXTURE_1D:
return ContextParameter { .type = GL_BOOL, .value = { .boolean_value = m_active_texture_unit->texture_1d_enabled() } };
case GL_TEXTURE_2D:
return ContextParameter { .type = GL_BOOL, .value = { .boolean_value = m_active_texture_unit->texture_2d_enabled() } };
case GL_TEXTURE_3D:
return ContextParameter { .type = GL_BOOL, .value = { .boolean_value = m_active_texture_unit->texture_3d_enabled() } };
case GL_TEXTURE_CUBE_MAP:
return ContextParameter { .type = GL_BOOL, .value = { .boolean_value = m_active_texture_unit->texture_cube_map_enabled() } };
case GL_TEXTURE_GEN_Q:
case GL_TEXTURE_GEN_R:
case GL_TEXTURE_GEN_S:
case GL_TEXTURE_GEN_T: {
auto generation_enabled = texture_coordinate_generation(m_active_texture_unit_index, name).enabled;
return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = generation_enabled } };
}
case GL_UNPACK_ALIGNMENT:
return ContextParameter { .type = GL_INT, .value = { .integer_value = m_unpack_alignment } };
case GL_UNPACK_IMAGE_HEIGHT:
return ContextParameter { .type = GL_BOOL, .value = { .integer_value = 0 } };
case GL_UNPACK_LSB_FIRST:
return ContextParameter { .type = GL_BOOL, .value = { .boolean_value = false } };
case GL_UNPACK_ROW_LENGTH:
return ContextParameter { .type = GL_INT, .value = { .integer_value = m_unpack_row_length } };
case GL_UNPACK_SKIP_PIXELS:
return ContextParameter { .type = GL_INT, .value = { .integer_value = 0 } };
case GL_UNPACK_SKIP_ROWS:
return ContextParameter { .type = GL_INT, .value = { .integer_value = 0 } };
case GL_UNPACK_SWAP_BYTES:
return ContextParameter { .type = GL_BOOL, .value = { .boolean_value = false } };
case GL_VIEWPORT:
return ContextParameter {
.type = GL_INT,
.count = 4,
.value = {
.integer_list = {
m_viewport.x(),
m_viewport.y(),
m_viewport.width(),
m_viewport.height(),
} }
};
default:
dbgln_if(GL_DEBUG, "get_context_parameter({:#x}): unknown context parameter", name);
return {};
}
}
void GLContext::gl_disable(GLenum capability)
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_disable, capability);
RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
auto rasterizer_options = m_rasterizer->options();
bool update_rasterizer_options = false;
switch (capability) {
case GL_CLIP_PLANE0:
case GL_CLIP_PLANE1:
case GL_CLIP_PLANE2:
case GL_CLIP_PLANE3:
case GL_CLIP_PLANE4:
case GL_CLIP_PLANE5: {
auto plane_idx = static_cast<size_t>(capability) - GL_CLIP_PLANE0;
m_clip_plane_attributes.enabled &= ~(1 << plane_idx);
m_clip_planes_dirty = true;
break;
}
case GL_COLOR_MATERIAL:
m_color_material_enabled = false;
break;
case GL_CULL_FACE:
m_cull_faces = false;
rasterizer_options.enable_culling = false;
update_rasterizer_options = true;
break;
case GL_DEPTH_TEST:
m_depth_test_enabled = false;
rasterizer_options.enable_depth_test = false;
update_rasterizer_options = true;
break;
case GL_BLEND:
m_blend_enabled = false;
rasterizer_options.enable_blending = false;
update_rasterizer_options = true;
break;
case GL_ALPHA_TEST:
m_alpha_test_enabled = false;
rasterizer_options.enable_alpha_test = false;
update_rasterizer_options = true;
break;
case GL_DITHER:
m_dither_enabled = false;
break;
case GL_FOG:
rasterizer_options.fog_enabled = false;
update_rasterizer_options = true;
break;
case GL_LIGHTING:
m_lighting_enabled = false;
rasterizer_options.lighting_enabled = false;
update_rasterizer_options = true;
break;
case GL_LIGHT0:
case GL_LIGHT1:
case GL_LIGHT2:
case GL_LIGHT3:
case GL_LIGHT4:
case GL_LIGHT5:
case GL_LIGHT6:
case GL_LIGHT7:
m_light_states.at(capability - GL_LIGHT0).is_enabled = false;
m_light_state_is_dirty = true;
break;
case GL_LINE_SMOOTH:
m_line_smooth = false;
rasterizer_options.line_smooth = false;
update_rasterizer_options = true;
break;
case GL_NORMALIZE:
m_normalize = false;
rasterizer_options.normalization_enabled = false;
update_rasterizer_options = true;
break;
case GL_POINT_SMOOTH:
m_point_smooth = false;
rasterizer_options.point_smooth = false;
update_rasterizer_options = true;
break;
case GL_POLYGON_OFFSET_FILL:
m_depth_offset_enabled = false;
rasterizer_options.depth_offset_enabled = false;
update_rasterizer_options = true;
break;
case GL_SCISSOR_TEST:
rasterizer_options.scissor_enabled = false;
update_rasterizer_options = true;
break;
case GL_STENCIL_TEST:
m_stencil_test_enabled = false;
rasterizer_options.enable_stencil_test = false;
update_rasterizer_options = true;
break;
case GL_TEXTURE_1D:
m_active_texture_unit->set_texture_1d_enabled(false);
m_sampler_config_is_dirty = true;
break;
case GL_TEXTURE_2D:
m_active_texture_unit->set_texture_2d_enabled(false);
m_sampler_config_is_dirty = true;
break;
case GL_TEXTURE_3D:
m_active_texture_unit->set_texture_3d_enabled(false);
m_sampler_config_is_dirty = true;
break;
case GL_TEXTURE_CUBE_MAP:
m_active_texture_unit->set_texture_cube_map_enabled(false);
m_sampler_config_is_dirty = true;
break;
case GL_TEXTURE_GEN_Q:
case GL_TEXTURE_GEN_R:
case GL_TEXTURE_GEN_S:
case GL_TEXTURE_GEN_T:
texture_coordinate_generation(m_active_texture_unit_index, capability).enabled = false;
m_texcoord_generation_dirty = true;
break;
default:
dbgln_if(GL_DEBUG, "gl_disable({:#x}): unknown parameter", capability);
RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
}
if (update_rasterizer_options)
m_rasterizer->set_options(rasterizer_options);
}
void GLContext::gl_disable_client_state(GLenum cap)
{
RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
switch (cap) {
case GL_COLOR_ARRAY:
m_client_side_color_array_enabled = false;
break;
case GL_NORMAL_ARRAY:
m_client_side_normal_array_enabled = false;
break;
case GL_TEXTURE_COORD_ARRAY:
m_client_side_texture_coord_array_enabled[m_client_active_texture] = false;
break;
case GL_VERTEX_ARRAY:
m_client_side_vertex_array_enabled = false;
break;
default:
RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
}
}
void GLContext::gl_enable(GLenum capability)
{
APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_enable, capability);
RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
auto rasterizer_options = m_rasterizer->options();
bool update_rasterizer_options = false;
switch (capability) {
case GL_CLIP_PLANE0:
case GL_CLIP_PLANE1:
case GL_CLIP_PLANE2:
case GL_CLIP_PLANE3:
case GL_CLIP_PLANE4:
case GL_CLIP_PLANE5: {
auto plane_idx = static_cast<size_t>(capability) - GL_CLIP_PLANE0;
m_clip_plane_attributes.enabled |= (1 << plane_idx);
m_clip_planes_dirty = true;
break;
}
case GL_COLOR_MATERIAL:
m_color_material_enabled = true;
break;
case GL_CULL_FACE:
m_cull_faces = true;
rasterizer_options.enable_culling = true;
update_rasterizer_options = true;
break;
case GL_DEPTH_TEST:
m_depth_test_enabled = true;
rasterizer_options.enable_depth_test = true;
update_rasterizer_options = true;
break;
case GL_BLEND:
m_blend_enabled = true;
rasterizer_options.enable_blending = true;
update_rasterizer_options = true;
break;
case GL_ALPHA_TEST:
m_alpha_test_enabled = true;
rasterizer_options.enable_alpha_test = true;
update_rasterizer_options = true;
break;
case GL_DITHER:
m_dither_enabled = true;
break;
case GL_FOG:
rasterizer_options.fog_enabled = true;
update_rasterizer_options = true;
break;
case GL_LIGHTING:
m_lighting_enabled = true;
rasterizer_options.lighting_enabled = true;
update_rasterizer_options = true;
break;
case GL_LIGHT0:
case GL_LIGHT1:
case GL_LIGHT2:
case GL_LIGHT3:
case GL_LIGHT4:
case GL_LIGHT5:
case GL_LIGHT6:
case GL_LIGHT7:
m_light_states.at(capability - GL_LIGHT0).is_enabled = true;
m_light_state_is_dirty = true;
break;
case GL_LINE_SMOOTH:
m_line_smooth = true;
rasterizer_options.line_smooth = true;
update_rasterizer_options = true;
break;
case GL_NORMALIZE:
m_normalize = true;
rasterizer_options.normalization_enabled = true;
update_rasterizer_options = true;
break;
case GL_POINT_SMOOTH:
m_point_smooth = true;
rasterizer_options.point_smooth = true;
update_rasterizer_options = true;
break;
case GL_POLYGON_OFFSET_FILL:
m_depth_offset_enabled = true;
rasterizer_options.depth_offset_enabled = true;
update_rasterizer_options = true;
break;
case GL_SCISSOR_TEST:
rasterizer_options.scissor_enabled = true;
update_rasterizer_options = true;
break;
case GL_STENCIL_TEST:
m_stencil_test_enabled = true;
rasterizer_options.enable_stencil_test = true;
update_rasterizer_options = true;
break;
case GL_TEXTURE_1D:
m_active_texture_unit->set_texture_1d_enabled(true);
m_sampler_config_is_dirty = true;
break;
case GL_TEXTURE_2D:
m_active_texture_unit->set_texture_2d_enabled(true);
m_sampler_config_is_dirty = true;
break;
case GL_TEXTURE_3D:
m_active_texture_unit->set_texture_3d_enabled(true);
m_sampler_config_is_dirty = true;
break;
case GL_TEXTURE_CUBE_MAP:
m_active_texture_unit->set_texture_cube_map_enabled(true);
m_sampler_config_is_dirty = true;
break;
case GL_TEXTURE_GEN_Q:
case GL_TEXTURE_GEN_R:
case GL_TEXTURE_GEN_S:
case GL_TEXTURE_GEN_T:
texture_coordinate_generation(m_active_texture_unit_index, capability).enabled = true;
m_texcoord_generation_dirty = true;
break;
default:
dbgln_if(GL_DEBUG, "gl_enable({:#x}): unknown parameter", capability);
RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
}
if (update_rasterizer_options)
m_rasterizer->set_options(rasterizer_options);
}
void GLContext::gl_enable_client_state(GLenum cap)
{
RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
switch (cap) {
case GL_COLOR_ARRAY:
m_client_side_color_array_enabled = true;
break;
case GL_NORMAL_ARRAY:
m_client_side_normal_array_enabled = true;
break;
case GL_TEXTURE_COORD_ARRAY:
m_client_side_texture_coord_array_enabled[m_client_active_texture] = true;
break;
case GL_VERTEX_ARRAY:
m_client_side_vertex_array_enabled = true;
break;
default:
RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
}
}
void GLContext::gl_get_booleanv(GLenum pname, GLboolean* data)
{
RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
auto optional_parameter = get_context_parameter(pname);
RETURN_WITH_ERROR_IF(!optional_parameter.has_value(), GL_INVALID_ENUM);
auto parameter = optional_parameter.release_value();
switch (parameter.type) {
case GL_BOOL:
*data = parameter.value.boolean_value ? GL_TRUE : GL_FALSE;
break;
case GL_DOUBLE:
*data = (parameter.value.double_value == 0.0) ? GL_FALSE : GL_TRUE;
break;
case GL_INT:
*data = (parameter.value.integer_value == 0) ? GL_FALSE : GL_TRUE;
break;
default:
VERIFY_NOT_REACHED();
}
}
void GLContext::gl_get_doublev(GLenum pname, GLdouble* params)
{
get_floating_point(pname, params);
}
template<typename T>
void GLContext::get_floating_point(GLenum pname, T* params)
{
RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
// Handle matrix retrieval first
auto flatten_and_assign_matrix = [&params](FloatMatrix4x4 const& matrix) {
auto elements = matrix.elements();
for (size_t i = 0; i < 4; ++i) {
for (size_t j = 0; j < 4; ++j) {
// Return transposed matrix since OpenGL defines them as column-major
params[i * 4 + j] = static_cast<T>(elements[j][i]);
}
}
};
switch (pname) {
case GL_MODELVIEW_MATRIX:
flatten_and_assign_matrix(m_model_view_matrix);
return;
case GL_PROJECTION_MATRIX:
flatten_and_assign_matrix(m_projection_matrix);
return;
}
// Regular parameters
auto optional_parameter = get_context_parameter(pname);
RETURN_WITH_ERROR_IF(!optional_parameter.has_value(), GL_INVALID_ENUM);
auto parameter = optional_parameter.release_value();
switch (parameter.type) {
case GL_BOOL:
*params = parameter.value.boolean_value ? GL_TRUE : GL_FALSE;
break;
case GL_DOUBLE:
for (size_t i = 0; i < parameter.count; ++i)
params[i] = parameter.value.double_list[i];
break;
case GL_INT:
for (size_t i = 0; i < parameter.count; ++i)
params[i] = parameter.value.integer_list[i];
break;
default:
VERIFY_NOT_REACHED();
}
}
void GLContext::gl_get_floatv(GLenum pname, GLfloat* params)
{
get_floating_point(pname, params);
}
void GLContext::gl_get_integerv(GLenum pname, GLint* data)
{
RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
auto optional_parameter = get_context_parameter(pname);
RETURN_WITH_ERROR_IF(!optional_parameter.has_value(), GL_INVALID_ENUM);
auto parameter = optional_parameter.release_value();
switch (parameter.type) {
case GL_BOOL:
*data = parameter.value.boolean_value ? GL_TRUE : GL_FALSE;
break;
case GL_DOUBLE: {
double const int_range = static_cast<double>(NumericLimits<GLint>::max()) - NumericLimits<GLint>::min();
for (size_t i = 0; i < parameter.count; ++i) {
double const result_factor = (clamp(parameter.value.double_list[i], -1.0, 1.0) + 1.0) / 2.0;
data[i] = static_cast<GLint>(NumericLimits<GLint>::min() + result_factor * int_range);
}
break;
}
case GL_INT:
for (size_t i = 0; i < parameter.count; ++i)
data[i] = parameter.value.integer_list[i];
break;
default:
VERIFY_NOT_REACHED();
}
}
GLboolean GLContext::gl_is_enabled(GLenum capability)
{
RETURN_VALUE_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION, 0);
auto optional_parameter = get_context_parameter(capability);
RETURN_VALUE_WITH_ERROR_IF(!optional_parameter.has_value(), GL_INVALID_ENUM, 0);
auto parameter = optional_parameter.release_value();
RETURN_VALUE_WITH_ERROR_IF(!parameter.is_capability, GL_INVALID_ENUM, 0);
return parameter.value.boolean_value;
}
GPU::PackingSpecification GLContext::get_packing_specification(PackingType packing_type)
{
// Make use of the fact that the GL_PACK_* and GL_UNPACK_* enum constants are in the exact same order
auto const offset = (packing_type == PackingType::Unpack) ? 0 : (GL_PACK_SWAP_BYTES - GL_UNPACK_SWAP_BYTES);
auto get_packing_value = [&](GLenum packing_parameter) -> GLint {
GLint value;
gl_get_integerv(packing_parameter + offset, &value);
return value;
};
// FIXME: add support for GL_UNPACK_SKIP_PIXELS, GL_UNPACK_SKIP_ROWS and GL_UNPACK_LSB_FIRST
GLint byte_alignment { get_packing_value(GL_UNPACK_ALIGNMENT) };
GLint swap_bytes { get_packing_value(GL_UNPACK_SWAP_BYTES) };
GLint depth_stride { get_packing_value(GL_UNPACK_IMAGE_HEIGHT) };
GLint row_stride { get_packing_value(GL_UNPACK_ROW_LENGTH) };
return {
.depth_stride = static_cast<u32>(depth_stride),
.row_stride = static_cast<u32>(row_stride),
.byte_alignment = static_cast<u8>(byte_alignment),
.component_bytes_order = swap_bytes == GL_TRUE ? GPU::ComponentBytesOrder::Reversed : GPU::ComponentBytesOrder::Normal,
};
}
}