diff --git a/Userland/Libraries/LibGL/ContextParameter.cpp b/Userland/Libraries/LibGL/ContextParameter.cpp index b08687d611..965b3207fc 100644 --- a/Userland/Libraries/LibGL/ContextParameter.cpp +++ b/Userland/Libraries/LibGL/ContextParameter.cpp @@ -72,6 +72,8 @@ Optional GLContext::get_context_parameter(GLenum name) 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_LOD_BIAS: + return ContextParameter { .type = GL_DOUBLE, .value = { .double_value = static_cast(m_device_info.max_texture_lod_bias) } }; case GL_MAX_TEXTURE_SIZE: return ContextParameter { .type = GL_INT, .value = { .integer_value = 4096 } }; case GL_MAX_TEXTURE_STACK_DEPTH: diff --git a/Userland/Libraries/LibGL/GL/gl.h b/Userland/Libraries/LibGL/GL/gl.h index ae86fff125..d59596b16f 100644 --- a/Userland/Libraries/LibGL/GL/gl.h +++ b/Userland/Libraries/LibGL/GL/gl.h @@ -144,6 +144,7 @@ extern "C" { #define GL_MAP2_VERTEX_4 0x0DB8 #define GL_NORMAL_ARRAY 0x8075 #define GL_NORMAL_ARRAY_TYPE 0x807E +#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD // Blend factors #define GL_ZERO 0 @@ -485,6 +486,8 @@ extern "C" { #define GL_Q 0x2003 // Texture Environment and Parameters +#define GL_ADD 0x0104 +#define GL_ALPHA_SCALE 0x0D1C #define GL_MODULATE 0x2100 #define GL_TEXTURE_ENV_MODE 0x2200 #define GL_DECAL 0x2101 @@ -501,9 +504,41 @@ extern "C" { #define GL_TEXTURE_WRAP_T 0x2803 #define GL_CLAMP 0x2900 #define GL_REPEAT 0x2901 -#define GL_MIRRORED_REPEAT 0x8370 #define GL_CLAMP_TO_BORDER 0x812D #define GL_CLAMP_TO_EDGE 0x812F +#define GL_MIRRORED_REPEAT 0x8370 +#define GL_SUBTRACT 0x84E7 +#define GL_TEXTURE_FILTER_CONTROL 0x8500 +#define GL_TEXTURE_LOD_BIAS 0x8501 +#define GL_COMBINE 0x8570 +#define GL_COMBINE_RGB 0x8571 +#define GL_COMBINE_ALPHA 0x8572 +#define GL_RGB_SCALE 0x8573 +#define GL_ADD_SIGNED 0x8574 +#define GL_INTERPOLATE 0x8575 +#define GL_CONSTANT 0x8576 +#define GL_PRIMARY_COLOR 0x8577 +#define GL_PREVIOUS 0x8578 +#define GL_SRC0_RGB 0x8580 +#define GL_SOURCE0_RGB 0x8580 +#define GL_SRC1_RGB 0x8581 +#define GL_SOURCE1_RGB 0x8581 +#define GL_SRC2_RGB 0x8582 +#define GL_SOURCE2_RGB 0x8582 +#define GL_SRC0_ALPHA 0x8588 +#define GL_SOURCE0_ALPHA 0x8588 +#define GL_SRC1_ALPHA 0x8589 +#define GL_SOURCE1_ALPHA 0x8589 +#define GL_SRC2_ALPHA 0x858A +#define GL_SOURCE2_ALPHA 0x858A +#define GL_OPERAND0_RGB 0x8590 +#define GL_OPERAND1_RGB 0x8591 +#define GL_OPERAND2_RGB 0x8592 +#define GL_OPERAND0_ALPHA 0x8598 +#define GL_OPERAND1_ALPHA 0x8599 +#define GL_OPERAND2_ALPHA 0x859A +#define GL_DOT3_RGB 0x86AE +#define GL_DOT3_RGBA 0x86AF // Texture gen modes #define GL_EYE_LINEAR 0x2400 @@ -538,8 +573,6 @@ extern "C" { #define GL_MODELVIEW_MATRIX 0x0BA6 #define GL_PROJECTION_MATRIX 0x0BA7 -#define GL_ADD 0x0104 - // User clipping planes #define GL_MAX_CLIP_PLANES 0x0D32 #define GL_CLIP_PLANE0 0x3000 diff --git a/Userland/Libraries/LibGL/GLContext.cpp b/Userland/Libraries/LibGL/GLContext.cpp index 91dc4c6eb6..2c1c050556 100644 --- a/Userland/Libraries/LibGL/GLContext.cpp +++ b/Userland/Libraries/LibGL/GLContext.cpp @@ -935,6 +935,9 @@ void GLContext::build_extension_string() extensions.append("GL_EXT_texture_env_add"sv); } + if (m_device_info.max_texture_lod_bias > 0.f) + extensions.append("GL_EXT_texture_lod_bias"sv); + m_extensions = String::join(' ', extensions); } diff --git a/Userland/Libraries/LibGL/Tex/TextureUnit.h b/Userland/Libraries/LibGL/Tex/TextureUnit.h index c485ca9c91..0dc2ac8a18 100644 --- a/Userland/Libraries/LibGL/Tex/TextureUnit.h +++ b/Userland/Libraries/LibGL/Tex/TextureUnit.h @@ -19,8 +19,26 @@ public: RefPtr texture_2d_target_texture() const { return m_texture_2d_target_texture; } void set_texture_2d_target_texture(RefPtr const& texture) { m_texture_2d_target_texture = texture; } + void set_alpha_combinator(GLenum combinator) { m_alpha_combinator = combinator; } + GLenum alpha_combinator() const { return m_alpha_combinator; } + void set_alpha_operand(size_t index, GLenum operand) { m_alpha_operand[index] = operand; } + GLenum alpha_operand(size_t index) const { return m_alpha_operand[index]; } + void set_alpha_scale(float scale) { m_alpha_scale = scale; } + float alpha_scale() const { return m_alpha_scale; } + void set_alpha_source(size_t index, GLenum source) { m_alpha_source[index] = source; } + GLenum alpha_source(size_t index) const { return m_alpha_source[index]; } void set_env_mode(GLenum mode) { m_env_mode = mode; } GLenum env_mode() const { return m_env_mode; } + void set_level_of_detail_bias(float bias) { m_level_of_detail_bias = bias; } + float level_of_detail_bias() const { return m_level_of_detail_bias; } + void set_rgb_combinator(GLenum combinator) { m_rgb_combinator = combinator; } + GLenum rgb_combinator() const { return m_rgb_combinator; } + void set_rgb_operand(size_t index, GLenum operand) { m_rgb_operand[index] = operand; } + GLenum rgb_operand(size_t index) const { return m_rgb_operand[index]; } + void set_rgb_scale(float scale) { m_rgb_scale = scale; } + float rgb_scale() const { return m_rgb_scale; } + void set_rgb_source(size_t index, GLenum source) { m_rgb_source[index] = source; } + GLenum rgb_source(size_t index) const { return m_rgb_source[index]; } bool texture_1d_enabled() const { return m_texture_1d_enabled; }; void set_texture_1d_enabled(bool texture_1d_enabled) { m_texture_1d_enabled = texture_1d_enabled; } @@ -32,7 +50,16 @@ public: void set_texture_cube_map_enabled(bool texture_cube_map_enabled) { m_texture_cube_map_enabled = texture_cube_map_enabled; } private: + GLenum m_alpha_combinator { GL_MODULATE }; + Array m_alpha_operand { GL_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA }; + float m_alpha_scale { 1.f }; + Array m_alpha_source { GL_TEXTURE, GL_PREVIOUS, GL_CONSTANT }; GLenum m_env_mode { GL_MODULATE }; + float m_level_of_detail_bias { 0.f }; + GLenum m_rgb_combinator { GL_MODULATE }; + Array m_rgb_operand { GL_SRC_COLOR, GL_SRC_COLOR, GL_SRC_ALPHA }; + float m_rgb_scale { 1.f }; + Array m_rgb_source { GL_TEXTURE, GL_PREVIOUS, GL_CONSTANT }; // Bound textures RefPtr m_texture_2d_target_texture {}; diff --git a/Userland/Libraries/LibGL/Texture.cpp b/Userland/Libraries/LibGL/Texture.cpp index 75fd4bbbcc..921bbfa7d7 100644 --- a/Userland/Libraries/LibGL/Texture.cpp +++ b/Userland/Libraries/LibGL/Texture.cpp @@ -243,27 +243,152 @@ void GLContext::gl_tex_env(GLenum target, GLenum pname, GLfloat param) APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_tex_env, target, pname, param); RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION); - // FIXME: We currently only support a subset of possible target values. Implement the rest! - RETURN_WITH_ERROR_IF(target != GL_TEXTURE_ENV, GL_INVALID_ENUM); + RETURN_WITH_ERROR_IF(target != GL_TEXTURE_ENV && target != GL_TEXTURE_FILTER_CONTROL, GL_INVALID_ENUM); + RETURN_WITH_ERROR_IF(target == GL_TEXTURE_FILTER_CONTROL && pname != GL_TEXTURE_LOD_BIAS, GL_INVALID_ENUM); - // FIXME: We currently only support a subset of possible pname values. Implement the rest! - RETURN_WITH_ERROR_IF(pname != GL_TEXTURE_ENV_MODE, GL_INVALID_ENUM); - - auto param_enum = static_cast(param); - - switch (param_enum) { - case GL_MODULATE: - case GL_REPLACE: - case GL_DECAL: - case GL_ADD: - m_active_texture_unit->set_env_mode(param_enum); - m_sampler_config_is_dirty = true; + switch (target) { + case GL_TEXTURE_ENV: + switch (pname) { + case GL_ALPHA_SCALE: + RETURN_WITH_ERROR_IF(param != 1.f && param != 2.f && param != 4.f, GL_INVALID_VALUE); + m_active_texture_unit->set_alpha_scale(param); + break; + case GL_COMBINE_ALPHA: { + auto param_enum = static_cast(param); + switch (param_enum) { + case GL_ADD: + case GL_ADD_SIGNED: + case GL_INTERPOLATE: + case GL_MODULATE: + case GL_REPLACE: + case GL_SUBTRACT: + m_active_texture_unit->set_alpha_combinator(param_enum); + break; + default: + RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM); + } + break; + } + case GL_COMBINE_RGB: { + auto param_enum = static_cast(param); + switch (param_enum) { + case GL_ADD: + case GL_ADD_SIGNED: + case GL_DOT3_RGB: + case GL_DOT3_RGBA: + case GL_INTERPOLATE: + case GL_MODULATE: + case GL_REPLACE: + case GL_SUBTRACT: + m_active_texture_unit->set_rgb_combinator(param_enum); + break; + default: + RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM); + } + break; + } + case GL_OPERAND0_ALPHA: + case GL_OPERAND1_ALPHA: + case GL_OPERAND2_ALPHA: { + auto param_enum = static_cast(param); + switch (param_enum) { + case GL_ONE_MINUS_SRC_ALPHA: + case GL_SRC_ALPHA: + m_active_texture_unit->set_alpha_operand(pname - GL_OPERAND0_ALPHA, param_enum); + break; + default: + RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM); + } + break; + } + case GL_OPERAND0_RGB: + case GL_OPERAND1_RGB: + case GL_OPERAND2_RGB: { + auto param_enum = static_cast(param); + switch (param_enum) { + case GL_ONE_MINUS_SRC_ALPHA: + case GL_ONE_MINUS_SRC_COLOR: + case GL_SRC_ALPHA: + case GL_SRC_COLOR: + m_active_texture_unit->set_rgb_operand(pname - GL_OPERAND0_RGB, param_enum); + break; + default: + RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM); + } + break; + } + case GL_RGB_SCALE: + RETURN_WITH_ERROR_IF(param != 1.f && param != 2.f && param != 4.f, GL_INVALID_VALUE); + m_active_texture_unit->set_rgb_scale(param); + break; + case GL_SRC0_ALPHA: + case GL_SRC1_ALPHA: + case GL_SRC2_ALPHA: { + auto param_enum = static_cast(param); + switch (param_enum) { + case GL_CONSTANT: + case GL_PREVIOUS: + case GL_PRIMARY_COLOR: + case GL_TEXTURE: + case GL_TEXTURE0 ... GL_TEXTURE31: + m_active_texture_unit->set_alpha_source(pname - GL_SRC0_ALPHA, param_enum); + break; + default: + RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM); + } + break; + } + case GL_SRC0_RGB: + case GL_SRC1_RGB: + case GL_SRC2_RGB: { + auto param_enum = static_cast(param); + switch (param_enum) { + case GL_CONSTANT: + case GL_PREVIOUS: + case GL_PRIMARY_COLOR: + case GL_TEXTURE: + case GL_TEXTURE0 ... GL_TEXTURE31: + m_active_texture_unit->set_rgb_source(pname - GL_SRC0_RGB, param_enum); + break; + default: + RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM); + } + break; + } + case GL_TEXTURE_ENV_MODE: { + auto param_enum = static_cast(param); + switch (param_enum) { + case GL_ADD: + case GL_BLEND: + case GL_COMBINE: + case GL_DECAL: + case GL_MODULATE: + case GL_REPLACE: + m_active_texture_unit->set_env_mode(param_enum); + break; + default: + RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM); + } + break; + } + default: + RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM); + } + break; + case GL_TEXTURE_FILTER_CONTROL: + switch (pname) { + case GL_TEXTURE_LOD_BIAS: + m_active_texture_unit->set_level_of_detail_bias(param); + break; + default: + VERIFY_NOT_REACHED(); + } break; default: - // FIXME: We currently only support a subset of possible param values. Implement the rest! - dbgln_if(GL_DEBUG, "gl_tex_env({:#x}, {:#x}, {}): param unimplemented", target, pname, param); - RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM); + VERIFY_NOT_REACHED(); } + + m_sampler_config_is_dirty = true; } void GLContext::gl_tex_gen(GLenum coord, GLenum pname, GLint param) @@ -537,6 +662,7 @@ void GLContext::sync_device_sampler_config() auto texture_2d = texture_unit.texture_2d_target_texture(); VERIFY(!texture_2d.is_null()); config.bound_image = texture_2d->device_image(); + config.level_of_detail_bias = texture_unit.level_of_detail_bias(); auto const& sampler = texture_2d->sampler(); @@ -620,21 +746,96 @@ void GLContext::sync_device_sampler_config() VERIFY_NOT_REACHED(); } - switch (texture_unit.env_mode()) { - case GL_MODULATE: - config.fixed_function_texture_env_mode = GPU::TextureEnvMode::Modulate; - break; - case GL_REPLACE: - config.fixed_function_texture_env_mode = GPU::TextureEnvMode::Replace; - break; - case GL_DECAL: - config.fixed_function_texture_env_mode = GPU::TextureEnvMode::Decal; - break; - case GL_ADD: - config.fixed_function_texture_env_mode = GPU::TextureEnvMode::Add; - break; - default: - VERIFY_NOT_REACHED(); + auto& fixed_function_env = config.fixed_function_texture_environment; + + auto get_env_mode = [](GLenum mode) { + switch (mode) { + case GL_ADD: + return GPU::TextureEnvMode::Add; + case GL_BLEND: + return GPU::TextureEnvMode::Blend; + case GL_COMBINE: + return GPU::TextureEnvMode::Combine; + case GL_DECAL: + return GPU::TextureEnvMode::Decal; + case GL_MODULATE: + return GPU::TextureEnvMode::Modulate; + case GL_REPLACE: + return GPU::TextureEnvMode::Replace; + default: + VERIFY_NOT_REACHED(); + } + }; + fixed_function_env.env_mode = get_env_mode(texture_unit.env_mode()); + + fixed_function_env.alpha_scale = texture_unit.alpha_scale(); + fixed_function_env.rgb_scale = texture_unit.rgb_scale(); + + auto get_combinator = [](GLenum combinator) { + switch (combinator) { + case GL_ADD: + return GPU::TextureCombinator::Add; + case GL_ADD_SIGNED: + return GPU::TextureCombinator::AddSigned; + case GL_DOT3_RGB: + return GPU::TextureCombinator::Dot3RGB; + case GL_DOT3_RGBA: + return GPU::TextureCombinator::Dot3RGBA; + case GL_INTERPOLATE: + return GPU::TextureCombinator::Interpolate; + case GL_MODULATE: + return GPU::TextureCombinator::Modulate; + case GL_REPLACE: + return GPU::TextureCombinator::Replace; + case GL_SUBTRACT: + return GPU::TextureCombinator::Subtract; + default: + VERIFY_NOT_REACHED(); + } + }; + fixed_function_env.alpha_combinator = get_combinator(texture_unit.alpha_combinator()); + fixed_function_env.rgb_combinator = get_combinator(texture_unit.rgb_combinator()); + + auto get_operand = [](GLenum operand) { + switch (operand) { + case GL_ONE_MINUS_SRC_ALPHA: + return GPU::TextureOperand::OneMinusSourceAlpha; + case GL_ONE_MINUS_SRC_COLOR: + return GPU::TextureOperand::OneMinusSourceColor; + case GL_SRC_ALPHA: + return GPU::TextureOperand::SourceAlpha; + case GL_SRC_COLOR: + return GPU::TextureOperand::SourceColor; + default: + VERIFY_NOT_REACHED(); + } + }; + auto get_source = [](GLenum source) { + switch (source) { + case GL_CONSTANT: + return GPU::TextureSource::Constant; + case GL_PREVIOUS: + return GPU::TextureSource::Previous; + case GL_PRIMARY_COLOR: + return GPU::TextureSource::PrimaryColor; + case GL_TEXTURE: + return GPU::TextureSource::Texture; + case GL_TEXTURE0 ... GL_TEXTURE31: + return GPU::TextureSource::TextureStage; + default: + VERIFY_NOT_REACHED(); + } + }; + for (size_t j = 0; j < 3; ++j) { + fixed_function_env.alpha_operand[j] = get_operand(texture_unit.alpha_operand(j)); + fixed_function_env.alpha_source[j] = get_source(texture_unit.alpha_source(j)); + if (fixed_function_env.alpha_source[j] == GPU::TextureSource::TextureStage) + fixed_function_env.alpha_source_texture_stage = texture_unit.alpha_source(j) - GL_TEXTURE0; + + fixed_function_env.rgb_operand[j] = get_operand(texture_unit.rgb_operand(j)); + fixed_function_env.rgb_source[j] = get_source(texture_unit.rgb_source(j)); + if (fixed_function_env.rgb_source[j] == GPU::TextureSource::TextureStage) + fixed_function_env.rgb_source_texture_stage = texture_unit.rgb_source(j) - GL_TEXTURE0; } config.border_color = sampler.border_color(); diff --git a/Userland/Libraries/LibGPU/DeviceInfo.h b/Userland/Libraries/LibGPU/DeviceInfo.h index 3693bc820d..6e39a0001e 100644 --- a/Userland/Libraries/LibGPU/DeviceInfo.h +++ b/Userland/Libraries/LibGPU/DeviceInfo.h @@ -16,6 +16,7 @@ struct DeviceInfo final { unsigned num_texture_units; unsigned num_lights; unsigned max_clip_planes; + float max_texture_lod_bias; u8 stencil_bits; bool supports_npot_textures; bool supports_texture_env_add; diff --git a/Userland/Libraries/LibGPU/SamplerConfig.h b/Userland/Libraries/LibGPU/SamplerConfig.h index a4b5b140d6..898bdfb3b0 100644 --- a/Userland/Libraries/LibGPU/SamplerConfig.h +++ b/Userland/Libraries/LibGPU/SamplerConfig.h @@ -1,11 +1,13 @@ /* * Copyright (c) 2022, Stephan Unverwerth + * Copyright (c) 2022, Jelle Raaijmakers * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once +#include #include #include @@ -31,22 +33,67 @@ enum class TextureWrapMode { }; enum class TextureEnvMode { + Add, + Blend, + Combine, + Decal, Modulate, Replace, - Decal, +}; + +enum class TextureCombinator { Add, + AddSigned, + Dot3RGB, + Dot3RGBA, + Interpolate, + Modulate, + Replace, + Subtract, +}; + +enum class TextureOperand { + OneMinusSourceAlpha, + OneMinusSourceColor, + SourceAlpha, + SourceColor, +}; + +enum class TextureSource { + Constant, + Previous, + PrimaryColor, + Texture, + TextureStage, +}; + +struct FixedFunctionTextureEnvironment final { + TextureCombinator alpha_combinator { TextureCombinator::Modulate }; + Array alpha_operand { TextureOperand::SourceAlpha, TextureOperand::SourceAlpha, TextureOperand::SourceAlpha }; + float alpha_scale { 1.f }; + Array alpha_source { TextureSource::Texture, TextureSource::Previous, TextureSource::Constant }; + u8 alpha_source_texture_stage { 0 }; + // FIXME: color is never actually updated + FloatVector4 color { 0.f, 0.f, 0.f, 0.f }; + TextureEnvMode env_mode { TextureEnvMode::Modulate }; + TextureCombinator rgb_combinator { TextureCombinator::Modulate }; + Array rgb_operand { TextureOperand::SourceColor, TextureOperand::SourceColor, TextureOperand::SourceAlpha }; + float rgb_scale { 1.f }; + Array rgb_source { TextureSource::Texture, TextureSource::Previous, TextureSource::Constant }; + u8 rgb_source_texture_stage { 0 }; }; struct SamplerConfig final { RefPtr bound_image; + float level_of_detail_bias { 0.f }; MipMapFilter mipmap_filter { MipMapFilter::Nearest }; TextureFilter texture_mag_filter { TextureFilter::Linear }; TextureFilter texture_min_filter { TextureFilter::Linear }; TextureWrapMode texture_wrap_u { TextureWrapMode::Repeat }; TextureWrapMode texture_wrap_v { TextureWrapMode::Repeat }; TextureWrapMode texture_wrap_w { TextureWrapMode::Repeat }; - FloatVector4 border_color { 0, 0, 0, 1 }; - TextureEnvMode fixed_function_texture_env_mode { TextureEnvMode::Modulate }; + FloatVector4 border_color { 0.f, 0.f, 0.f, 1.f }; + FixedFunctionTextureEnvironment fixed_function_texture_environment {}; }; } diff --git a/Userland/Libraries/LibSoftGPU/Config.h b/Userland/Libraries/LibSoftGPU/Config.h index 545207017e..52ed67ead8 100644 --- a/Userland/Libraries/LibSoftGPU/Config.h +++ b/Userland/Libraries/LibSoftGPU/Config.h @@ -20,6 +20,7 @@ static constexpr bool ENABLE_STATISTICS_OVERLAY = false; static constexpr int MILLISECONDS_PER_STATISTICS_PERIOD = 500; static constexpr int NUM_LIGHTS = 8; static constexpr int MAX_CLIP_PLANES = 6; +static constexpr float MAX_TEXTURE_LOD_BIAS = 2.f; static constexpr int SUBPIXEL_BITS = 6; // See: https://www.khronos.org/opengl/wiki/Common_Mistakes#Texture_edge_color_problem diff --git a/Userland/Libraries/LibSoftGPU/Device.cpp b/Userland/Libraries/LibSoftGPU/Device.cpp index 7ab7f3bb34..e864bf34c2 100644 --- a/Userland/Libraries/LibSoftGPU/Device.cpp +++ b/Userland/Libraries/LibSoftGPU/Device.cpp @@ -813,6 +813,7 @@ GPU::DeviceInfo Device::info() const .num_texture_units = GPU::NUM_SAMPLERS, .num_lights = NUM_LIGHTS, .max_clip_planes = MAX_CLIP_PLANES, + .max_texture_lod_bias = MAX_TEXTURE_LOD_BIAS, .stencil_bits = sizeof(GPU::StencilType) * 8, .supports_npot_textures = true, .supports_texture_env_add = true, @@ -1187,40 +1188,124 @@ void Device::draw_primitives(GPU::PrimitiveType primitive_type, FloatMatrix4x4 c ALWAYS_INLINE void Device::shade_fragments(PixelQuad& quad) { - quad.out_color = quad.vertex_color; + Array, GPU::NUM_SAMPLERS> texture_stage_texel; + auto current_color = quad.vertex_color; for (size_t i : m_enabled_texture_units) { - // FIXME: implement GL_TEXTURE_1D, GL_TEXTURE_3D and GL_TEXTURE_CUBE_MAP auto const& sampler = m_samplers[i]; auto texel = sampler.sample_2d(quad.texture_coordinates[i].xy()); + texture_stage_texel[i] = texel; INCREASE_STATISTICS_COUNTER(g_num_sampler_calls, 1); - // FIXME: Implement more blend modes - switch (sampler.config().fixed_function_texture_env_mode) { - case GPU::TextureEnvMode::Modulate: - quad.out_color = quad.out_color * texel; + // FIXME: implement support for GL_ALPHA, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_INTENSITY and GL_RGB internal formats + auto& fixed_function_env = sampler.config().fixed_function_texture_environment; + switch (fixed_function_env.env_mode) { + case GPU::TextureEnvMode::Add: + current_color.set_x(current_color.x() + texel.x()); + current_color.set_y(current_color.y() + texel.y()); + current_color.set_z(current_color.z() + texel.z()); + current_color.set_w(current_color.w() * texel.w()); break; - case GPU::TextureEnvMode::Replace: - quad.out_color = texel; + case GPU::TextureEnvMode::Blend: { + auto blend_color = expand4(fixed_function_env.color); + current_color.set_x(mix(current_color.x(), blend_color.x(), texel.x())); + current_color.set_y(mix(current_color.y(), blend_color.y(), texel.y())); + current_color.set_z(mix(current_color.z(), blend_color.z(), texel.z())); + current_color.set_w(current_color.w() * texel.w()); break; + } + case GPU::TextureEnvMode::Combine: { + auto get_source_color = [&](GPU::TextureSource source, u8 texture_stage) { + switch (source) { + case GPU::TextureSource::Constant: + return expand4(fixed_function_env.color); + case GPU::TextureSource::Previous: + return current_color; + case GPU::TextureSource::PrimaryColor: + return quad.vertex_color; + case GPU::TextureSource::Texture: + return texel; + case GPU::TextureSource::TextureStage: + return texture_stage_texel[texture_stage]; + } + VERIFY_NOT_REACHED(); + }; + auto get_argument_value = [](GPU::TextureOperand operand, auto value) { + switch (operand) { + case GPU::TextureOperand::OneMinusSourceAlpha: + case GPU::TextureOperand::OneMinusSourceColor: + return expand4(FloatVector4 { 1.f, 1.f, 1.f, 1.f }) - value; + case GPU::TextureOperand::SourceAlpha: + case GPU::TextureOperand::SourceColor: + return value; + } + VERIFY_NOT_REACHED(); + }; + auto calculate_combinator = [](GPU::TextureCombinator combinator, auto arg0, auto arg1, auto arg2) { + switch (combinator) { + case GPU::TextureCombinator::Add: + return arg0 + arg1; + case GPU::TextureCombinator::AddSigned: + return arg0 + arg1 - expand4(FloatVector4 { .5f, .5f, .5f, .5f }); + case GPU::TextureCombinator::Dot3RGB: + case GPU::TextureCombinator::Dot3RGBA: { + auto scalar = 4.f * ((arg0.x() - .5f) * (arg1.x() - .5f) + (arg0.y() - 0.5f) * (arg1.y() - 0.5f) + (arg0.z() - 0.5f) * (arg1.z() - 0.5f)); + return Vector4 { scalar, scalar, scalar, scalar }; + } + case GPU::TextureCombinator::Interpolate: + return mix(arg0, arg1, arg2); + case GPU::TextureCombinator::Modulate: + return arg0 * arg1; + case GPU::TextureCombinator::Replace: + return arg0; + case GPU::TextureCombinator::Subtract: + return arg0 - arg1; + } + VERIFY_NOT_REACHED(); + }; + auto calculate_color = [&](GPU::TextureCombinator combinator, auto& operands, auto& sources, u8 texture_stage) { + auto arg0 = get_argument_value(operands[0], get_source_color(sources[0], texture_stage)); + auto arg1 = get_argument_value(operands[1], get_source_color(sources[1], texture_stage)); + auto arg2 = get_argument_value(operands[2], get_source_color(sources[2], texture_stage)); + return calculate_combinator(combinator, arg0, arg1, arg2); + }; + + auto rgb_color = calculate_color( + fixed_function_env.rgb_combinator, + fixed_function_env.rgb_operand, + fixed_function_env.rgb_source, + fixed_function_env.rgb_source_texture_stage); + auto alpha_color = calculate_color( + fixed_function_env.alpha_combinator, + fixed_function_env.alpha_operand, + fixed_function_env.alpha_source, + fixed_function_env.alpha_source_texture_stage); + + current_color.set_x(rgb_color.x() * fixed_function_env.rgb_scale); + current_color.set_y(rgb_color.y() * fixed_function_env.rgb_scale); + current_color.set_z(rgb_color.z() * fixed_function_env.rgb_scale); + current_color.set_w(alpha_color.w() * fixed_function_env.alpha_scale); + + current_color.clamp(expand4(0.f), expand4(1.f)); + break; + } case GPU::TextureEnvMode::Decal: { auto dst_alpha = texel.w(); - quad.out_color.set_x(mix(quad.out_color.x(), texel.x(), dst_alpha)); - quad.out_color.set_y(mix(quad.out_color.y(), texel.y(), dst_alpha)); - quad.out_color.set_z(mix(quad.out_color.z(), texel.z(), dst_alpha)); + current_color.set_x(mix(current_color.x(), texel.x(), dst_alpha)); + current_color.set_y(mix(current_color.y(), texel.y(), dst_alpha)); + current_color.set_z(mix(current_color.z(), texel.z(), dst_alpha)); break; } - case GPU::TextureEnvMode::Add: - quad.out_color.set_x(quad.out_color.x() + texel.x()); - quad.out_color.set_y(quad.out_color.y() + texel.y()); - quad.out_color.set_z(quad.out_color.z() + texel.z()); - quad.out_color.set_w(quad.out_color.w() * texel.w()); // FIXME: If texture format is `GL_INTENSITY` alpha components must be added (https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTexEnv.xml) + case GPU::TextureEnvMode::Modulate: + current_color = current_color * texel; + break; + case GPU::TextureEnvMode::Replace: + current_color = texel; break; - default: - VERIFY_NOT_REACHED(); } } + quad.out_color = current_color; // Calculate fog // Math from here: https://opengl-notes.readthedocs.io/en/latest/topics/texturing/aliasing.html diff --git a/Userland/Libraries/LibSoftGPU/Sampler.cpp b/Userland/Libraries/LibSoftGPU/Sampler.cpp index f5361adac2..42c6287d0a 100644 --- a/Userland/Libraries/LibSoftGPU/Sampler.cpp +++ b/Userland/Libraries/LibSoftGPU/Sampler.cpp @@ -134,10 +134,13 @@ Vector4 Sampler::sample_2d(Vector2 const& uv) if (m_config.mipmap_filter == GPU::MipMapFilter::None) return sample_2d_lod(uv, expand4(base_level), m_config.texture_min_filter); + // FIXME: add texture-level support for GL_TEXTURE_LOD_BIAS; below is only texture unit-level + auto texture_lod_bias = AK::clamp(m_config.level_of_detail_bias, -MAX_TEXTURE_LOD_BIAS, MAX_TEXTURE_LOD_BIAS); // FIXME: Instead of clamping to num_levels - 1, actually make the max mipmap level configurable with glTexParameteri(GL_TEXTURE_MAX_LEVEL, max_level) auto min_level = expand4(static_cast(base_level)); - auto max_level = expand4(image.num_levels() - 1.0f); - auto level = min(max(log2_approximate(scale_factor) * 0.5f, min_level), max_level); + auto max_level = expand4(static_cast(image.num_levels()) - 1.f); + auto lambda_xy = log2_approximate(scale_factor) * .5f + texture_lod_bias; + auto level = clamp(lambda_xy, min_level, max_level); auto lower_level_texel = sample_2d_lod(uv, to_u32x4(level), m_config.texture_min_filter);