瀏覽代碼

LibGL+LibGPU+LibSoftGPU: Implement matrix stack per texture unit

Each texture unit now has its own texture transformation matrix stack.
Introduce a new texture unit configuration that is synced when changed.
Because we're no longer passing a silly `Vector` when drawing each
primitive, this results in a slightly improved frames per second :^)
Jelle Raaijmakers 2 年之前
父節點
當前提交
00d46e5d77

+ 24 - 0
Tests/LibGL/TestRender.cpp

@@ -228,3 +228,27 @@ TEST_CASE(0007_test_rgba_to_rgb_texture)
     context->present();
     context->present();
     expect_bitmap_equals_reference(context->frontbuffer(), "0007_test_rgba_to_rgb_texture"sv);
     expect_bitmap_equals_reference(context->frontbuffer(), "0007_test_rgba_to_rgb_texture"sv);
 }
 }
+
+TEST_CASE(0008_test_pop_matrix_regression)
+{
+    auto context = create_testing_context(64, 64);
+
+    // Load identity matrix after popping
+    glMatrixMode(GL_MODELVIEW);
+    glTranslatef(10.f, 10.f, 10.f);
+    glPushMatrix();
+    glPopMatrix();
+    glLoadIdentity();
+
+    glBegin(GL_TRIANGLES);
+    glColor3f(0.f, 1.f, 0.f);
+    glVertex2f(.5f, -.5f);
+    glVertex2f(.0f, .5f);
+    glVertex2f(-.5f, -.5f);
+    glEnd();
+
+    EXPECT_EQ(glGetError(), 0u);
+
+    context->present();
+    expect_bitmap_equals_reference(context->frontbuffer(), "0008_test_pop_matrix_regression"sv);
+}

二進制
Tests/LibGL/reference-images/0008_test_pop_matrix_regression.qoi


+ 1 - 1
Userland/Libraries/LibGL/ClipPlane.cpp

@@ -22,7 +22,7 @@ void GLContext::gl_clip_plane(GLenum plane, GLdouble const* equation)
     auto plane_idx = static_cast<size_t>(plane) - GL_CLIP_PLANE0;
     auto plane_idx = static_cast<size_t>(plane) - GL_CLIP_PLANE0;
 
 
     auto eqn = FloatVector4(equation[0], equation[1], equation[2], equation[3]);
     auto eqn = FloatVector4(equation[0], equation[1], equation[2], equation[3]);
-    m_clip_plane_attributes.eye_clip_plane[plane_idx] = m_model_view_matrix * eqn;
+    m_clip_plane_attributes.eye_clip_plane[plane_idx] = model_view_matrix() * eqn;
     m_clip_planes_dirty = true;
     m_clip_planes_dirty = true;
 }
 }
 
 

+ 12 - 4
Userland/Libraries/LibGL/ContextParameter.cpp

@@ -276,25 +276,29 @@ void GLContext::gl_disable(GLenum capability)
     case GL_TEXTURE_1D:
     case GL_TEXTURE_1D:
         m_active_texture_unit->set_texture_1d_enabled(false);
         m_active_texture_unit->set_texture_1d_enabled(false);
         m_sampler_config_is_dirty = true;
         m_sampler_config_is_dirty = true;
+        m_texture_units_dirty = true;
         break;
         break;
     case GL_TEXTURE_2D:
     case GL_TEXTURE_2D:
         m_active_texture_unit->set_texture_2d_enabled(false);
         m_active_texture_unit->set_texture_2d_enabled(false);
         m_sampler_config_is_dirty = true;
         m_sampler_config_is_dirty = true;
+        m_texture_units_dirty = true;
         break;
         break;
     case GL_TEXTURE_3D:
     case GL_TEXTURE_3D:
         m_active_texture_unit->set_texture_3d_enabled(false);
         m_active_texture_unit->set_texture_3d_enabled(false);
         m_sampler_config_is_dirty = true;
         m_sampler_config_is_dirty = true;
+        m_texture_units_dirty = true;
         break;
         break;
     case GL_TEXTURE_CUBE_MAP:
     case GL_TEXTURE_CUBE_MAP:
         m_active_texture_unit->set_texture_cube_map_enabled(false);
         m_active_texture_unit->set_texture_cube_map_enabled(false);
         m_sampler_config_is_dirty = true;
         m_sampler_config_is_dirty = true;
+        m_texture_units_dirty = true;
         break;
         break;
     case GL_TEXTURE_GEN_Q:
     case GL_TEXTURE_GEN_Q:
     case GL_TEXTURE_GEN_R:
     case GL_TEXTURE_GEN_R:
     case GL_TEXTURE_GEN_S:
     case GL_TEXTURE_GEN_S:
     case GL_TEXTURE_GEN_T:
     case GL_TEXTURE_GEN_T:
         texture_coordinate_generation(m_active_texture_unit_index, capability).enabled = false;
         texture_coordinate_generation(m_active_texture_unit_index, capability).enabled = false;
-        m_texcoord_generation_dirty = true;
+        m_texture_units_dirty = true;
         break;
         break;
     default:
     default:
         dbgln_if(GL_DEBUG, "gl_disable({:#x}): unknown parameter", capability);
         dbgln_if(GL_DEBUG, "gl_disable({:#x}): unknown parameter", capability);
@@ -426,25 +430,29 @@ void GLContext::gl_enable(GLenum capability)
     case GL_TEXTURE_1D:
     case GL_TEXTURE_1D:
         m_active_texture_unit->set_texture_1d_enabled(true);
         m_active_texture_unit->set_texture_1d_enabled(true);
         m_sampler_config_is_dirty = true;
         m_sampler_config_is_dirty = true;
+        m_texture_units_dirty = true;
         break;
         break;
     case GL_TEXTURE_2D:
     case GL_TEXTURE_2D:
         m_active_texture_unit->set_texture_2d_enabled(true);
         m_active_texture_unit->set_texture_2d_enabled(true);
         m_sampler_config_is_dirty = true;
         m_sampler_config_is_dirty = true;
+        m_texture_units_dirty = true;
         break;
         break;
     case GL_TEXTURE_3D:
     case GL_TEXTURE_3D:
         m_active_texture_unit->set_texture_3d_enabled(true);
         m_active_texture_unit->set_texture_3d_enabled(true);
         m_sampler_config_is_dirty = true;
         m_sampler_config_is_dirty = true;
+        m_texture_units_dirty = true;
         break;
         break;
     case GL_TEXTURE_CUBE_MAP:
     case GL_TEXTURE_CUBE_MAP:
         m_active_texture_unit->set_texture_cube_map_enabled(true);
         m_active_texture_unit->set_texture_cube_map_enabled(true);
         m_sampler_config_is_dirty = true;
         m_sampler_config_is_dirty = true;
+        m_texture_units_dirty = true;
         break;
         break;
     case GL_TEXTURE_GEN_Q:
     case GL_TEXTURE_GEN_Q:
     case GL_TEXTURE_GEN_R:
     case GL_TEXTURE_GEN_R:
     case GL_TEXTURE_GEN_S:
     case GL_TEXTURE_GEN_S:
     case GL_TEXTURE_GEN_T:
     case GL_TEXTURE_GEN_T:
         texture_coordinate_generation(m_active_texture_unit_index, capability).enabled = true;
         texture_coordinate_generation(m_active_texture_unit_index, capability).enabled = true;
-        m_texcoord_generation_dirty = true;
+        m_texture_units_dirty = true;
         break;
         break;
     default:
     default:
         dbgln_if(GL_DEBUG, "gl_enable({:#x}): unknown parameter", capability);
         dbgln_if(GL_DEBUG, "gl_enable({:#x}): unknown parameter", capability);
@@ -522,10 +530,10 @@ void GLContext::get_floating_point(GLenum pname, T* params)
     };
     };
     switch (pname) {
     switch (pname) {
     case GL_MODELVIEW_MATRIX:
     case GL_MODELVIEW_MATRIX:
-        flatten_and_assign_matrix(m_model_view_matrix);
+        flatten_and_assign_matrix(model_view_matrix());
         return;
         return;
     case GL_PROJECTION_MATRIX:
     case GL_PROJECTION_MATRIX:
-        flatten_and_assign_matrix(m_projection_matrix);
+        flatten_and_assign_matrix(projection_matrix());
         return;
         return;
     }
     }
 
 

+ 11 - 17
Userland/Libraries/LibGL/GLContext.cpp

