mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-11 21:37:58 +03:00
LibGL+LibSoftGPU: Implement fixed pipeline support for GL_COMBINE
`GL_COMBINE` is basically a fixed function calculator to perform simple arithmetics on configurable fragment sources. This patch implements a number of texture env parameters with support for the RGBA internal format.
This commit is contained in:
parent
494024b70e
commit
1d36bfdac1
Notes:
sideshowbarker
2024-07-17 07:15:53 +09:00
Author: https://github.com/gmta Commit: https://github.com/SerenityOS/serenity/commit/1d36bfdac1 Pull-request: https://github.com/SerenityOS/serenity/pull/15150 Reviewed-by: https://github.com/linusg ✅ Reviewed-by: https://github.com/sunverwerth ✅
@ -72,6 +72,8 @@ Optional<ContextParameter> 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<GLdouble>(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:
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -19,8 +19,26 @@ public:
|
||||
RefPtr<Texture2D> texture_2d_target_texture() const { return m_texture_2d_target_texture; }
|
||||
void set_texture_2d_target_texture(RefPtr<Texture2D> 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<GLenum, 3> m_alpha_operand { GL_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA };
|
||||
float m_alpha_scale { 1.f };
|
||||
Array<GLenum, 3> 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<GLenum, 3> m_rgb_operand { GL_SRC_COLOR, GL_SRC_COLOR, GL_SRC_ALPHA };
|
||||
float m_rgb_scale { 1.f };
|
||||
Array<GLenum, 3> m_rgb_source { GL_TEXTURE, GL_PREVIOUS, GL_CONSTANT };
|
||||
|
||||
// Bound textures
|
||||
RefPtr<Texture2D> m_texture_2d_target_texture {};
|
||||
|
@ -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<GLenum>(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<GLenum>(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<GLenum>(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<GLenum>(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<GLenum>(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<GLenum>(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<GLenum>(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<GLenum>(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();
|
||||
|
@ -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;
|
||||
|
@ -1,11 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Stephan Unverwerth <s.unverwerth@serenityos.org>
|
||||
* Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Array.h>
|
||||
#include <LibGPU/Image.h>
|
||||
#include <LibGfx/Vector4.h>
|
||||
|
||||
@ -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<TextureOperand, 3> alpha_operand { TextureOperand::SourceAlpha, TextureOperand::SourceAlpha, TextureOperand::SourceAlpha };
|
||||
float alpha_scale { 1.f };
|
||||
Array<TextureSource, 3> 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<TextureOperand, 3> rgb_operand { TextureOperand::SourceColor, TextureOperand::SourceColor, TextureOperand::SourceAlpha };
|
||||
float rgb_scale { 1.f };
|
||||
Array<TextureSource, 3> rgb_source { TextureSource::Texture, TextureSource::Previous, TextureSource::Constant };
|
||||
u8 rgb_source_texture_stage { 0 };
|
||||
};
|
||||
|
||||
struct SamplerConfig final {
|
||||
RefPtr<Image> 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 {};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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<Vector4<f32x4>, 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<f32x4> { 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
|
||||
|
@ -134,10 +134,13 @@ Vector4<AK::SIMD::f32x4> Sampler::sample_2d(Vector2<AK::SIMD::f32x4> 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<float>(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<float>(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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user