@@ -59,14 +59,14 @@ GLContext::GLContext(RefPtr<GPU::Driver> driver, NonnullOwnPtr<GPU::Device> devi
     // coordinate generation config.
     // coordinate generation config.
     m_texture_coordinate_generation.resize(m_device_info.num_texture_units);
     m_texture_coordinate_generation.resize(m_device_info.num_texture_units);
     for (auto& texture_coordinate_generation : m_texture_coordinate_generation) {
     for (auto& texture_coordinate_generation : m_texture_coordinate_generation) {
-        texture_coordinate_generation[0].object_plane_coefficients = { 1.0f, 0.0f, 0.0f, 0.0f };
-        texture_coordinate_generation[0].eye_plane_coefficients = { 1.0f, 0.0f, 0.0f, 0.0f };
-        texture_coordinate_generation[1].object_plane_coefficients = { 0.0f, 1.0f, 0.0f, 0.0f };
-        texture_coordinate_generation[1].eye_plane_coefficients = { 0.0f, 1.0f, 0.0f, 0.0f };
-        texture_coordinate_generation[2].object_plane_coefficients = { 0.0f, 0.0f, 0.0f, 0.0f };
-        texture_coordinate_generation[2].eye_plane_coefficients = { 0.0f, 0.0f, 0.0f, 0.0f };
-        texture_coordinate_generation[3].object_plane_coefficients = { 0.0f, 0.0f, 0.0f, 0.0f };
-        texture_coordinate_generation[3].eye_plane_coefficients = { 0.0f, 0.0f, 0.0f, 0.0f };
+        texture_coordinate_generation[0].object_plane_coefficients = { 1.f, 0.f, 0.f, 0.f };
+        texture_coordinate_generation[0].eye_plane_coefficients = { 1.f, 0.f, 0.f, 0.f };
+        texture_coordinate_generation[1].object_plane_coefficients = { 0.f, 1.f, 0.f, 0.f };
+        texture_coordinate_generation[1].eye_plane_coefficients = { 0.f, 1.f, 0.f, 0.f };
+        texture_coordinate_generation[2].object_plane_coefficients = { 0.f, 0.f, 0.f, 0.f };
+        texture_coordinate_generation[2].eye_plane_coefficients = { 0.f, 0.f, 0.f, 0.f };
+        texture_coordinate_generation[3].object_plane_coefficients = { 0.f, 0.f, 0.f, 0.f };
+        texture_coordinate_generation[3].eye_plane_coefficients = { 0.f, 0.f, 0.f, 0.f };
     }
     }
 
 
     build_extension_string();
     build_extension_string();
@@ -134,12 +134,6 @@ void GLContext::gl_end()
     RETURN_WITH_ERROR_IF(!m_in_draw_state, GL_INVALID_OPERATION);
     RETURN_WITH_ERROR_IF(!m_in_draw_state, GL_INVALID_OPERATION);
     m_in_draw_state = false;
     m_in_draw_state = false;
 
 
-    Vector<size_t, 32> enabled_texture_units;
-    for (size_t i = 0; i < m_texture_units.size(); ++i) {
-        if (m_texture_units[i].texture_2d_enabled())
-            enabled_texture_units.append(i);
-    }
-
     sync_device_config();
     sync_device_config();
 
 
     GPU::PrimitiveType primitive_type;
     GPU::PrimitiveType primitive_type;
@@ -174,7 +168,7 @@ void GLContext::gl_end()
         VERIFY_NOT_REACHED();
         VERIFY_NOT_REACHED();
     }
     }
 
 
-    m_rasterizer->draw_primitives(primitive_type, m_model_view_matrix, m_projection_matrix, m_texture_matrix, m_vertex_list, enabled_texture_units);
+    m_rasterizer->draw_primitives(primitive_type, model_view_matrix(), projection_matrix(), m_vertex_list);
     m_vertex_list.clear_with_capacity();
     m_vertex_list.clear_with_capacity();
 }
 }
 
 
@@ -832,7 +826,7 @@ void GLContext::gl_raster_pos(GLfloat x, GLfloat y, GLfloat z, GLfloat w)
     APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_raster_pos, x, y, z, w);
     APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_raster_pos, x, y, z, w);
     RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
     RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
 
 
-    m_rasterizer->set_raster_position({ x, y, z, w }, m_model_view_matrix, m_projection_matrix);
+    m_rasterizer->set_raster_position({ x, y, z, w }, model_view_matrix(), projection_matrix());
 }
 }
 
 
 void GLContext::gl_line_width(GLfloat width)
 void GLContext::gl_line_width(GLfloat width)
@@ -913,7 +907,7 @@ void GLContext::present()
 void GLContext::sync_device_config()
 void GLContext::sync_device_config()
 {
 {
     sync_device_sampler_config();
     sync_device_sampler_config();
-    sync_device_texcoord_config();
+    sync_device_texture_units();
     sync_light_state();
     sync_light_state();
     sync_stencil_configuration();
     sync_stencil_configuration();
     sync_clip_planes();
     sync_clip_planes();

+ 17 - 12
Userland/Libraries/LibGL/GLContext.h

@@ -217,7 +217,7 @@ public:
 private:
 private:
     void sync_device_config();
     void sync_device_config();
     void sync_device_sampler_config();
     void sync_device_sampler_config();
-    void sync_device_texcoord_config();
+    void sync_device_texture_units();
     void sync_light_state();
     void sync_light_state();
     void sync_stencil_configuration();
     void sync_stencil_configuration();
     void sync_clip_planes();
     void sync_clip_planes();
@@ -256,16 +256,22 @@ private:
 
 
     GLenum m_current_draw_mode;
     GLenum m_current_draw_mode;
     GLenum m_current_matrix_mode { GL_MODELVIEW };
     GLenum m_current_matrix_mode { GL_MODELVIEW };
-    FloatMatrix4x4 m_projection_matrix { FloatMatrix4x4::identity() };
-    FloatMatrix4x4 m_model_view_matrix { FloatMatrix4x4::identity() };
-    FloatMatrix4x4 m_texture_matrix { FloatMatrix4x4::identity() };
-    FloatMatrix4x4* m_current_matrix { &m_model_view_matrix };
-
-    Vector<FloatMatrix4x4> m_projection_matrix_stack;
-    Vector<FloatMatrix4x4> m_model_view_matrix_stack;
-    // FIXME: implement multi-texturing: the texture matrix stack should live inside a texture unit
-    Vector<FloatMatrix4x4> m_texture_matrix_stack;
+
+    FloatMatrix4x4& projection_matrix() { return m_projection_matrix_stack.last(); }
+    FloatMatrix4x4& model_view_matrix() { return m_model_view_matrix_stack.last(); }
+
+    Vector<FloatMatrix4x4> m_projection_matrix_stack { FloatMatrix4x4::identity() };
+    Vector<FloatMatrix4x4> m_model_view_matrix_stack { FloatMatrix4x4::identity() };
     Vector<FloatMatrix4x4>* m_current_matrix_stack { &m_model_view_matrix_stack };
     Vector<FloatMatrix4x4>* m_current_matrix_stack { &m_model_view_matrix_stack };
+    FloatMatrix4x4* m_current_matrix { &m_current_matrix_stack->last() };
+
+    ALWAYS_INLINE void update_current_matrix(FloatMatrix4x4 const& new_matrix)
+    {
+        *m_current_matrix = new_matrix;
+
+        if (m_current_matrix_mode == GL_TEXTURE)
+            m_texture_units_dirty = true;
+    }
 
 
     Gfx::IntRect m_viewport;
     Gfx::IntRect m_viewport;
 
 
@@ -353,6 +359,7 @@ private:
     Vector<TextureUnit> m_texture_units;
     Vector<TextureUnit> m_texture_units;
     TextureUnit* m_active_texture_unit;
     TextureUnit* m_active_texture_unit;
     size_t m_active_texture_unit_index { 0 };
     size_t m_active_texture_unit_index { 0 };
+    bool m_texture_units_dirty { true };
 
 
     // Texture coordinate generation state
     // Texture coordinate generation state
     struct TextureCoordinateGeneration {
     struct TextureCoordinateGeneration {
@@ -362,8 +369,6 @@ private:
         FloatVector4 eye_plane_coefficients { 0.0f, 0.0f, 0.0f, 0.0f };
         FloatVector4 eye_plane_coefficients { 0.0f, 0.0f, 0.0f, 0.0f };
     };
     };
     Vector<Array<TextureCoordinateGeneration, 4>> m_texture_coordinate_generation;
     Vector<Array<TextureCoordinateGeneration, 4>> m_texture_coordinate_generation;
-    bool m_texcoord_generation_dirty { true };
-
     ALWAYS_INLINE TextureCoordinateGeneration& texture_coordinate_generation(size_t texture_unit, GLenum capability)
     ALWAYS_INLINE TextureCoordinateGeneration& texture_coordinate_generation(size_t texture_unit, GLenum capability)
     {
     {
         return m_texture_coordinate_generation[texture_unit][capability - GL_TEXTURE_GEN_S];
         return m_texture_coordinate_generation[texture_unit][capability - GL_TEXTURE_GEN_S];

+ 4 - 4
Userland/Libraries/LibGL/Lighting.cpp

@@ -249,7 +249,7 @@ void GLContext::gl_lightfv(GLenum light, GLenum pname, GLfloat const* params)
         break;
         break;
     case GL_POSITION:
     case GL_POSITION:
         light_state.position = { params[0], params[1], params[2], params[3] };
         light_state.position = { params[0], params[1], params[2], params[3] };
-        light_state.position = m_model_view_matrix * light_state.position;
+        light_state.position = model_view_matrix() * light_state.position;
         break;
         break;
     case GL_CONSTANT_ATTENUATION:
     case GL_CONSTANT_ATTENUATION:
         RETURN_WITH_ERROR_IF(params[0] < 0.f, GL_INVALID_VALUE);
         RETURN_WITH_ERROR_IF(params[0] < 0.f, GL_INVALID_VALUE);
@@ -277,7 +277,7 @@ void GLContext::gl_lightfv(GLenum light, GLenum pname, GLfloat const* params)
     }
     }
     case GL_SPOT_DIRECTION: {
     case GL_SPOT_DIRECTION: {
         FloatVector4 direction_vector = { params[0], params[1], params[2], 0.f };
         FloatVector4 direction_vector = { params[0], params[1], params[2], 0.f };
-        direction_vector = m_model_view_matrix * direction_vector;
+        direction_vector = model_view_matrix() * direction_vector;
         light_state.spotlight_direction = direction_vector.xyz();
         light_state.spotlight_direction = direction_vector.xyz();
         break;
         break;
     }
     }
@@ -313,7 +313,7 @@ void GLContext::gl_lightiv(GLenum light, GLenum pname, GLint const* params)
         break;
         break;
     case GL_POSITION:
     case GL_POSITION:
         light_state.position = to_float_vector(params[0], params[1], params[2], params[3]);
         light_state.position = to_float_vector(params[0], params[1], params[2], params[3]);
-        light_state.position = m_model_view_matrix * light_state.position;
+        light_state.position = model_view_matrix() * light_state.position;
         break;
         break;
     case GL_CONSTANT_ATTENUATION:
     case GL_CONSTANT_ATTENUATION:
         RETURN_WITH_ERROR_IF(params[0] < 0, GL_INVALID_VALUE);
         RETURN_WITH_ERROR_IF(params[0] < 0, GL_INVALID_VALUE);
@@ -341,7 +341,7 @@ void GLContext::gl_lightiv(GLenum light, GLenum pname, GLint const* params)
     }
     }
     case GL_SPOT_DIRECTION: {
     case GL_SPOT_DIRECTION: {
         auto direction_vector = to_float_vector(params[0], params[1], params[2], 0.0f);
         auto direction_vector = to_float_vector(params[0], params[1], params[2], 0.0f);
-        direction_vector = m_model_view_matrix * direction_vector;
+        direction_vector = model_view_matrix() * direction_vector;
         light_state.spotlight_direction = direction_vector.xyz();
         light_state.spotlight_direction = direction_vector.xyz();
         break;
         break;
     }
     }

+ 16 - 16
Userland/Libraries/LibGL/Matrix.cpp

@@ -45,7 +45,7 @@ void GLContext::gl_frustum(GLdouble left, GLdouble right, GLdouble bottom, GLdou
         0, 0, c, d,
         0, 0, c, d,
         0, 0, -1, 0
         0, 0, -1, 0
     };
     };
-    *m_current_matrix = *m_current_matrix * frustum;
+    update_current_matrix(*m_current_matrix * frustum);
 }
 }
 
 
 void GLContext::gl_load_identity()
 void GLContext::gl_load_identity()
@@ -53,7 +53,7 @@ void GLContext::gl_load_identity()
     APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_load_identity);
     APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_load_identity);
     RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
     RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
 
 
-    *m_current_matrix = FloatMatrix4x4::identity();
+    update_current_matrix(FloatMatrix4x4::identity());
 }
 }
 
 
 void GLContext::gl_load_matrix(FloatMatrix4x4 const& matrix)
 void GLContext::gl_load_matrix(FloatMatrix4x4 const& matrix)
@@ -61,7 +61,7 @@ void GLContext::gl_load_matrix(FloatMatrix4x4 const& matrix)
     APPEND_TO_CALL_LIST_WITH_ARG_AND_RETURN_IF_NEEDED(gl_load_matrix, matrix);
     APPEND_TO_CALL_LIST_WITH_ARG_AND_RETURN_IF_NEEDED(gl_load_matrix, matrix);
     RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
     RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
 
 
-    *m_current_matrix = matrix;
+    update_current_matrix(matrix);
 }
 }
 
 
 void GLContext::gl_matrix_mode(GLenum mode)
 void GLContext::gl_matrix_mode(GLenum mode)
@@ -73,20 +73,18 @@ void GLContext::gl_matrix_mode(GLenum mode)
     m_current_matrix_mode = mode;
     m_current_matrix_mode = mode;
     switch (mode) {
     switch (mode) {
     case GL_MODELVIEW:
     case GL_MODELVIEW:
-        m_current_matrix = &m_model_view_matrix;
         m_current_matrix_stack = &m_model_view_matrix_stack;
         m_current_matrix_stack = &m_model_view_matrix_stack;
         break;
         break;
     case GL_PROJECTION:
     case GL_PROJECTION:
-        m_current_matrix = &m_projection_matrix;
         m_current_matrix_stack = &m_projection_matrix_stack;
         m_current_matrix_stack = &m_projection_matrix_stack;
         break;
         break;
     case GL_TEXTURE:
     case GL_TEXTURE:
-        m_current_matrix = &m_texture_matrix;
-        m_current_matrix_stack = &m_texture_matrix_stack;
+        m_current_matrix_stack = &m_active_texture_unit->texture_matrix_stack();
         break;
         break;
     default:
     default:
         VERIFY_NOT_REACHED();
         VERIFY_NOT_REACHED();
     }
     }
+    m_current_matrix = &m_current_matrix_stack->last();
 }
 }
 
 
 void GLContext::gl_mult_matrix(FloatMatrix4x4 const& matrix)
 void GLContext::gl_mult_matrix(FloatMatrix4x4 const& matrix)
@@ -94,7 +92,7 @@ void GLContext::gl_mult_matrix(FloatMatrix4x4 const& matrix)
     APPEND_TO_CALL_LIST_WITH_ARG_AND_RETURN_IF_NEEDED(gl_mult_matrix, matrix);
     APPEND_TO_CALL_LIST_WITH_ARG_AND_RETURN_IF_NEEDED(gl_mult_matrix, matrix);
     RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
     RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
 
 
-    *m_current_matrix = *m_current_matrix * matrix;
+    update_current_matrix(*m_current_matrix * matrix);
 }
 }
 
 
 void GLContext::gl_ortho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near_val, GLdouble far_val)
 void GLContext::gl_ortho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near_val, GLdouble far_val)
@@ -117,25 +115,27 @@ void GLContext::gl_ortho(GLdouble left, GLdouble right, GLdouble bottom, GLdoubl
         0, 0, static_cast<float>(-2 / fn), static_cast<float>(tz),
         0, 0, static_cast<float>(-2 / fn), static_cast<float>(tz),
         0, 0, 0, 1
         0, 0, 0, 1
     };
     };
-    *m_current_matrix = *m_current_matrix * projection;
+    update_current_matrix(*m_current_matrix * projection);
 }
 }
 
 
 void GLContext::gl_pop_matrix()
 void GLContext::gl_pop_matrix()
 {
 {
     APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_pop_matrix);
     APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_pop_matrix);
     RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
     RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
-    RETURN_WITH_ERROR_IF((*m_current_matrix_stack).is_empty(), GL_STACK_UNDERFLOW);
+    RETURN_WITH_ERROR_IF(m_current_matrix_stack->size() <= 1, GL_STACK_UNDERFLOW);
 
 
-    *m_current_matrix = (*m_current_matrix_stack).take_last();
+    m_current_matrix_stack->take_last();
+    m_current_matrix = &m_current_matrix_stack->last();
 }
 }
 
 
 void GLContext::gl_push_matrix()
 void GLContext::gl_push_matrix()
 {
 {
     APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_push_matrix);
     APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_push_matrix);
     RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
     RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
-    RETURN_WITH_ERROR_IF((*m_current_matrix_stack).size() >= matrix_stack_limit(m_current_matrix_mode), GL_STACK_OVERFLOW);
+    RETURN_WITH_ERROR_IF(m_current_matrix_stack->size() >= matrix_stack_limit(m_current_matrix_mode), GL_STACK_OVERFLOW);
 
 
-    (*m_current_matrix_stack).append(*m_current_matrix);
+    m_current_matrix_stack->append(*m_current_matrix);
+    m_current_matrix = &m_current_matrix_stack->last();
 }
 }
 
 
 void GLContext::gl_rotate(GLfloat angle, GLfloat x, GLfloat y, GLfloat z)
 void GLContext::gl_rotate(GLfloat angle, GLfloat x, GLfloat y, GLfloat z)
@@ -147,7 +147,7 @@ void GLContext::gl_rotate(GLfloat angle, GLfloat x, GLfloat y, GLfloat z)
     if (axis.length() > 0.f)
     if (axis.length() > 0.f)
         axis.normalize();
         axis.normalize();
     auto rotation_mat = Gfx::rotation_matrix(axis, angle * static_cast<float>(M_PI * 2 / 360));
     auto rotation_mat = Gfx::rotation_matrix(axis, angle * static_cast<float>(M_PI * 2 / 360));
-    *m_current_matrix = *m_current_matrix * rotation_mat;
+    update_current_matrix(*m_current_matrix * rotation_mat);
 }
 }
 
 
 void GLContext::gl_scale(GLdouble x, GLdouble y, GLdouble z)
 void GLContext::gl_scale(GLdouble x, GLdouble y, GLdouble z)
@@ -156,7 +156,7 @@ void GLContext::gl_scale(GLdouble x, GLdouble y, GLdouble z)
     RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
     RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
 
 
     auto scale_matrix = Gfx::scale_matrix(FloatVector3 { static_cast<float>(x), static_cast<float>(y), static_cast<float>(z) });
     auto scale_matrix = Gfx::scale_matrix(FloatVector3 { static_cast<float>(x), static_cast<float>(y), static_cast<float>(z) });
-    *m_current_matrix = *m_current_matrix * scale_matrix;
+    update_current_matrix(*m_current_matrix * scale_matrix);
 }
 }
 
 
 void GLContext::gl_translate(GLdouble x, GLdouble y, GLdouble z)
 void GLContext::gl_translate(GLdouble x, GLdouble y, GLdouble z)
@@ -165,7 +165,7 @@ void GLContext::gl_translate(GLdouble x, GLdouble y, GLdouble z)
     RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
     RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
 
 
     auto translation_matrix = Gfx::translation_matrix(FloatVector3 { static_cast<float>(x), static_cast<float>(y), static_cast<float>(z) });
     auto translation_matrix = Gfx::translation_matrix(FloatVector3 { static_cast<float>(x), static_cast<float>(y), static_cast<float>(z) });
-    *m_current_matrix = *m_current_matrix * translation_matrix;
+    update_current_matrix(*m_current_matrix * translation_matrix);
 }
 }
 
 
 }
 }

+ 8 - 0
Userland/Libraries/LibGL/Tex/TextureUnit.h

@@ -8,7 +8,9 @@
 #pragma once
 #pragma once
 
 
 #include <AK/RefPtr.h>
 #include <AK/RefPtr.h>
+#include <AK/Vector.h>
 #include <LibGL/Tex/Texture2D.h>
 #include <LibGL/Tex/Texture2D.h>
+#include <LibGfx/Matrix4x4.h>
 
 
 namespace GL {
 namespace GL {
 
 
@@ -49,6 +51,9 @@ public:
     bool texture_cube_map_enabled() const { return m_texture_cube_map_enabled; };
     bool texture_cube_map_enabled() const { return m_texture_cube_map_enabled; };
     void set_texture_cube_map_enabled(bool texture_cube_map_enabled) { m_texture_cube_map_enabled = texture_cube_map_enabled; }
     void set_texture_cube_map_enabled(bool texture_cube_map_enabled) { m_texture_cube_map_enabled = texture_cube_map_enabled; }
 
 
+    FloatMatrix4x4& texture_matrix() { return m_texture_matrix_stack.last(); }
+    Vector<FloatMatrix4x4>& texture_matrix_stack() { return m_texture_matrix_stack; }
+
 private:
 private:
     GLenum m_alpha_combinator { GL_MODULATE };
     GLenum m_alpha_combinator { GL_MODULATE };
     Array<GLenum, 3> m_alpha_operand { GL_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA };
     Array<GLenum, 3> m_alpha_operand { GL_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA };
@@ -69,6 +74,9 @@ private:
     bool m_texture_2d_enabled { false };
     bool m_texture_2d_enabled { false };
     bool m_texture_3d_enabled { false };
     bool m_texture_3d_enabled { false };
     bool m_texture_cube_map_enabled { false };
     bool m_texture_cube_map_enabled { false };
+
+    // Matrix stack for this unit
+    Vector<FloatMatrix4x4> m_texture_matrix_stack { FloatMatrix4x4::identity() };
 };
 };
 
 
 }
 }

+ 33 - 24
Userland/Libraries/LibGL/Texture.cpp

@@ -19,6 +19,11 @@ void GLContext::gl_active_texture(GLenum texture)
 
 
     m_active_texture_unit_index = texture - GL_TEXTURE0;
     m_active_texture_unit_index = texture - GL_TEXTURE0;
     m_active_texture_unit = &m_texture_units.at(m_active_texture_unit_index);
     m_active_texture_unit = &m_texture_units.at(m_active_texture_unit_index);
+
+    if (m_current_matrix_mode == GL_TEXTURE) {
+        m_current_matrix_stack = &m_active_texture_unit->texture_matrix_stack();
+        m_current_matrix = &m_current_matrix_stack->last();
+    }
 }
 }
 
 
 void GLContext::gl_bind_texture(GLenum target, GLuint texture)
 void GLContext::gl_bind_texture(GLenum target, GLuint texture)
@@ -471,7 +476,7 @@ void GLContext::gl_tex_gen(GLenum coord, GLenum pname, GLint param)
 
 
     GLenum const capability = GL_TEXTURE_GEN_S + (coord - GL_S);
     GLenum const capability = GL_TEXTURE_GEN_S + (coord - GL_S);
     texture_coordinate_generation(m_active_texture_unit_index, capability).generation_mode = param;
     texture_coordinate_generation(m_active_texture_unit_index, capability).generation_mode = param;
-    m_texcoord_generation_dirty = true;
+    m_texture_units_dirty = true;
 }
 }
 
 
 void GLContext::gl_tex_gen_floatv(GLenum coord, GLenum pname, GLfloat const* params)
 void GLContext::gl_tex_gen_floatv(GLenum coord, GLenum pname, GLfloat const* params)
@@ -506,7 +511,7 @@ void GLContext::gl_tex_gen_floatv(GLenum coord, GLenum pname, GLfloat const* par
         texture_coordinate_generation(m_active_texture_unit_index, capability).object_plane_coefficients = { params[0], params[1], params[2], params[3] };
         texture_coordinate_generation(m_active_texture_unit_index, capability).object_plane_coefficients = { params[0], params[1], params[2], params[3] };
         break;
         break;
     case GL_EYE_PLANE: {
     case GL_EYE_PLANE: {
-        auto const& inverse_model_view = m_model_view_matrix.inverse();
+        auto const& inverse_model_view = model_view_matrix().inverse();
         auto input_coefficients = FloatVector4 { params[0], params[1], params[2], params[3] };
         auto input_coefficients = FloatVector4 { params[0], params[1], params[2], params[3] };
 
 
         // Note: we are allowed to store transformed coefficients here, according to the documentation on
         // Note: we are allowed to store transformed coefficients here, according to the documentation on
@@ -522,7 +527,7 @@ void GLContext::gl_tex_gen_floatv(GLenum coord, GLenum pname, GLfloat const* par
         VERIFY_NOT_REACHED();
         VERIFY_NOT_REACHED();
     }
     }
 
 
-    m_texcoord_generation_dirty = true;
+    m_texture_units_dirty = true;
 }
 }
 
 
 void GLContext::gl_tex_image_2d(GLenum target, GLint level, GLint internal_format, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, GLvoid const* data)
 void GLContext::gl_tex_image_2d(GLenum target, GLint level, GLint internal_format, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, GLvoid const* data)
@@ -909,39 +914,41 @@ void GLContext::sync_device_sampler_config()
     }
     }
 }
 }
 
 
-void GLContext::sync_device_texcoord_config()
+void GLContext::sync_device_texture_units()
 {
 {
-    if (!m_texcoord_generation_dirty)
+    if (!m_texture_units_dirty)
         return;
         return;
-    m_texcoord_generation_dirty = false;
+    m_texture_units_dirty = false;
 
 
-    auto options = m_rasterizer->options();
-
-    for (size_t i = 0; i < m_device_info.num_texture_units; ++i) {
+    for (GPU::TextureUnitIndex i = 0; i < m_device_info.num_texture_units; ++i) {
+        GPU::TextureUnitConfiguration texture_unit_configuration;
+        texture_unit_configuration.enabled = m_texture_units[i].texture_2d_enabled();
+        texture_unit_configuration.transformation_matrix = m_texture_units[i].texture_matrix();
 
 
+        // Tex coord generation
         u8 enabled_coordinates = GPU::TexCoordGenerationCoordinate::None;
         u8 enabled_coordinates = GPU::TexCoordGenerationCoordinate::None;
         for (GLenum capability = GL_TEXTURE_GEN_S; capability <= GL_TEXTURE_GEN_Q; ++capability) {
         for (GLenum capability = GL_TEXTURE_GEN_S; capability <= GL_TEXTURE_GEN_Q; ++capability) {
             auto const context_coordinate_config = texture_coordinate_generation(i, capability);
             auto const context_coordinate_config = texture_coordinate_generation(i, capability);
             if (!context_coordinate_config.enabled)
             if (!context_coordinate_config.enabled)
                 continue;
                 continue;
 
 
-            GPU::TexCoordGenerationConfig* texcoord_generation_config;
+            GPU::TexCoordGeneration* texcoord_generation;
             switch (capability) {
             switch (capability) {
             case GL_TEXTURE_GEN_S:
             case GL_TEXTURE_GEN_S:
                 enabled_coordinates |= GPU::TexCoordGenerationCoordinate::S;
                 enabled_coordinates |= GPU::TexCoordGenerationCoordinate::S;
-                texcoord_generation_config = &options.texcoord_generation_config[i][0];
+                texcoord_generation = &texture_unit_configuration.tex_coord_generation[0];
                 break;
                 break;
             case GL_TEXTURE_GEN_T:
             case GL_TEXTURE_GEN_T:
                 enabled_coordinates |= GPU::TexCoordGenerationCoordinate::T;
                 enabled_coordinates |= GPU::TexCoordGenerationCoordinate::T;
-                texcoord_generation_config = &options.texcoord_generation_config[i][1];
+                texcoord_generation = &texture_unit_configuration.tex_coord_generation[1];
                 break;
                 break;
             case GL_TEXTURE_GEN_R:
             case GL_TEXTURE_GEN_R:
                 enabled_coordinates |= GPU::TexCoordGenerationCoordinate::R;
                 enabled_coordinates |= GPU::TexCoordGenerationCoordinate::R;
-                texcoord_generation_config = &options.texcoord_generation_config[i][2];
+                texcoord_generation = &texture_unit_configuration.tex_coord_generation[2];
                 break;
                 break;
             case GL_TEXTURE_GEN_Q:
             case GL_TEXTURE_GEN_Q:
                 enabled_coordinates |= GPU::TexCoordGenerationCoordinate::Q;
                 enabled_coordinates |= GPU::TexCoordGenerationCoordinate::Q;
-                texcoord_generation_config = &options.texcoord_generation_config[i][3];
+                texcoord_generation = &texture_unit_configuration.tex_coord_generation[3];
                 break;
                 break;
             default:
             default:
                 VERIFY_NOT_REACHED();
                 VERIFY_NOT_REACHED();
@@ -949,28 +956,30 @@ void GLContext::sync_device_texcoord_config()
 
 
             switch (context_coordinate_config.generation_mode) {
             switch (context_coordinate_config.generation_mode) {
             case GL_OBJECT_LINEAR:
             case GL_OBJECT_LINEAR:
-                texcoord_generation_config->mode = GPU::TexCoordGenerationMode::ObjectLinear;
-                texcoord_generation_config->coefficients = context_coordinate_config.object_plane_coefficients;
+                texcoord_generation->mode = GPU::TexCoordGenerationMode::ObjectLinear;
+                texcoord_generation->coefficients = context_coordinate_config.object_plane_coefficients;
                 break;
                 break;
             case GL_EYE_LINEAR:
             case GL_EYE_LINEAR:
-                texcoord_generation_config->mode = GPU::TexCoordGenerationMode::EyeLinear;
-                texcoord_generation_config->coefficients = context_coordinate_config.eye_plane_coefficients;
+                texcoord_generation->mode = GPU::TexCoordGenerationMode::EyeLinear;
+                texcoord_generation->coefficients = context_coordinate_config.eye_plane_coefficients;
                 break;
                 break;
             case GL_SPHERE_MAP:
             case GL_SPHERE_MAP:
-                texcoord_generation_config->mode = GPU::TexCoordGenerationMode::SphereMap;
+                texcoord_generation->mode = GPU::TexCoordGenerationMode::SphereMap;
                 break;
                 break;
             case GL_REFLECTION_MAP:
             case GL_REFLECTION_MAP:
-                texcoord_generation_config->mode = GPU::TexCoordGenerationMode::ReflectionMap;
+                texcoord_generation->mode = GPU::TexCoordGenerationMode::ReflectionMap;
                 break;
                 break;
             case GL_NORMAL_MAP:
             case GL_NORMAL_MAP:
-                texcoord_generation_config->mode = GPU::TexCoordGenerationMode::NormalMap;
+                texcoord_generation->mode = GPU::TexCoordGenerationMode::NormalMap;
                 break;
                 break;
+            default:
+                VERIFY_NOT_REACHED();
             }
             }
         }
         }
-        options.texcoord_generation_enabled_coordinates[i] = enabled_coordinates;
-    }
+        texture_unit_configuration.tex_coord_generation_enabled = enabled_coordinates;
 
 
-    m_rasterizer->set_options(options);
+        m_rasterizer->set_texture_unit_configuration(i, texture_unit_configuration);
+    }
 }
 }
 
 
 }
 }

+ 1 - 1
Userland/Libraries/LibGPU/Config.h

@@ -18,6 +18,6 @@ using StencilType = u8;
 
 
 // FIXME: This constant was originally introduced in LibSoftGPU and is currently used in the Vertex struct.
 // FIXME: This constant was originally introduced in LibSoftGPU and is currently used in the Vertex struct.
 //        Once we refactor the interface this should move back into LibSoftGPU.
 //        Once we refactor the interface this should move back into LibSoftGPU.
-static constexpr int NUM_SAMPLERS = 2;
+static constexpr int NUM_TEXTURE_UNITS = 2;
 
 
 }
 }

+ 3 - 2
Userland/Libraries/LibGPU/Device.h

@@ -21,7 +21,7 @@
 #include <LibGPU/RasterizerOptions.h>
 #include <LibGPU/RasterizerOptions.h>
 #include <LibGPU/SamplerConfig.h>
 #include <LibGPU/SamplerConfig.h>
 #include <LibGPU/StencilConfiguration.h>
 #include <LibGPU/StencilConfiguration.h>
-#include <LibGPU/TexCoordGenerationConfig.h>
+#include <LibGPU/TextureUnitConfiguration.h>
 #include <LibGPU/Vertex.h>
 #include <LibGPU/Vertex.h>
 #include <LibGfx/Bitmap.h>
 #include <LibGfx/Bitmap.h>
 #include <LibGfx/Matrix3x3.h>
 #include <LibGfx/Matrix3x3.h>
@@ -38,7 +38,7 @@ public:
 
 
     virtual DeviceInfo info() const = 0;
     virtual DeviceInfo info() const = 0;
 
 
-    virtual void draw_primitives(PrimitiveType, FloatMatrix4x4 const& model_view_transform, FloatMatrix4x4 const& projection_transform, FloatMatrix4x4 const& texture_transform, Vector<Vertex>& vertices, Vector<size_t> const& enabled_texture_units) = 0;
+    virtual void draw_primitives(PrimitiveType, FloatMatrix4x4 const& model_view_transform, FloatMatrix4x4 const& projection_transform, Vector<Vertex>& vertices) = 0;
     virtual void resize(Gfx::IntSize const& min_size) = 0;
     virtual void resize(Gfx::IntSize const& min_size) = 0;
     virtual void clear_color(FloatVector4 const&) = 0;
     virtual void clear_color(FloatVector4 const&) = 0;
     virtual void clear_depth(DepthType) = 0;
     virtual void clear_depth(DepthType) = 0;
@@ -61,6 +61,7 @@ public:
     virtual void set_light_state(unsigned, Light const&) = 0;
     virtual void set_light_state(unsigned, Light const&) = 0;
     virtual void set_material_state(Face, Material const&) = 0;
     virtual void set_material_state(Face, Material const&) = 0;
     virtual void set_stencil_configuration(Face, StencilConfiguration const&) = 0;
     virtual void set_stencil_configuration(Face, StencilConfiguration const&) = 0;
+    virtual void set_texture_unit_configuration(TextureUnitIndex, TextureUnitConfiguration const&) = 0;
     virtual void set_clip_planes(Vector<FloatVector4> const&) = 0;
     virtual void set_clip_planes(Vector<FloatVector4> const&) = 0;
 
 
     virtual RasterPosition raster_position() const = 0;
     virtual RasterPosition raster_position() const = 0;

+ 1 - 1
Userland/Libraries/LibGPU/Enums.h

@@ -120,7 +120,7 @@ enum StencilTestFunction {
     NotEqual,
     NotEqual,
 };
 };
 
 
-enum TexCoordGenerationCoordinate {
+enum TexCoordGenerationCoordinate : u8 {
     None = 0x0,
     None = 0x0,
     S = 0x1,
     S = 0x1,
     T = 0x2,
     T = 0x2,

+ 0 - 3
Userland/Libraries/LibGPU/RasterizerOptions.h

@@ -10,7 +10,6 @@
 #include <AK/Array.h>
 #include <AK/Array.h>
 #include <LibGPU/Config.h>
 #include <LibGPU/Config.h>
 #include <LibGPU/Enums.h>
 #include <LibGPU/Enums.h>
-#include <LibGPU/TexCoordGenerationConfig.h>
 #include <LibGfx/Rect.h>
 #include <LibGfx/Rect.h>
 #include <LibGfx/Vector4.h>
 #include <LibGfx/Vector4.h>
 
 
@@ -53,8 +52,6 @@ struct RasterizerOptions {
     WindingOrder front_face { WindingOrder::CounterClockwise };
     WindingOrder front_face { WindingOrder::CounterClockwise };
     bool cull_back { true };
     bool cull_back { true };
     bool cull_front { false };
     bool cull_front { false };
-    Array<u8, NUM_SAMPLERS> texcoord_generation_enabled_coordinates {};
-    Array<Array<TexCoordGenerationConfig, 4>, NUM_SAMPLERS> texcoord_generation_config {};
     Gfx::IntRect viewport;
     Gfx::IntRect viewport;
     bool lighting_enabled { false };
     bool lighting_enabled { false };
     bool color_material_enabled { false };
     bool color_material_enabled { false };

+ 0 - 1
Userland/Libraries/LibGPU/StencilConfiguration.h

@@ -6,7 +6,6 @@
 
 
 #pragma once
 #pragma once
 
 
-#include <LibGPU/Config.h>
 #include <LibGPU/Enums.h>
 #include <LibGPU/Enums.h>
 
 
 namespace GPU {
 namespace GPU {

+ 0 - 19
Userland/Libraries/LibGPU/TexCoordGenerationConfig.h

@@ -1,19 +0,0 @@
-/*
- * Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
-
-#pragma once
-
-#include <LibGPU/Enums.h>
-#include <LibGfx/Vector4.h>
-
-namespace GPU {
-
-struct TexCoordGenerationConfig {
-    GPU::TexCoordGenerationMode mode { GPU::TexCoordGenerationMode::EyeLinear };
-    FloatVector4 coefficients {};
-};
-
-}

+ 30 - 0
Userland/Libraries/LibGPU/TextureUnitConfiguration.h

@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibGPU/Enums.h>
+#include <LibGfx/Matrix4x4.h>
+#include <LibGfx/Vector4.h>
+
+namespace GPU {
+
+typedef u8 TextureUnitIndex;
+
+struct TexCoordGeneration {
+    bool enabled;
+    TexCoordGenerationMode mode;
+    FloatVector4 coefficients;
+};
+
+struct TextureUnitConfiguration {
+    bool enabled { false };
+    FloatMatrix4x4 transformation_matrix { FloatMatrix4x4::identity() };
+    u8 tex_coord_generation_enabled;
+    TexCoordGeneration tex_coord_generation[4];
+};
+
+}

+ 1 - 1
Userland/Libraries/LibGPU/Vertex.h

@@ -20,7 +20,7 @@ struct Vertex {
     FloatVector4 clip_coordinates;
     FloatVector4 clip_coordinates;
     FloatVector4 window_coordinates;
     FloatVector4 window_coordinates;
     FloatVector4 color;
     FloatVector4 color;
-    Array<FloatVector4, GPU::NUM_SAMPLERS> tex_coords;
+    Array<FloatVector4, GPU::NUM_TEXTURE_UNITS> tex_coords;
     FloatVector3 normal;
     FloatVector3 normal;
 };
 };
 
 

+ 1 - 1
Userland/Libraries/LibSoftGPU/Clipper.cpp

@@ -61,7 +61,7 @@ static GPU::Vertex clip_intersection_point(GPU::Vertex const& p1, GPU::Vertex co
     out.eye_coordinates = mix(p1.eye_coordinates, p2.eye_coordinates, a);
     out.eye_coordinates = mix(p1.eye_coordinates, p2.eye_coordinates, a);
     out.clip_coordinates = mix(p1.clip_coordinates, p2.clip_coordinates, a);
     out.clip_coordinates = mix(p1.clip_coordinates, p2.clip_coordinates, a);
     out.color = mix(p1.color, p2.color, a);
     out.color = mix(p1.color, p2.color, a);
-    for (size_t i = 0; i < GPU::NUM_SAMPLERS; ++i)
+    for (size_t i = 0; i < GPU::NUM_TEXTURE_UNITS; ++i)
         out.tex_coords[i] = mix(p1.tex_coords[i], p2.tex_coords[i], a);
         out.tex_coords[i] = mix(p1.tex_coords[i], p2.tex_coords[i], a);
     out.normal = mix(p1.normal, p2.normal, a);
     out.normal = mix(p1.normal, p2.normal, a);
     return out;
     return out;

+ 40 - 41
Userland/Libraries/LibSoftGPU/Device.cpp

@@ -543,7 +543,7 @@ void Device::rasterize_line_antialiased(GPU::Vertex& from, GPU::Vertex& to)
             // FIXME: interpolate color, tex coords and fog depth along the distance of the line
             // FIXME: interpolate color, tex coords and fog depth along the distance of the line
             //        in clip space (i.e. NOT distance_from_line)
             //        in clip space (i.e. NOT distance_from_line)
             quad.vertex_color = from_color4;
             quad.vertex_color = from_color4;
-            for (size_t i = 0; i < GPU::NUM_SAMPLERS; ++i)
+            for (size_t i = 0; i < GPU::NUM_TEXTURE_UNITS; ++i)
                 quad.texture_coordinates[i] = expand4(from.tex_coords[i]);
                 quad.texture_coordinates[i] = expand4(from.tex_coords[i]);
             quad.fog_depth = from_fog_depth4;
             quad.fog_depth = from_fog_depth4;
         });
         });
@@ -590,7 +590,7 @@ void Device::rasterize_point_aliased(GPU::Vertex& point)
         },
         },
         [&point](auto& quad) {
         [&point](auto& quad) {
             quad.vertex_color = expand4(point.color);
             quad.vertex_color = expand4(point.color);
-            for (size_t i = 0; i < GPU::NUM_SAMPLERS; ++i)
+            for (size_t i = 0; i < GPU::NUM_TEXTURE_UNITS; ++i)
                 quad.texture_coordinates[i] = expand4(point.tex_coords[i]);
                 quad.texture_coordinates[i] = expand4(point.tex_coords[i]);
             quad.fog_depth = expand4(abs(point.eye_coordinates.z()));
             quad.fog_depth = expand4(abs(point.eye_coordinates.z()));
         });
         });
@@ -625,7 +625,7 @@ void Device::rasterize_point_antialiased(GPU::Vertex& point)
         },
         },
         [&point](auto& quad) {
         [&point](auto& quad) {
             quad.vertex_color = expand4(point.color);
             quad.vertex_color = expand4(point.color);
-            for (size_t i = 0; i < GPU::NUM_SAMPLERS; ++i)
+            for (size_t i = 0; i < GPU::NUM_TEXTURE_UNITS; ++i)
                 quad.texture_coordinates[i] = expand4(point.tex_coords[i]);
                 quad.texture_coordinates[i] = expand4(point.tex_coords[i]);
             quad.fog_depth = expand4(abs(point.eye_coordinates.z()));
             quad.fog_depth = expand4(abs(point.eye_coordinates.z()));
         });
         });
@@ -634,7 +634,7 @@ void Device::rasterize_point_antialiased(GPU::Vertex& point)
 void Device::rasterize_point(GPU::Vertex& point)
 void Device::rasterize_point(GPU::Vertex& point)
 {
 {
     // Divide texture coordinates R, S and T by Q
     // Divide texture coordinates R, S and T by Q
-    for (size_t i = 0; i < GPU::NUM_SAMPLERS; ++i) {
+    for (size_t i = 0; i < GPU::NUM_TEXTURE_UNITS; ++i) {
         auto& tex_coord = point.tex_coords[i];
         auto& tex_coord = point.tex_coords[i];
         auto one_over_w = 1 / tex_coord.w();
         auto one_over_w = 1 / tex_coord.w();
         tex_coord = {
         tex_coord = {
@@ -790,7 +790,7 @@ void Device::rasterize_triangle(Triangle& triangle)
             else
             else
                 quad.vertex_color = expand4(vertex0.color);
                 quad.vertex_color = expand4(vertex0.color);
 
 
-            for (size_t i = 0; i < GPU::NUM_SAMPLERS; ++i)
+            for (GPU::TextureUnitIndex i = 0; i < GPU::NUM_TEXTURE_UNITS; ++i)
                 quad.texture_coordinates[i] = interpolate(expand4(vertex0.tex_coords[i]), expand4(vertex1.tex_coords[i]), expand4(vertex2.tex_coords[i]), quad.barycentrics);
                 quad.texture_coordinates[i] = interpolate(expand4(vertex0.tex_coords[i]), expand4(vertex1.tex_coords[i]), expand4(vertex2.tex_coords[i]), quad.barycentrics);
 
 
             if (m_options.fog_enabled)
             if (m_options.fog_enabled)
@@ -810,7 +810,7 @@ GPU::DeviceInfo Device::info() const
     return {
     return {
         .vendor_name = "SerenityOS",
         .vendor_name = "SerenityOS",
         .device_name = "SoftGPU",
         .device_name = "SoftGPU",
-        .num_texture_units = GPU::NUM_SAMPLERS,
+        .num_texture_units = GPU::NUM_TEXTURE_UNITS,
         .num_lights = NUM_LIGHTS,
         .num_lights = NUM_LIGHTS,
         .max_clip_planes = MAX_CLIP_PLANES,
         .max_clip_planes = MAX_CLIP_PLANES,
         .max_texture_lod_bias = MAX_TEXTURE_LOD_BIAS,
         .max_texture_lod_bias = MAX_TEXTURE_LOD_BIAS,
@@ -820,18 +820,17 @@ GPU::DeviceInfo Device::info() const
     };
     };
 }
 }
 
 
-static void generate_texture_coordinates(GPU::Vertex& vertex, GPU::RasterizerOptions const& options)
+static void generate_texture_coordinates(GPU::Vertex const& vertex, FloatVector4& tex_coord, GPU::TextureUnitConfiguration const& texture_unit_configuration)
 {
 {
-    auto generate_coordinate = [&](size_t texcoord_index, size_t config_index) -> float {
-        auto mode = options.texcoord_generation_config[texcoord_index][config_index].mode;
-
-        switch (mode) {
+    auto generate_coordinate = [&](size_t config_index) -> float {
+        auto const& tex_coord_generation = texture_unit_configuration.tex_coord_generation[config_index];
+        switch (tex_coord_generation.mode) {
         case GPU::TexCoordGenerationMode::ObjectLinear: {
         case GPU::TexCoordGenerationMode::ObjectLinear: {
-            auto coefficients = options.texcoord_generation_config[texcoord_index][config_index].coefficients;
+            auto coefficients = tex_coord_generation.coefficients;
             return coefficients.dot(vertex.position);
             return coefficients.dot(vertex.position);
         }
         }
         case GPU::TexCoordGenerationMode::EyeLinear: {
         case GPU::TexCoordGenerationMode::EyeLinear: {
-            auto coefficients = options.texcoord_generation_config[texcoord_index][config_index].coefficients;
+            auto coefficients = tex_coord_generation.coefficients;
             return coefficients.dot(vertex.eye_coordinates);
             return coefficients.dot(vertex.eye_coordinates);
         }
         }
         case GPU::TexCoordGenerationMode::SphereMap: {
         case GPU::TexCoordGenerationMode::SphereMap: {
@@ -853,21 +852,20 @@ static void generate_texture_coordinates(GPU::Vertex& vertex, GPU::RasterizerOpt
         case GPU::TexCoordGenerationMode::NormalMap: {
         case GPU::TexCoordGenerationMode::NormalMap: {
             return vertex.normal[config_index];
             return vertex.normal[config_index];
         }
         }
-        default:
-            VERIFY_NOT_REACHED();
         }
         }
+        VERIFY_NOT_REACHED();
     };
     };
 
 
-    for (size_t i = 0; i < vertex.tex_coords.size(); ++i) {
-        auto& tex_coord = vertex.tex_coords[i];
-        auto const enabled_coords = options.texcoord_generation_enabled_coordinates[i];
-        tex_coord = {
-            ((enabled_coords & GPU::TexCoordGenerationCoordinate::S) > 0) ? generate_coordinate(i, 0) : tex_coord.x(),
-            ((enabled_coords & GPU::TexCoordGenerationCoordinate::T) > 0) ? generate_coordinate(i, 1) : tex_coord.y(),
-            ((enabled_coords & GPU::TexCoordGenerationCoordinate::R) > 0) ? generate_coordinate(i, 2) : tex_coord.z(),
-            ((enabled_coords & GPU::TexCoordGenerationCoordinate::Q) > 0) ? generate_coordinate(i, 3) : tex_coord.w(),
-        };
-    }
+    auto const enabled_coords = texture_unit_configuration.tex_coord_generation_enabled;
+    if (enabled_coords == GPU::TexCoordGenerationCoordinate::None)
+        return;
+
+    tex_coord = {
+        ((enabled_coords & GPU::TexCoordGenerationCoordinate::S) > 0) ? generate_coordinate(0) : tex_coord.x(),
+        ((enabled_coords & GPU::TexCoordGenerationCoordinate::T) > 0) ? generate_coordinate(1) : tex_coord.y(),
+        ((enabled_coords & GPU::TexCoordGenerationCoordinate::R) > 0) ? generate_coordinate(2) : tex_coord.z(),
+        ((enabled_coords & GPU::TexCoordGenerationCoordinate::Q) > 0) ? generate_coordinate(3) : tex_coord.w(),
+    };
 }
 }
 
 
 void Device::calculate_vertex_lighting(GPU::Vertex& vertex) const
 void Device::calculate_vertex_lighting(GPU::Vertex& vertex) const
@@ -989,8 +987,7 @@ void Device::calculate_vertex_lighting(GPU::Vertex& vertex) const
     vertex.color.clamp(0.0f, 1.0f);
     vertex.color.clamp(0.0f, 1.0f);
 }
 }
 
 
-void Device::draw_primitives(GPU::PrimitiveType primitive_type, FloatMatrix4x4 const& model_view_transform, FloatMatrix4x4 const& projection_transform,
-    FloatMatrix4x4 const& texture_transform, Vector<GPU::Vertex>& vertices, Vector<size_t> const& enabled_texture_units)
+void Device::draw_primitives(GPU::PrimitiveType primitive_type, FloatMatrix4x4 const& model_view_transform, FloatMatrix4x4 const& projection_transform, Vector<GPU::Vertex>& vertices)
 {
 {
     // At this point, the user has effectively specified that they are done with defining the geometry
     // At this point, the user has effectively specified that they are done with defining the geometry
     // of what they want to draw. We now need to do a few things (https://www.khronos.org/opengl/wiki/Rendering_Pipeline_Overview):
     // of what they want to draw. We now need to do a few things (https://www.khronos.org/opengl/wiki/Rendering_Pipeline_Overview):
@@ -1005,17 +1002,10 @@ void Device::draw_primitives(GPU::PrimitiveType primitive_type, FloatMatrix4x4 c
     if (vertices.is_empty())
     if (vertices.is_empty())
         return;
         return;
 
 
-    m_enabled_texture_units = enabled_texture_units;
-
     // Set up normals transform by taking the upper left 3x3 elements from the model view matrix
     // Set up normals transform by taking the upper left 3x3 elements from the model view matrix
     // See section 2.11.3 of the OpenGL 1.5 spec
     // See section 2.11.3 of the OpenGL 1.5 spec
     auto const normal_transform = model_view_transform.submatrix_from_topleft<3>().transpose().inverse();
     auto const normal_transform = model_view_transform.submatrix_from_topleft<3>().transpose().inverse();
 
 
-    // Generate texture coordinates if at least one coordinate is enabled
-    bool texture_coordinate_generation_enabled = any_of(
-        m_options.texcoord_generation_enabled_coordinates,
-        [](auto coordinates_enabled) { return coordinates_enabled != GPU::TexCoordGenerationCoordinate::None; });
-
     // First, transform all vertices
     // First, transform all vertices
     for (auto& vertex : vertices) {
     for (auto& vertex : vertices) {
         vertex.eye_coordinates = model_view_transform * vertex.position;
         vertex.eye_coordinates = model_view_transform * vertex.position;
@@ -1028,11 +1018,13 @@ void Device::draw_primitives(GPU::PrimitiveType primitive_type, FloatMatrix4x4 c
 
 
         vertex.clip_coordinates = projection_transform * vertex.eye_coordinates;
         vertex.clip_coordinates = projection_transform * vertex.eye_coordinates;
 
 
-        if (texture_coordinate_generation_enabled)
-            generate_texture_coordinates(vertex, m_options);
-
-        for (size_t i = 0; i < GPU::NUM_SAMPLERS; ++i)
-            vertex.tex_coords[i] = texture_transform * vertex.tex_coords[i];
+        for (GPU::TextureUnitIndex i = 0; i < GPU::NUM_TEXTURE_UNITS; ++i) {
+            auto const& texture_unit_configuration = m_texture_unit_configuration[i];
+            if (!texture_unit_configuration.enabled)
+                continue;
+            generate_texture_coordinates(vertex, vertex.tex_coords[i], texture_unit_configuration);
+            vertex.tex_coords[i] = texture_unit_configuration.transformation_matrix * vertex.tex_coords[i];
+        }
     }
     }
 
 
     // Window coordinate calculation
     // Window coordinate calculation
@@ -1188,10 +1180,12 @@ void Device::draw_primitives(GPU::PrimitiveType primitive_type, FloatMatrix4x4 c
 
 
 ALWAYS_INLINE void Device::shade_fragments(PixelQuad& quad)
 ALWAYS_INLINE void Device::shade_fragments(PixelQuad& quad)
 {
 {
-    Array<Vector4<f32x4>, GPU::NUM_SAMPLERS> texture_stage_texel;
+    Array<Vector4<f32x4>, GPU::NUM_TEXTURE_UNITS> texture_stage_texel;
 
 
     auto current_color = quad.vertex_color;
     auto current_color = quad.vertex_color;
-    for (size_t i : m_enabled_texture_units) {
+    for (GPU::TextureUnitIndex i = 0; i < GPU::NUM_TEXTURE_UNITS; ++i) {
+        if (!m_texture_unit_configuration[i].enabled)
+            continue;
         auto const& sampler = m_samplers[i];
         auto const& sampler = m_samplers[i];
 
 
         auto texel = sampler.sample_2d(quad.texture_coordinates[i].xy());
         auto texel = sampler.sample_2d(quad.texture_coordinates[i].xy());
@@ -1661,6 +1655,11 @@ void Device::set_stencil_configuration(GPU::Face face, GPU::StencilConfiguration
     m_stencil_configuration[face] = stencil_configuration;
     m_stencil_configuration[face] = stencil_configuration;
 }
 }
 
 
+void Device::set_texture_unit_configuration(GPU::TextureUnitIndex index, GPU::TextureUnitConfiguration const& configuration)
+{
+    m_texture_unit_configuration[index] = configuration;
+}
+
 void Device::set_raster_position(GPU::RasterPosition const& raster_position)
 void Device::set_raster_position(GPU::RasterPosition const& raster_position)
 {
 {
     m_raster_position = raster_position;
     m_raster_position = raster_position;

+ 5 - 4
Userland/Libraries/LibSoftGPU/Device.h

@@ -23,7 +23,7 @@
 #include <LibGPU/RasterizerOptions.h>
 #include <LibGPU/RasterizerOptions.h>
 #include <LibGPU/SamplerConfig.h>
 #include <LibGPU/SamplerConfig.h>
 #include <LibGPU/StencilConfiguration.h>
 #include <LibGPU/StencilConfiguration.h>
-#include <LibGPU/TexCoordGenerationConfig.h>
+#include <LibGPU/TextureUnitConfiguration.h>
 #include <LibGPU/Vertex.h>
 #include <LibGPU/Vertex.h>
 #include <LibGfx/Bitmap.h>
 #include <LibGfx/Bitmap.h>
 #include <LibGfx/Matrix4x4.h>
 #include <LibGfx/Matrix4x4.h>
@@ -47,7 +47,7 @@ public:
 
 
     virtual GPU::DeviceInfo info() const override;
     virtual GPU::DeviceInfo info() const override;
 
 
-    virtual void draw_primitives(GPU::PrimitiveType, FloatMatrix4x4 const& model_view_transform, FloatMatrix4x4 const& projection_transform, FloatMatrix4x4 const& texture_transform, Vector<GPU::Vertex>& vertices, Vector<size_t> const& enabled_texture_units) override;
+    virtual void draw_primitives(GPU::PrimitiveType, FloatMatrix4x4 const& model_view_transform, FloatMatrix4x4 const& projection_transform, Vector<GPU::Vertex>& vertices) override;
     virtual void resize(Gfx::IntSize const& min_size) override;
     virtual void resize(Gfx::IntSize const& min_size) override;
     virtual void clear_color(FloatVector4 const&) override;
     virtual void clear_color(FloatVector4 const&) override;
     virtual void clear_depth(GPU::DepthType) override;
     virtual void clear_depth(GPU::DepthType) override;
@@ -70,6 +70,7 @@ public:
     virtual void set_light_state(unsigned, GPU::Light const&) override;
     virtual void set_light_state(unsigned, GPU::Light const&) override;
     virtual void set_material_state(GPU::Face, GPU::Material const&) override;
     virtual void set_material_state(GPU::Face, GPU::Material const&) override;
     virtual void set_stencil_configuration(GPU::Face, GPU::StencilConfiguration const&) override;
     virtual void set_stencil_configuration(GPU::Face, GPU::StencilConfiguration const&) override;
+    virtual void set_texture_unit_configuration(GPU::TextureUnitIndex, GPU::TextureUnitConfiguration const&) override;
     virtual void set_clip_planes(Vector<FloatVector4> const&) override;
     virtual void set_clip_planes(Vector<FloatVector4> const&) override;
 
 
     virtual GPU::RasterPosition raster_position() const override { return m_raster_position; }
     virtual GPU::RasterPosition raster_position() const override { return m_raster_position; }
@@ -107,14 +108,14 @@ private:
     Vector<Triangle> m_triangle_list;
     Vector<Triangle> m_triangle_list;
     Vector<Triangle> m_processed_triangles;
     Vector<Triangle> m_processed_triangles;
     Vector<GPU::Vertex> m_clipped_vertices;
     Vector<GPU::Vertex> m_clipped_vertices;
-    Array<Sampler, GPU::NUM_SAMPLERS> m_samplers;
-    Vector<size_t> m_enabled_texture_units;
+    Array<Sampler, GPU::NUM_TEXTURE_UNITS> m_samplers;
     AlphaBlendFactors m_alpha_blend_factors;
     AlphaBlendFactors m_alpha_blend_factors;
     Array<GPU::Light, NUM_LIGHTS> m_lights;
     Array<GPU::Light, NUM_LIGHTS> m_lights;
     Array<GPU::Material, 2u> m_materials;
     Array<GPU::Material, 2u> m_materials;
     GPU::RasterPosition m_raster_position;
     GPU::RasterPosition m_raster_position;
     Vector<FloatVector4> m_clip_planes;
     Vector<FloatVector4> m_clip_planes;
     Array<GPU::StencilConfiguration, 2u> m_stencil_configuration;
     Array<GPU::StencilConfiguration, 2u> m_stencil_configuration;
+    Array<GPU::TextureUnitConfiguration, GPU::NUM_TEXTURE_UNITS> m_texture_unit_configuration;
 };
 };
 
 
 }
 }

+ 1 - 1
Userland/Libraries/LibSoftGPU/PixelQuad.h

@@ -25,7 +25,7 @@ struct PixelQuad final {
     Vector3<f32x4> barycentrics;
     Vector3<f32x4> barycentrics;
     f32x4 depth;
     f32x4 depth;
     Vector4<f32x4> vertex_color;
     Vector4<f32x4> vertex_color;
-    Array<Vector4<f32x4>, GPU::NUM_SAMPLERS> texture_coordinates;
+    Array<Vector4<f32x4>, GPU::NUM_TEXTURE_UNITS> texture_coordinates;
     Vector4<f32x4> out_color;
     Vector4<f32x4> out_color;
     f32x4 fog_depth;
     f32x4 fog_depth;
     i32x4 mask;
     i32x4 mask;